1use crate::error::{ConsoleError, Result};
4use serde::{Deserialize, Serialize};
5use std::env;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct ConsoleConfig {
10 pub bind_address: String,
12
13 pub port: u16,
15
16 pub auth_endpoint: String,
18
19 pub aviladb_endpoint: String,
21
22 pub storage_endpoint: String,
24
25 pub observability_endpoint: String,
27
28 pub session_secret: String,
30
31 pub debug: bool,
33
34 pub cors_origins: Vec<String>,
36
37 pub rate_limit: u32,
39
40 pub ws_ping_interval: u64,
42
43 pub max_ws_connections: usize,
45
46 pub static_dir: String,
48
49 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 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 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}