//! Server configuration //! //! Configuration is loaded from environment variables. //! Required variables: //! - DATABASE_URL: PostgreSQL connection string //! - JWT_SECRET: Secret key for JWT token signing //! //! Optional variables: //! - SERVER_HOST: Host to bind to (default: 0.0.0.0) //! - SERVER_PORT: Port to bind to (default: 3001) //! - DB_MAX_CONNECTIONS: Max database connections (default: 10) //! - DOWNLOADS_DIR: Directory containing agent binaries (default: /var/www/downloads) //! - DOWNLOADS_BASE_URL: Base URL for downloads (default: http://localhost:3001/downloads) //! - AUTO_UPDATE_ENABLED: Enable automatic agent updates (default: true) //! - UPDATE_TIMEOUT_SECS: Timeout for agent updates (default: 180) use std::path::PathBuf; use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; /// Root server configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServerConfig { /// Server binding configuration pub server: ServerBindConfig, /// Database configuration pub database: DatabaseConfig, /// Authentication configuration pub auth: AuthConfig, /// Agent updates configuration pub updates: UpdatesConfig, } /// Server binding configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServerBindConfig { /// Host to bind to pub host: String, /// Port to bind to pub port: u16, } /// Database configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DatabaseConfig { /// PostgreSQL connection URL pub url: String, /// Maximum number of connections in the pool pub max_connections: u32, } /// Authentication configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AuthConfig { /// JWT signing secret pub jwt_secret: String, /// JWT token expiry in hours pub jwt_expiry_hours: u64, /// API key prefix for agents pub api_key_prefix: String, } /// Agent updates configuration #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UpdatesConfig { /// Directory containing agent binaries pub downloads_dir: PathBuf, /// Base URL for agent downloads pub downloads_base_url: String, /// Enable automatic agent updates pub auto_update_enabled: bool, /// Timeout for agent updates in seconds pub update_timeout_secs: u64, /// Interval for scanning downloads directory (seconds) pub scan_interval_secs: u64, } impl ServerConfig { /// Load configuration from environment variables pub fn from_env() -> Result { let database_url = std::env::var("DATABASE_URL").context("DATABASE_URL environment variable not set")?; let jwt_secret = std::env::var("JWT_SECRET").context("JWT_SECRET environment variable not set")?; let host = std::env::var("SERVER_HOST").unwrap_or_else(|_| "0.0.0.0".to_string()); let port = std::env::var("SERVER_PORT") .ok() .and_then(|p| p.parse().ok()) .unwrap_or(3001); let max_connections = std::env::var("DB_MAX_CONNECTIONS") .ok() .and_then(|p| p.parse().ok()) .unwrap_or(10); let jwt_expiry_hours = std::env::var("JWT_EXPIRY_HOURS") .ok() .and_then(|p| p.parse().ok()) .unwrap_or(24); let api_key_prefix = std::env::var("API_KEY_PREFIX").unwrap_or_else(|_| "grmm_".to_string()); // Updates configuration let downloads_dir = std::env::var("DOWNLOADS_DIR") .map(PathBuf::from) .unwrap_or_else(|_| PathBuf::from("/var/www/downloads")); let downloads_base_url = std::env::var("DOWNLOADS_BASE_URL") .unwrap_or_else(|_| format!("http://{}:{}/downloads", host, port)); let auto_update_enabled = std::env::var("AUTO_UPDATE_ENABLED") .map(|v| v.to_lowercase() == "true" || v == "1") .unwrap_or(true); let update_timeout_secs = std::env::var("UPDATE_TIMEOUT_SECS") .ok() .and_then(|p| p.parse().ok()) .unwrap_or(180); let scan_interval_secs = std::env::var("SCAN_INTERVAL_SECS") .ok() .and_then(|p| p.parse().ok()) .unwrap_or(300); // 5 minutes Ok(Self { server: ServerBindConfig { host, port }, database: DatabaseConfig { url: database_url, max_connections, }, auth: AuthConfig { jwt_secret, jwt_expiry_hours, api_key_prefix, }, updates: UpdatesConfig { downloads_dir, downloads_base_url, auto_update_enabled, update_timeout_secs, scan_interval_secs, }, }) } }