use std::collections::HashMap; use std::sync::OnceLock; pub static GLOBAL_CONFIG: OnceLock = OnceLock::new(); #[derive(Clone, Debug)] pub struct AppConfig { pub env: HashMap, } impl AppConfig { const ENV_FILES: &'static [&'static str] = &[".env", ".env.local"]; pub fn load() -> AppConfig { let mut env = HashMap::new(); for env_file in AppConfig::ENV_FILES { if let Err(e) = dotenvy::from_path(env_file) { tracing::debug!(file = %env_file, error = %e, "dotenv load skipped"); } if let Ok(env_file_content) = std::fs::read_to_string(env_file) { for line in env_file_content.lines() { if let Some((key, value)) = line.split_once('=') { env.insert(key.to_string(), value.to_string()); } } } } // Environment variables (e.g. K8s injected APP_DOMAIN_URL) take precedence over .env files env = std::env::vars().chain(env).collect(); let this = AppConfig { env }; // Handle the race condition: if another thread already set the global, return it. // This is safe because config is immutable after load. if GLOBAL_CONFIG.get().is_some() { GLOBAL_CONFIG.get().unwrap().clone() } else { let _ = GLOBAL_CONFIG.set(this); GLOBAL_CONFIG .get() .expect("global config should be set after load") .clone() } } } pub mod ai; pub mod app; pub mod avatar; pub mod database; pub mod domain; pub mod embed; pub mod hook; pub mod logs; pub mod nats; pub mod qdrant; pub mod redis; pub mod smtp; pub mod ssh; pub mod storage;