Skip to main content

mini_apm/
config.rs

1use std::env;
2
3#[derive(Clone, Debug)]
4pub struct Config {
5    pub sqlite_path: String,
6    pub api_key: Option<String>,
7    pub retention_days_errors: i64,
8    pub retention_days_hourly_rollups: i64,
9    pub retention_days_spans: i64,
10    pub slow_request_threshold_ms: f64,
11    pub mini_apm_url: String,
12    pub enable_user_accounts: bool,
13    pub enable_projects: bool,
14    pub session_secret: String,
15}
16
17impl Config {
18    pub fn from_env() -> anyhow::Result<Self> {
19        // SESSION_SECRET is required when user accounts are enabled
20        let enable_user_accounts = env::var("ENABLE_USER_ACCOUNTS")
21            .map(|v| v == "1" || v.to_lowercase() == "true")
22            .unwrap_or(false);
23
24        let session_secret = env::var("SESSION_SECRET").ok();
25
26        if enable_user_accounts && session_secret.is_none() {
27            anyhow::bail!(
28                "SESSION_SECRET environment variable is required when ENABLE_USER_ACCOUNTS=true. \
29                Generate one with: openssl rand -hex 32"
30            );
31        }
32
33        // Warn if using default secret in development
34        let session_secret = session_secret.unwrap_or_else(|| {
35            if enable_user_accounts {
36                // This shouldn't happen due to the check above, but just in case
37                panic!("SESSION_SECRET is required");
38            }
39            // In single-user mode, generate a random secret per run
40            use rand::Rng;
41            let bytes: [u8; 32] = rand::thread_rng().r#gen();
42            hex::encode(bytes)
43        });
44
45        Ok(Self {
46            sqlite_path: env::var("SQLITE_PATH")
47                .unwrap_or_else(|_| "./data/miniapm.db".to_string()),
48            api_key: env::var("MINI_APM_API_KEY").ok(),
49            retention_days_errors: env::var("RETENTION_DAYS_ERRORS")
50                .ok()
51                .and_then(|v| v.parse().ok())
52                .filter(|&v| v > 0)
53                .unwrap_or(30),
54            retention_days_hourly_rollups: env::var("RETENTION_DAYS_HOURLY_ROLLUPS")
55                .ok()
56                .and_then(|v| v.parse().ok())
57                .filter(|&v| v > 0)
58                .unwrap_or(90),
59            retention_days_spans: env::var("RETENTION_DAYS_SPANS")
60                .ok()
61                .and_then(|v| v.parse().ok())
62                .filter(|&v| v > 0)
63                .unwrap_or(7),
64            slow_request_threshold_ms: env::var("SLOW_REQUEST_THRESHOLD_MS")
65                .ok()
66                .and_then(|v| v.parse().ok())
67                .filter(|&v| v > 0.0)
68                .unwrap_or(500.0),
69            mini_apm_url: env::var("MINI_APM_URL")
70                .unwrap_or_else(|_| "http://localhost:3000".to_string()),
71            enable_user_accounts,
72            enable_projects: env::var("ENABLE_PROJECTS")
73                .map(|v| v == "1" || v.to_lowercase() == "true")
74                .unwrap_or(false),
75            session_secret,
76        })
77    }
78
79    pub fn api_key_configured(&self) -> bool {
80        self.api_key.as_ref().is_some_and(|k| !k.is_empty())
81    }
82}