AkurAI Build
Menu

Public repository / 3a463af917761506796f6f58d832f707a1e1af3a

AkurAI-Monitor / src/config.rs

Latest change 8bade644cef8ddaa358cc88eb6017c8e64d5a6e4 - AKURAI-MONITOR-7 monitor processes, docker, systemd; fix #errors alert delivery by Ólafur Búi Ólafsson

use std::sync::LazyLock;

pub struct Config {
    pub listen_addr: String,
    pub db_url: String,
    pub static_dir: String,
    pub alert_log: String,
    pub log_files: Vec<String>,
    pub interval_secs: u64,
    pub retention_days: i64,
    pub log_retention_days: i64,
    pub log_debug_retention_days: i64,
    pub log_max_rows: i64,
    pub log_stale_secs: i64,
    pub journal_replay_secs: i64,
    pub oidc_issuer: String,
    pub oidc_client_id: String,
    pub oidc_client_secret: String,
    pub oidc_redirect_uri: String,
    pub ingest_token: String,
    pub api_token: String,
    pub journal_units: Vec<String>,
    pub journal_match: Vec<String>,
    pub systemd_match: Vec<String>,
    pub proc_top_n: usize,
    pub pibun_ingest_url: String,
    pub pibun_ingest_token: String,
    pub pibun_ingest_channel: String,
}

fn cli_overrides(
    args: impl Iterator<Item = String>,
) -> (Option<String>, Option<String>, Option<String>) {
    let mut static_dir = None;
    let mut host = None;
    let mut port = None;
    let mut args = args.peekable();
    while let Some(arg) = args.next() {
        let target = match arg.as_str() {
            "--dir" => &mut static_dir,
            "--host" => &mut host,
            "--port" => &mut port,
            _ => continue,
        };
        *target = args.next();
    }
    (static_dir, host, port)
}

static CONFIG: LazyLock<Config> = LazyLock::new(|| {
    let (static_dir, host, port) = cli_overrides(std::env::args().skip(1));
    let listen_addr = match (host, port) {
        (Some(host), Some(port)) => format!("{host}:{port}"),
        _ => std::env::var("MONITOR_LISTEN").unwrap_or_else(|_| "127.0.0.1:8800".to_string()),
    };
    let log_files_str = std::env::var("MONITOR_LOG_FILES")
        .unwrap_or_else(|_| "/var/log/syslog,/var/log/auth.log".to_string());
    let log_files: Vec<String> = log_files_str
        .split(',')
        .map(|s| s.trim().to_string())
        .filter(|s| !s.is_empty())
        .collect();

    let base_url = std::env::var("MONITOR_BASE_URL")
        .unwrap_or_else(|_| "https://monitor.olibuijr.com".to_string())
        .trim_end_matches('/')
        .to_string();

    let redirect_uri = std::env::var("MONITOR_OIDC_REDIRECT_URI")
        .unwrap_or_else(|_| format!("{base_url}/auth/callback"));

    let journal_match: Vec<String> = std::env::var("MONITOR_JOURNAL_MATCH")
        .unwrap_or_else(|_| "akurai-,golfsetrid,rust-monitor".to_string())
        .split(',')
        .map(|s| s.trim().to_string())
        .filter(|s| !s.is_empty())
        .collect();

    Config {
        listen_addr,
        db_url: std::env::var("MONITOR_DB_URL")
            .unwrap_or_else(|_| "mysql://monitor@127.0.0.1:3306/akurai_monitor".to_string()),
        static_dir: static_dir
            .or_else(|| std::env::var("MONITOR_STATIC_DIR").ok())
            .unwrap_or_else(|| "./ui".to_string()),
        alert_log: std::env::var("MONITOR_ALERT_LOG")
            .unwrap_or_else(|_| "./data/alerts.log".to_string()),
        log_files,
        interval_secs: std::env::var("MONITOR_INTERVAL")
            .ok()
            .and_then(|v| v.parse().ok())
            .unwrap_or(60),
        retention_days: std::env::var("MONITOR_RETENTION_DAYS")
            .ok()
            .and_then(|v| v.parse().ok())
            .unwrap_or(30),
        log_retention_days: std::env::var("MONITOR_LOG_RETENTION_DAYS")
            .ok()
            .and_then(|v| v.parse().ok())
            .unwrap_or(7),
        log_debug_retention_days: std::env::var("MONITOR_LOG_DEBUG_RETENTION_DAYS")
            .ok()
            .and_then(|v| v.parse().ok())
            .unwrap_or(2),
        log_max_rows: std::env::var("MONITOR_LOG_MAX_ROWS")
            .ok()
            .and_then(|v| v.parse().ok())
            .unwrap_or(2_000_000),
        log_stale_secs: std::env::var("MONITOR_LOG_STALE_SECS")
            .ok()
            .and_then(|v| v.parse().ok())
            .unwrap_or(900),
        journal_replay_secs: std::env::var("MONITOR_JOURNAL_REPLAY_SECS")
            .ok()
            .and_then(|v| v.parse().ok())
            .unwrap_or(300),
        oidc_issuer: std::env::var("MONITOR_OIDC_ISSUER")
            .unwrap_or_else(|_| "https://auth.olibuijr.com".to_string()),
        oidc_client_id: std::env::var("MONITOR_OIDC_CLIENT_ID").unwrap_or_default(),
        oidc_client_secret: std::env::var("MONITOR_OIDC_CLIENT_SECRET").unwrap_or_default(),
        oidc_redirect_uri: redirect_uri,
        ingest_token: std::env::var("MONITOR_INGEST_TOKEN").unwrap_or_default(),
        api_token: std::env::var("MONITOR_API_TOKEN").unwrap_or_default(),
        journal_units: std::env::var("MONITOR_JOURNAL_UNITS")
            .unwrap_or_default()
            .split(',')
            .map(|s| s.trim().to_string())
            .filter(|s| !s.is_empty())
            .collect(),
        journal_match: journal_match.clone(),
        systemd_match: std::env::var("MONITOR_SYSTEMD_MATCH")
            .ok()
            .map(|raw| {
                raw.split(',')
                    .map(|s| s.trim().to_string())
                    .filter(|s| !s.is_empty())
                    .collect()
            })
            .unwrap_or(journal_match),
        proc_top_n: std::env::var("MONITOR_PROC_TOP_N")
            .ok()
            .and_then(|v| v.parse().ok())
            .unwrap_or(15),
        pibun_ingest_url: std::env::var("MONITOR_PIBUN_INGEST_URL")
            .unwrap_or_else(|_| "http://100.88.0.9:4173/api/ingest-error".to_string()),
        pibun_ingest_token: std::env::var("MONITOR_PIBUN_INGEST_TOKEN").unwrap_or_default(),
        pibun_ingest_channel: std::env::var("MONITOR_PIBUN_INGEST_CHANNEL")
            .unwrap_or_else(|_| "errors".to_string()),
    }
});

pub fn get() -> &'static Config {
    &CONFIG
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn deployment_cli_overrides_static_directory_and_listener() {
        let args = [
            "serve",
            "--dir",
            "/opt/rust-monitor/app",
            "--host",
            "127.0.0.1",
            "--port",
            "8800",
            "--name",
            "rust-monitor",
        ]
        .into_iter()
        .map(str::to_owned);

        assert_eq!(
            cli_overrides(args),
            (
                Some("/opt/rust-monitor/app".to_string()),
                Some("127.0.0.1".to_string()),
                Some("8800".to_string())
            )
        );
    }
}