Lodestone/
config.rs

1// src/config.rs
2use anyhow::Result;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::fs;
6use std::net::{IpAddr, SocketAddr};
7use std::path::{Path, PathBuf};
8use tracing::{info, warn};
9use uuid::Uuid;
10
11/// Main configuration for Lodestone
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Config {
14    /// General node configuration
15    #[serde(default)]
16    pub node: NodeSettings,
17    
18    /// Router configuration
19    #[serde(default)]
20    pub router: RouterSettings,
21    
22    /// Discovery configuration
23    #[serde(default)]
24    pub discovery: DiscoverySettings,
25    
26    /// Security settings
27    #[serde(default)]
28    pub security: SecuritySettings,
29    
30    /// Advanced settings
31    #[serde(default)]
32    pub advanced: AdvancedSettings,
33}
34
35impl Config {
36    /// Load configuration from file
37    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
38        let content = fs::read_to_string(&path)?;
39        let config: Config = toml::from_str(&content)?;
40        Ok(config)
41    }
42    
43    /// Save configuration to file
44    pub fn save_to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
45        let content = toml::to_string_pretty(self)?;
46        fs::write(path, content)?;
47        Ok(())
48    }
49    
50    /// Create a new default configuration
51    pub fn new() -> Self {
52        Self::default()
53    }
54}
55
56impl Default for Config {
57    fn default() -> Self {
58        Self {
59            node: NodeSettings::default(),
60            router: RouterSettings::default(),
61            discovery: DiscoverySettings::default(),
62            security: SecuritySettings::default(),
63            advanced: AdvancedSettings::default(),
64        }
65    }
66}
67
68/// Node-specific settings
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct NodeSettings {
71    /// Unique identifier for this node
72    pub id: Option<String>,
73    
74    /// Node name for human-readable identification
75    pub name: Option<String>,
76    
77    /// IP address to bind to
78    pub bind_ip: Option<String>,
79    
80    /// Port for the router to listen on
81    pub router_port: u16,
82    
83    /// Port for the API to listen on
84    pub api_port: u16,
85    
86    /// Directory to store data
87    pub data_dir: String,
88    
89    /// Tags for node categorization
90    pub tags: Vec<String>,
91}
92
93impl Default for NodeSettings {
94    fn default() -> Self {
95        Self {
96            id: None,
97            name: None,
98            bind_ip: Some("0.0.0.0".to_string()),
99            router_port: 8080,
100            api_port: 8081,
101            data_dir: "./data".to_string(),
102            tags: vec![],
103        }
104    }
105}
106
107/// Router configuration
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct RouterSettings {
110    /// Global timeout for requests in milliseconds
111    pub global_timeout_ms: u64,
112    
113    /// Maximum number of connections
114    pub max_connections: usize,
115    
116    /// Enable automatic retries
117    pub enable_retries: bool,
118    
119    /// Default number of retries
120    pub default_retry_count: u32,
121    
122    /// TCP proxy configuration
123    pub tcp_proxy: TcpProxySettings,
124    
125    /// Static routes (path -> upstream)
126    pub static_routes: HashMap<String, String>,
127}
128
129impl Default for RouterSettings {
130    fn default() -> Self {
131        Self {
132            global_timeout_ms: 30000,
133            max_connections: 10000,
134            enable_retries: true,
135            default_retry_count: 3,
136            tcp_proxy: TcpProxySettings::default(),
137            static_routes: HashMap::new(),
138        }
139    }
140}
141
142/// TCP proxy configuration
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct TcpProxySettings {
145    /// Enable TCP proxy
146    pub enabled: bool,
147    
148    /// TCP listen port
149    pub listen_port: u16,
150    
151    /// Enable connection pooling
152    pub connection_pooling: bool,
153    
154    /// Maximum idle time in seconds
155    pub max_idle_time_secs: u64,
156    
157    /// Enable UDP proxy
158    pub udp_enabled: bool,
159    
160    /// UDP listen port
161    pub udp_listen_port: u16,
162}
163
164impl Default for TcpProxySettings {
165    fn default() -> Self {
166        Self {
167            enabled: true,
168            listen_port: 9090,
169            connection_pooling: true,
170            max_idle_time_secs: 60,
171            udp_enabled: false,
172            udp_listen_port: 9090,
173        }
174    }
175}
176
177/// Service discovery configuration
178#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct DiscoverySettings {
180    /// Health check interval in seconds
181    pub health_check_interval_secs: u64,
182    
183    /// Health check timeout in milliseconds
184    pub health_check_timeout_ms: u64,
185    
186    /// Default health check path for HTTP services
187    pub default_health_check_path: String,
188    
189    /// TTL for service registrations in seconds
190    pub service_ttl_secs: u64,
191    
192    /// Deregistration delay in seconds
193    pub deregistration_delay_secs: u64,
194}
195
196impl Default for DiscoverySettings {
197    fn default() -> Self {
198        Self {
199            health_check_interval_secs: 10,
200            health_check_timeout_ms: 2000,
201            default_health_check_path: "/health".to_string(),
202            service_ttl_secs: 60,
203            deregistration_delay_secs: 30,
204        }
205    }
206}
207
208/// Security settings
209#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct SecuritySettings {
211    /// Enable TLS
212    pub tls_enabled: bool,
213    
214    /// Path to TLS certificate
215    pub tls_cert_path: Option<String>,
216    
217    /// Path to TLS key
218    pub tls_key_path: Option<String>,
219    
220    /// Require client certificates
221    pub require_client_certs: bool,
222    
223    /// Path to CA certificate
224    pub ca_cert_path: Option<String>,
225    
226    /// Enable API authentication
227    pub api_auth_enabled: bool,
228    
229    /// API access tokens
230    pub api_tokens: Vec<String>,
231}
232
233impl Default for SecuritySettings {
234    fn default() -> Self {
235        Self {
236            tls_enabled: false,
237            tls_cert_path: None,
238            tls_key_path: None,
239            require_client_certs: false,
240            ca_cert_path: None,
241            api_auth_enabled: false,
242            api_tokens: vec![],
243        }
244    }
245}
246
247/// Advanced settings
248#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct AdvancedSettings {
250    /// Log level
251    pub log_level: String,
252    
253    /// Enable metrics
254    pub metrics_enabled: bool,
255    
256    /// Metrics port
257    pub metrics_port: u16,
258    
259    /// Enable profiling
260    pub profiling_enabled: bool,
261    
262    /// Profiling port
263    pub profiling_port: u16,
264}
265
266impl Default for AdvancedSettings {
267    fn default() -> Self {
268        Self {
269            log_level: "info".to_string(),
270            metrics_enabled: true,
271            metrics_port: 9100,
272            profiling_enabled: false,
273            profiling_port: 9101,
274        }
275    }
276}
277
278/// Node configuration passed from command line
279#[derive(Debug, Clone)]
280pub struct NodeConfig {
281    /// Unique identifier for this node
282    pub node_id: Option<String>,
283    
284    /// Bind address for all services
285    pub bind_addr: Option<String>,
286    
287    /// Port for the router
288    pub router_port: u16,
289    
290    /// Port for the API
291    pub api_port: u16,
292    
293    /// Data directory
294    pub data_dir: PathBuf,
295    
296    /// Configuration file path
297    pub config_path: String,
298}
299
300impl NodeConfig {
301    /// Generate a node ID if one was not provided
302    pub fn node_id(&self) -> String {
303        self.node_id.clone().unwrap_or_else(|| Uuid::new_v4().to_string())
304    }
305    
306    /// Get the bind address
307    pub fn bind_addr(&self) -> String {
308        self.bind_addr.clone().unwrap_or_else(|| "0.0.0.0".to_string())
309    }
310    
311    /// Load the full configuration, overriding with command line values
312    pub fn load_full_config(&self) -> Result<Config> {
313        let mut config = if let Ok(cfg) = Config::from_file(&self.config_path) {
314            cfg
315        } else {
316            warn!("Could not load config from {}, using defaults", self.config_path);
317            Config::default()
318        };
319        
320        // Override with command line values
321        if let Some(node_id) = &self.node_id {
322            config.node.id = Some(node_id.clone());
323        }
324        
325        if let Some(bind_addr) = &self.bind_addr {
326            config.node.bind_ip = Some(bind_addr.clone());
327        }
328        
329        config.node.router_port = self.router_port;
330        config.node.api_port = self.api_port;
331        config.node.data_dir = self.data_dir.to_string_lossy().to_string();
332        
333        Ok(config)
334    }
335}