Skip to main content

backbone_core/config/
modules_config.rs

1//! Module configurations
2//!
3//! Defines configuration for bounded context modules (Sapiens, Postman, Bucket).
4
5use serde::{Deserialize, Serialize};
6
7// =============================================================================
8// Module Container
9// =============================================================================
10
11/// Module configurations
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct ModulesConfig {
14    /// Sapiens (user management) module
15    #[serde(default)]
16    pub sapiens: SapiensConfig,
17    /// Postman (email) module
18    #[serde(default)]
19    pub postman: PostmanConfig,
20    /// Bucket (file storage) module
21    #[serde(default)]
22    pub bucket: BucketConfig,
23}
24
25#[allow(clippy::derivable_impls)]
26impl Default for ModulesConfig {
27    fn default() -> Self {
28        Self {
29            sapiens: SapiensConfig::default(),
30            postman: PostmanConfig::default(),
31            bucket: BucketConfig::default(),
32        }
33    }
34}
35
36// =============================================================================
37// Sapiens Module
38// =============================================================================
39
40/// Sapiens module configuration
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct SapiensConfig {
43    /// Enable module
44    #[serde(default = "default_true")]
45    pub enabled: bool,
46    /// Database name to use
47    #[serde(default)]
48    pub database: Option<String>,
49    /// Cache name to use
50    #[serde(default)]
51    pub cache: Option<String>,
52    /// Bounded context name
53    #[serde(default = "default_sapiens_context")]
54    pub bounded_context: String,
55    /// Domain version
56    #[serde(default = "default_domain_version")]
57    pub domain_version: String,
58    /// Events this context publishes
59    #[serde(default)]
60    pub publishes: Vec<String>,
61    /// Services this context provides
62    #[serde(default)]
63    pub provides: Vec<String>,
64    /// Authentication configuration
65    #[serde(default)]
66    pub auth: Option<SapiensAuthConfig>,
67    /// Lockout configuration
68    #[serde(default)]
69    pub lockout: Option<SapiensLockoutConfig>,
70}
71
72fn default_true() -> bool { true }
73fn default_false() -> bool { false }
74fn default_sapiens_context() -> String { "user-management".to_string() }
75fn default_domain_version() -> String { "1.0.0".to_string() }
76
77impl Default for SapiensConfig {
78    fn default() -> Self {
79        Self {
80            enabled: true,
81            database: Some("default".to_string()),
82            cache: Some("default".to_string()),
83            bounded_context: default_sapiens_context(),
84            domain_version: default_domain_version(),
85            publishes: vec![
86                "sapiens.user.created".to_string(),
87                "sapiens.user.updated".to_string(),
88                "sapiens.user.deleted".to_string(),
89            ],
90            provides: vec![
91                "user.authentication".to_string(),
92                "user.authorization".to_string(),
93                "user.profile".to_string(),
94            ],
95            auth: Some(SapiensAuthConfig::default()),
96            lockout: Some(SapiensLockoutConfig::default()),
97        }
98    }
99}
100
101/// Sapiens authentication configuration
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct SapiensAuthConfig {
104    /// JWT secret key
105    pub jwt_secret: String,
106    /// Token expiration in hours
107    #[serde(default = "default_token_expiration")]
108    pub token_expiration_hours: u64,
109    /// Refresh token expiration in days
110    #[serde(default = "default_refresh_expiration")]
111    pub refresh_token_expiration_days: u64,
112    /// Password hasher configuration
113    #[serde(default)]
114    pub password_hasher: PasswordHasherConfig,
115}
116
117fn default_token_expiration() -> u64 { 24 }
118fn default_refresh_expiration() -> u64 { 30 }
119
120impl Default for SapiensAuthConfig {
121    fn default() -> Self {
122        Self {
123            jwt_secret: String::new(), // Must be set via env
124            token_expiration_hours: default_token_expiration(),
125            refresh_token_expiration_days: default_refresh_expiration(),
126            password_hasher: PasswordHasherConfig::default(),
127        }
128    }
129}
130
131/// Password hasher configuration (Argon2)
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct PasswordHasherConfig {
134    /// Memory cost (m_cost)
135    #[serde(default = "default_iterations")]
136    pub iterations: u32,
137    /// Time cost (t_cost)
138    #[serde(default = "default_memory")]
139    pub memory: u32,
140    /// Parallelism (p_cost)
141    #[serde(default = "default_parallelism")]
142    pub parallelism: u32,
143    /// Output hash length
144    #[serde(default = "default_hash_length")]
145    pub hash_length: u32,
146}
147
148fn default_iterations() -> u32 { 19456 }
149fn default_memory() -> u32 { 2 }
150fn default_parallelism() -> u32 { 1 }
151fn default_hash_length() -> u32 { 32 }
152
153impl Default for PasswordHasherConfig {
154    fn default() -> Self {
155        Self {
156            iterations: default_iterations(),
157            memory: default_memory(),
158            parallelism: default_parallelism(),
159            hash_length: default_hash_length(),
160        }
161    }
162}
163
164/// Sapiens lockout configuration
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct SapiensLockoutConfig {
167    /// Maximum failed attempts before lockout
168    #[serde(default = "default_max_attempts")]
169    pub max_attempts: u32,
170    /// Lockout duration in minutes
171    #[serde(default = "default_lockout_duration")]
172    pub duration_minutes: u32,
173    /// Reset counter after hours
174    #[serde(default)]
175    pub reset_after_hours: Option<u32>,
176}
177
178fn default_max_attempts() -> u32 { 5 }
179fn default_lockout_duration() -> u32 { 15 }
180
181impl Default for SapiensLockoutConfig {
182    fn default() -> Self {
183        Self {
184            max_attempts: default_max_attempts(),
185            duration_minutes: default_lockout_duration(),
186            reset_after_hours: Some(24),
187        }
188    }
189}
190
191// =============================================================================
192// Postman Module
193// =============================================================================
194
195/// Postman module configuration
196#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct PostmanConfig {
198    /// Enable module
199    #[serde(default = "default_false")]
200    pub enabled: bool,
201    /// Database name to use
202    #[serde(default)]
203    pub database: Option<String>,
204    /// Bounded context name
205    #[serde(default = "default_postman_context")]
206    pub bounded_context: String,
207    /// Domain version
208    #[serde(default = "default_domain_version")]
209    pub domain_version: String,
210    /// Events this context listens to
211    #[serde(default)]
212    pub listens_to: Vec<String>,
213    /// SMTP configuration
214    #[serde(default)]
215    pub smtp: Option<SmtpConfig>,
216    /// Email templates configuration
217    #[serde(default)]
218    pub templates: Option<TemplatesConfig>,
219}
220
221fn default_postman_context() -> String { "email-notification".to_string() }
222
223impl Default for PostmanConfig {
224    fn default() -> Self {
225        Self {
226            enabled: false,
227            database: Some("default".to_string()),
228            bounded_context: default_postman_context(),
229            domain_version: default_domain_version(),
230            listens_to: vec![
231                "sapiens.user.created".to_string(),
232                "sapiens.user.email.changed".to_string(),
233            ],
234            smtp: None,
235            templates: None,
236        }
237    }
238}
239
240/// SMTP configuration
241#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct SmtpConfig {
243    /// SMTP host
244    pub host: String,
245    /// SMTP port
246    #[serde(default = "default_smtp_port")]
247    pub port: u16,
248    /// SMTP username
249    #[serde(default)]
250    pub username: Option<String>,
251    /// SMTP password
252    #[serde(default)]
253    pub password: Option<String>,
254    /// Use TLS
255    #[serde(default = "default_use_tls")]
256    pub use_tls: Option<bool>,
257    /// From email address
258    pub from_email: String,
259    /// From name
260    #[serde(default)]
261    pub from_name: Option<String>,
262}
263
264fn default_use_tls() -> Option<bool> { Some(true) }
265fn default_smtp_port() -> u16 { 587 }
266
267/// Email templates configuration
268#[derive(Debug, Clone, Serialize, Deserialize, Default)]
269pub struct TemplatesConfig {
270    /// Welcome email template path
271    #[serde(default)]
272    pub welcome_email: Option<String>,
273    /// Password reset template path
274    #[serde(default)]
275    pub password_reset: Option<String>,
276    /// Email verification template path
277    #[serde(default)]
278    pub email_verification: Option<String>,
279}
280
281// =============================================================================
282// Bucket Module
283// =============================================================================
284
285/// Bucket module configuration
286#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct BucketConfig {
288    /// Enable module
289    #[serde(default = "default_false")]
290    pub enabled: bool,
291    /// Database name to use
292    #[serde(default)]
293    pub database: Option<String>,
294    /// Bounded context name
295    #[serde(default = "default_bucket_context")]
296    pub bounded_context: String,
297    /// Domain version
298    #[serde(default = "default_domain_version")]
299    pub domain_version: String,
300    /// Storage configuration
301    #[serde(default)]
302    pub storage: Option<StorageConfig>,
303}
304
305fn default_bucket_context() -> String { "file-storage".to_string() }
306
307impl Default for BucketConfig {
308    fn default() -> Self {
309        Self {
310            enabled: false,
311            database: Some("default".to_string()),
312            bounded_context: default_bucket_context(),
313            domain_version: default_domain_version(),
314            storage: None,
315        }
316    }
317}
318
319/// Storage configuration
320#[derive(Debug, Clone, Serialize, Deserialize)]
321pub struct StorageConfig {
322    /// Storage driver (local, s3, minio)
323    #[serde(default = "default_storage_driver")]
324    pub driver: String,
325    /// Base path for local storage
326    #[serde(default)]
327    pub base_path: Option<String>,
328    /// Maximum file size (e.g., "100MB")
329    #[serde(default)]
330    pub max_file_size: Option<String>,
331    /// Allowed file extensions
332    #[serde(default)]
333    pub allowed_extensions: Option<Vec<String>>,
334}
335
336fn default_storage_driver() -> String { "local".to_string() }