backbone_core/config/
mod.rs1mod error;
26mod loader;
27mod schema;
28mod bus;
29
30mod app_config;
32mod contexts_config;
33mod database_config;
34mod features_config;
35mod logging_config;
36mod modules_config;
37mod monitoring_config;
38mod security_config;
39mod server_config;
40
41pub use error::{ConfigError, ConfigResult};
42pub use loader::ConfigLoader;
43pub use schema::{ConfigValidationError, validate_config};
44
45pub use app_config::{AppConfig, Environment};
47pub use contexts_config::{ContextsConfig, RedisEventBusConfig};
48pub use database_config::{CacheConfig, DatabaseConfig};
49pub use features_config::{FeaturesConfig, RateLimitingConfig};
50pub use logging_config::{LoggingConfig, LoggingFileConfig};
51pub use modules_config::{
52 ModulesConfig, PasswordHasherConfig, PostmanConfig, SapiensAuthConfig, SapiensConfig,
53 SapiensLockoutConfig, SmtpConfig, StorageConfig, BucketConfig, TemplatesConfig,
54};
55pub use monitoring_config::MonitoringConfig;
56pub use security_config::{CsrfConfig, SecurityConfig, SecurityHeadersConfig};
57pub use server_config::ServerConfig;
58
59pub use bus::{ConfigurationBus, ConfigValue, ConfigChangeEvent};
61
62use serde::{Deserialize, Serialize};
63use std::collections::HashMap;
64use std::path::Path;
65
66#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct BackboneConfig {
73 pub app: AppConfig,
75 pub server: ServerConfig,
77 #[serde(default)]
79 pub database: HashMap<String, DatabaseConfig>,
80 #[serde(default)]
82 pub cache: HashMap<String, CacheConfig>,
83 pub modules: ModulesConfig,
85 pub logging: LoggingConfig,
87 pub monitoring: MonitoringConfig,
89 pub contexts: ContextsConfig,
91 pub features: FeaturesConfig,
93 pub security: SecurityConfig,
95}
96
97impl BackboneConfig {
98 pub fn from_file<P: AsRef<Path>>(path: P) -> ConfigResult<Self> {
109 ConfigLoader::load_file(path)
110 }
111
112 pub fn from_file_with_env<P: AsRef<Path>>(base_path: P, env: &str) -> ConfigResult<Self> {
127 ConfigLoader::load_with_env(base_path, env)
128 }
129
130 pub fn from_env() -> ConfigResult<Self> {
134 let mut config = Self::default();
135 config.apply_env_overrides()?;
136 config.validate()?;
137 Ok(config)
138 }
139
140 fn apply_env_overrides(&mut self) -> ConfigResult<()> {
142 if let Ok(host) = std::env::var("HOST") {
144 self.server.host = host;
145 }
146 if let Ok(port) = std::env::var("PORT") {
147 self.server.port = port.parse().map_err(|_| {
148 ConfigError::env_var("PORT")
149 })?;
150 }
151 if let Ok(workers) = std::env::var("WORKERS") {
152 self.server.workers = Some(workers.parse().map_err(|_| {
153 ConfigError::env_var("WORKERS")
154 })?);
155 }
156
157 if let Ok(url) = std::env::var("DATABASE_URL") {
159 if let Some(db) = self.database.get_mut("default") {
160 db.url = url;
161 }
162 }
163
164 if let Ok(secret) = std::env::var("JWT_SECRET") {
166 if let Some(ref mut auth) = self.modules.sapiens.auth {
167 auth.jwt_secret = secret;
168 }
169 }
170
171 if let Ok(url) = std::env::var("REDIS_URL") {
173 if let Some(cache) = self.cache.get_mut("default") {
174 cache.url = url;
175 }
176 }
177
178 Ok(())
179 }
180
181 pub fn validate(&self) -> ConfigResult<()> {
185 validate_config(self)
186 }
187
188 pub fn default_database(&self) -> Option<&DatabaseConfig> {
190 self.database.get("default")
191 }
192
193 pub fn get_database(&self, name: &str) -> Option<&DatabaseConfig> {
195 self.database.get(name)
196 }
197
198 pub fn default_cache(&self) -> Option<&CacheConfig> {
200 self.cache.get("default")
201 }
202
203 pub fn get_cache(&self, name: &str) -> Option<&CacheConfig> {
205 self.cache.get(name)
206 }
207
208 pub fn is_module_enabled(&self, module_name: &str) -> bool {
210 match module_name {
211 "sapiens" => self.modules.sapiens.enabled,
212 "postman" => self.modules.postman.enabled,
213 "bucket" => self.modules.bucket.enabled,
214 _ => false,
215 }
216 }
217
218 pub fn environment(&self) -> &Environment {
220 &self.app.environment
221 }
222
223 pub fn is_production(&self) -> bool {
225 matches!(self.app.environment, Environment::Production)
226 }
227
228 pub fn is_development(&self) -> bool {
230 matches!(self.app.environment, Environment::Development)
231 }
232
233 pub fn is_debug(&self) -> bool {
235 self.app.debug
236 }
237
238 pub fn merge(mut self, other: Self) -> Self {
242 if other.app.name != self.app.name {
244 self.app = other.app;
245 }
246
247 self.server = other.server;
249
250 for (name, db_config) in other.database {
252 self.database.insert(name, db_config);
253 }
254
255 for (name, cache_config) in other.cache {
257 self.cache.insert(name, cache_config);
258 }
259
260 self.modules = other.modules;
262
263 self.logging = other.logging;
265
266 self.monitoring = other.monitoring;
268
269 self.contexts = other.contexts;
271
272 self.features = other.features;
274
275 self.security = other.security;
277
278 self
279 }
280}
281
282impl Default for BackboneConfig {
283 fn default() -> Self {
284 let mut database = HashMap::new();
285 database.insert(
286 "default".to_string(),
287 DatabaseConfig::default(),
288 );
289
290 let mut cache = HashMap::new();
291 cache.insert(
292 "default".to_string(),
293 CacheConfig::default(),
294 );
295
296 Self {
297 app: AppConfig::default(),
298 server: ServerConfig::default(),
299 database,
300 cache,
301 modules: ModulesConfig::default(),
302 logging: LoggingConfig::default(),
303 monitoring: MonitoringConfig::default(),
304 contexts: ContextsConfig::default(),
305 features: FeaturesConfig::default(),
306 security: SecurityConfig::default(),
307 }
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use super::*;
314
315 #[test]
316 fn test_default_config() {
317 let config = BackboneConfig::default();
318
319 assert_eq!(config.server.host, "0.0.0.0");
320 assert_eq!(config.server.port, 3000);
321 assert!(config.default_database().is_some());
322 assert!(config.default_cache().is_some());
323 }
324
325 #[test]
326 fn test_module_enabled() {
327 let config = BackboneConfig::default();
328
329 assert!(config.is_module_enabled("sapiens"));
330 assert!(!config.is_module_enabled("postman"));
331 assert!(!config.is_module_enabled("bucket"));
332 assert!(!config.is_module_enabled("unknown"));
333 }
334
335 #[test]
336 fn test_environment_checks() {
337 let mut config = BackboneConfig::default();
338
339 assert!(config.is_development());
340 assert!(!config.is_production());
341
342 config.app.environment = Environment::Production;
343 assert!(config.is_production());
344 assert!(!config.is_development());
345 }
346
347 #[test]
348 fn test_merge_configs() {
349 let mut config1 = BackboneConfig::default();
350 config1.server.port = 3000;
351
352 let mut config2 = BackboneConfig::default();
353 config2.server.port = 8080;
354 config2.server.host = "127.0.0.1".to_string();
355
356 let merged = config1.merge(config2);
357
358 assert_eq!(merged.server.port, 8080);
359 assert_eq!(merged.server.host, "127.0.0.1");
360 }
361}