avl_console/
config.rs

1//! Configuration management for AVL Console
2
3use crate::error::{ConsoleError, Result};
4use serde::{Deserialize, Serialize};
5use std::env;
6
7/// Console configuration
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ConsoleConfig {
10    /// Server bind address
11    pub bind_address: String,
12
13    /// Server port
14    pub port: u16,
15
16    /// AVL Auth endpoint
17    pub auth_endpoint: String,
18
19    /// AvilaDB endpoint
20    pub aviladb_endpoint: String,
21
22    /// AVL Storage endpoint
23    pub storage_endpoint: String,
24
25    /// AVL Observability endpoint
26    pub observability_endpoint: String,
27
28    /// Session secret for cookies
29    pub session_secret: String,
30
31    /// Enable debug mode
32    pub debug: bool,
33
34    /// CORS allowed origins
35    pub cors_origins: Vec<String>,
36
37    /// Rate limit: requests per minute
38    pub rate_limit: u32,
39
40    /// WebSocket ping interval (seconds)
41    pub ws_ping_interval: u64,
42
43    /// Maximum WebSocket connections per user
44    pub max_ws_connections: usize,
45
46    /// Static files directory
47    pub static_dir: String,
48
49    /// Templates directory
50    pub templates_dir: String,
51}
52
53impl Default for ConsoleConfig {
54    fn default() -> Self {
55        Self {
56            bind_address: "127.0.0.1".to_string(),
57            port: 8080,
58            auth_endpoint: "http://localhost:8001".to_string(),
59            aviladb_endpoint: "http://localhost:8000".to_string(),
60            storage_endpoint: "http://localhost:8002".to_string(),
61            observability_endpoint: "http://localhost:8003".to_string(),
62            session_secret: "avl-console-secret-change-in-production".to_string(),
63            debug: false,
64            cors_origins: vec!["http://localhost:8080".to_string()],
65            rate_limit: 100,
66            ws_ping_interval: 30,
67            max_ws_connections: 10,
68            static_dir: "static".to_string(),
69            templates_dir: "templates".to_string(),
70        }
71    }
72}
73
74impl ConsoleConfig {
75    /// Load configuration from environment variables
76    ///
77    /// # Environment Variables
78    ///
79    /// - `AVL_CONSOLE_BIND`: Server bind address (default: 127.0.0.1)
80    /// - `AVL_CONSOLE_PORT`: Server port (default: 8080)
81    /// - `AVL_AUTH_ENDPOINT`: AVL Auth endpoint
82    /// - `AVL_AVILADB_ENDPOINT`: AvilaDB endpoint
83    /// - `AVL_STORAGE_ENDPOINT`: Storage endpoint
84    /// - `AVL_OBSERVABILITY_ENDPOINT`: Observability endpoint
85    /// - `AVL_CONSOLE_SECRET`: Session secret
86    /// - `AVL_CONSOLE_DEBUG`: Enable debug mode
87    pub fn from_env() -> Result<Self> {
88        let mut config = Self::default();
89
90        if let Ok(bind) = env::var("AVL_CONSOLE_BIND") {
91            config.bind_address = bind;
92        }
93
94        if let Ok(port) = env::var("AVL_CONSOLE_PORT") {
95            config.port = port.parse().map_err(|_| {
96                ConsoleError::Config("Invalid port number".to_string())
97            })?;
98        }
99
100        if let Ok(endpoint) = env::var("AVL_AUTH_ENDPOINT") {
101            config.auth_endpoint = endpoint;
102        }
103
104        if let Ok(endpoint) = env::var("AVL_AVILADB_ENDPOINT") {
105            config.aviladb_endpoint = endpoint;
106        }
107
108        if let Ok(endpoint) = env::var("AVL_STORAGE_ENDPOINT") {
109            config.storage_endpoint = endpoint;
110        }
111
112        if let Ok(endpoint) = env::var("AVL_OBSERVABILITY_ENDPOINT") {
113            config.observability_endpoint = endpoint;
114        }
115
116        if let Ok(secret) = env::var("AVL_CONSOLE_SECRET") {
117            config.session_secret = secret;
118        }
119
120        if let Ok(debug) = env::var("AVL_CONSOLE_DEBUG") {
121            config.debug = debug.parse().unwrap_or(false);
122        }
123
124        if let Ok(origins) = env::var("AVL_CONSOLE_CORS_ORIGINS") {
125            config.cors_origins = origins.split(',').map(|s| s.to_string()).collect();
126        }
127
128        if let Ok(limit) = env::var("AVL_CONSOLE_RATE_LIMIT") {
129            config.rate_limit = limit.parse().unwrap_or(100);
130        }
131
132        Ok(config)
133    }
134
135    /// Validate configuration
136    pub fn validate(&self) -> Result<()> {
137        if self.session_secret == "avl-console-secret-change-in-production" {
138            tracing::warn!("⚠️  Using default session secret. Change in production!");
139        }
140
141        if self.rate_limit == 0 {
142            return Err(ConsoleError::Config(
143                "Rate limit must be greater than 0".to_string(),
144            ));
145        }
146
147        Ok(())
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    #[test]
156    fn test_default_config() {
157        let config = ConsoleConfig::default();
158        assert_eq!(config.port, 8080);
159        assert!(!config.debug);
160    }
161
162    #[test]
163    fn test_config_validation() {
164        let config = ConsoleConfig::default();
165        assert!(config.validate().is_ok());
166    }
167}