Compare commits
	
		
			No commits in common. "be04d0c7fe671e3492f9cda0ac36f348f6d66fba" and "7abce21aee2d407523be958e4e999c94f505d8f3" have entirely different histories. 
		
	
	
		
			be04d0c7fe
			...
			7abce21aee
		
	
		| 
						 | 
				
			
			@ -1,6 +1,3 @@
 | 
			
		|||
data_dir = "./data"
 | 
			
		||||
domain = "127.0.0.1"
 | 
			
		||||
port = 8080
 | 
			
		||||
 | 
			
		||||
[net]
 | 
			
		||||
type = "tcp"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ mod serve;
 | 
			
		|||
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
 | 
			
		||||
use clap::{Args, Parser, Subcommand, ValueEnum};
 | 
			
		||||
use clap::{Args, Parser, Subcommand};
 | 
			
		||||
use figment::{
 | 
			
		||||
    providers::{Env, Format, Serialized, Toml},
 | 
			
		||||
    Figment,
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ pub struct Cli {
 | 
			
		|||
    pub cmd: Command,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Args, Clone, Debug)]
 | 
			
		||||
#[derive(Serialize, Args, Clone)]
 | 
			
		||||
pub struct ClapConfig {
 | 
			
		||||
    #[arg(
 | 
			
		||||
        short,
 | 
			
		||||
| 
						 | 
				
			
			@ -35,43 +35,21 @@ pub struct ClapConfig {
 | 
			
		|||
    )]
 | 
			
		||||
    config_file: Option<PathBuf>,
 | 
			
		||||
 | 
			
		||||
    #[command(flatten)]
 | 
			
		||||
    net: NetConfig,
 | 
			
		||||
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    #[arg(long = "data", value_name = "DATA_DIR", global = true)]
 | 
			
		||||
    data_dir: Option<PathBuf>,
 | 
			
		||||
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    #[arg(short, long = "log", value_name = "LOG_LEVEL", global = true)]
 | 
			
		||||
    log_level: Option<LogLevel>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(ValueEnum, Serialize, Clone, Debug)]
 | 
			
		||||
#[serde(rename_all = "lowercase")]
 | 
			
		||||
pub enum NetConfigType {
 | 
			
		||||
    Tcp,
 | 
			
		||||
    Unix,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Clone, Args, Debug)]
 | 
			
		||||
pub struct NetConfig {
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    #[arg(long = "net", value_name = "NET", global = true)]
 | 
			
		||||
    r#type: Option<NetConfigType>,
 | 
			
		||||
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    #[arg(long, value_name = "DOMAIN", global = true)]
 | 
			
		||||
    #[arg(short, long, value_name = "DOMAIN", global = true)]
 | 
			
		||||
    domain: Option<String>,
 | 
			
		||||
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    #[arg(long, value_name = "PORT", global = true)]
 | 
			
		||||
    #[arg(short, long, value_name = "PORT", global = true)]
 | 
			
		||||
    port: Option<u16>,
 | 
			
		||||
 | 
			
		||||
    #[serde(skip_serializing_if = "Option::is_none")]
 | 
			
		||||
    #[serde(rename = "path")]
 | 
			
		||||
    #[arg(long, value_name = "SOCKET", global = true)]
 | 
			
		||||
    socket: Option<PathBuf>,
 | 
			
		||||
    #[arg(short, long = "log", value_name = "LOG_LEVEL", global = true)]
 | 
			
		||||
    log_level: Option<LogLevel>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Subcommand)]
 | 
			
		||||
| 
						 | 
				
			
			@ -87,18 +65,17 @@ pub enum Command {
 | 
			
		|||
 | 
			
		||||
impl Cli {
 | 
			
		||||
    pub fn run(&self) -> u8 {
 | 
			
		||||
        let mut figment =
 | 
			
		||||
            Figment::new().merge(Serialized::defaults(crate::config::Config::default()));
 | 
			
		||||
        let mut figment = Figment::new();
 | 
			
		||||
 | 
			
		||||
        if let Some(config_path) = &self.config.config_file {
 | 
			
		||||
            figment = figment.merge(Toml::file(config_path));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let config = figment
 | 
			
		||||
        let config: crate::config::Config = match figment
 | 
			
		||||
            .merge(Env::prefixed("OTTER_"))
 | 
			
		||||
            .merge(Serialized::defaults(self.config.clone()));
 | 
			
		||||
 | 
			
		||||
        let config = match config.extract::<crate::config::Config>() {
 | 
			
		||||
            .merge(Serialized::defaults(self.config.clone()))
 | 
			
		||||
            .extract()
 | 
			
		||||
        {
 | 
			
		||||
            Ok(config) => config,
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                eprintln!("{}", err);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
use std::{sync::Arc, time::Duration};
 | 
			
		||||
 | 
			
		||||
use crate::{config::NetConfig, server};
 | 
			
		||||
use crate::server;
 | 
			
		||||
 | 
			
		||||
pub fn serve(config: &crate::config::Config) -> u8 {
 | 
			
		||||
    tracing_subscriber::fmt()
 | 
			
		||||
| 
						 | 
				
			
			@ -27,63 +27,36 @@ pub fn serve(config: &crate::config::Config) -> u8 {
 | 
			
		|||
        .build()
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    // Spawn the session cleanup background task
 | 
			
		||||
    let address = format!("{}:{}", config.domain, config.port);
 | 
			
		||||
    tracing::info!("Starting server on {address}");
 | 
			
		||||
 | 
			
		||||
    let session_removal_duration = Duration::from_secs(config.session_cleanup_interval);
 | 
			
		||||
 | 
			
		||||
    tracing::info!("Starting session cleanup background task");
 | 
			
		||||
    rt.block_on(async {
 | 
			
		||||
        tokio::task::spawn(async move {
 | 
			
		||||
            let mut interval = tokio::time::interval(session_removal_duration);
 | 
			
		||||
 | 
			
		||||
    rt.spawn(async move {
 | 
			
		||||
        let mut interval = tokio::time::interval(session_removal_duration);
 | 
			
		||||
            loop {
 | 
			
		||||
                interval.tick().await;
 | 
			
		||||
 | 
			
		||||
        loop {
 | 
			
		||||
            interval.tick().await;
 | 
			
		||||
                tracing::info!("Performing session cleanup");
 | 
			
		||||
 | 
			
		||||
            tracing::info!("Performing session cleanup");
 | 
			
		||||
 | 
			
		||||
            match ctx.store.remove_old_sessions() {
 | 
			
		||||
                Ok(n) => {
 | 
			
		||||
                    tracing::info!("Removed {} old sessions", n);
 | 
			
		||||
                }
 | 
			
		||||
                Err(err) => {
 | 
			
		||||
                    tracing::error!("Error occured during session cleanup: {}", err);
 | 
			
		||||
                match ctx.store.remove_old_sessions() {
 | 
			
		||||
                    Ok(n) => {
 | 
			
		||||
                        tracing::info!("Removed {} old sessions", n);
 | 
			
		||||
                    }
 | 
			
		||||
                    Err(err) => {
 | 
			
		||||
                        tracing::error!("Error occured during session cleanup: {}", err);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let listener = tokio::net::TcpListener::bind(address).await.unwrap();
 | 
			
		||||
        axum::serve(listener, app.into_make_service())
 | 
			
		||||
            .await
 | 
			
		||||
            .unwrap()
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    tracing::info!("Initializing server");
 | 
			
		||||
 | 
			
		||||
    match &config.net {
 | 
			
		||||
        NetConfig::Tcp { domain, port } => {
 | 
			
		||||
            let address = format!("{}:{}", domain, port);
 | 
			
		||||
            tracing::info!("Listening on TCP address {address}");
 | 
			
		||||
 | 
			
		||||
            rt.block_on(async {
 | 
			
		||||
                let listener = tokio::net::TcpListener::bind(address).await.unwrap();
 | 
			
		||||
                axum::serve(listener, app.into_make_service())
 | 
			
		||||
                    .await
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
        NetConfig::Unix { path } => {
 | 
			
		||||
            // Try to remove the socket file first if it exists
 | 
			
		||||
            let _ = std::fs::remove_file(&path);
 | 
			
		||||
 | 
			
		||||
            if let Some(parent) = path.parent() {
 | 
			
		||||
                std::fs::create_dir_all(parent).unwrap();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            tracing::info!("Listening on Unix socket {:?}", path);
 | 
			
		||||
 | 
			
		||||
            rt.block_on(async {
 | 
			
		||||
                let listener = tokio::net::UnixListener::bind(path).unwrap();
 | 
			
		||||
 | 
			
		||||
                axum::serve(listener, app.into_make_service())
 | 
			
		||||
                    .await
 | 
			
		||||
                    .unwrap()
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    0
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ use std::path::PathBuf;
 | 
			
		|||
use clap::ValueEnum;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
#[derive(Deserialize, Serialize, Clone, ValueEnum, Copy, Debug)]
 | 
			
		||||
#[derive(Deserialize, Serialize, Clone, ValueEnum, Copy)]
 | 
			
		||||
#[serde(rename_all = "lowercase")]
 | 
			
		||||
pub enum LogLevel {
 | 
			
		||||
    Debug,
 | 
			
		||||
| 
						 | 
				
			
			@ -23,33 +23,37 @@ impl From<LogLevel> for tracing::Level {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
#[serde(rename_all = "lowercase")]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub enum NetConfig {
 | 
			
		||||
    Tcp { domain: String, port: u16 },
 | 
			
		||||
    Unix { path: PathBuf },
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Serialize, Deserialize)]
 | 
			
		||||
#[derive(Deserialize)]
 | 
			
		||||
pub struct Config {
 | 
			
		||||
    pub net: NetConfig,
 | 
			
		||||
    #[serde(default = "default_data_dir")]
 | 
			
		||||
    pub data_dir: PathBuf,
 | 
			
		||||
    #[serde(default = "default_domain")]
 | 
			
		||||
    pub domain: String,
 | 
			
		||||
    #[serde(default = "default_port")]
 | 
			
		||||
    pub port: u16,
 | 
			
		||||
    #[serde(default = "default_session_cleanup_interval")]
 | 
			
		||||
    pub session_cleanup_interval: u64,
 | 
			
		||||
    #[serde(default = "default_log_level")]
 | 
			
		||||
    pub log_level: LogLevel,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for Config {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            net: NetConfig::Tcp {
 | 
			
		||||
                domain: "127.0.0.1".to_string(),
 | 
			
		||||
                port: 8080,
 | 
			
		||||
            },
 | 
			
		||||
            data_dir: "./data".into(),
 | 
			
		||||
            // Once per day
 | 
			
		||||
            session_cleanup_interval: 60 * 60 * 24,
 | 
			
		||||
            log_level: LogLevel::Warn,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
fn default_data_dir() -> PathBuf {
 | 
			
		||||
    PathBuf::from("./data")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn default_domain() -> String {
 | 
			
		||||
    "127.0.0.1".to_string()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn default_port() -> u16 {
 | 
			
		||||
    8080
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn default_session_cleanup_interval() -> u64 {
 | 
			
		||||
    // Default is once a day
 | 
			
		||||
    60 * 60 * 24
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn default_log_level() -> LogLevel {
 | 
			
		||||
    LogLevel::Warn
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue