1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Config {
14 #[serde(default)]
16 pub node: NodeSettings,
17
18 #[serde(default)]
20 pub router: RouterSettings,
21
22 #[serde(default)]
24 pub discovery: DiscoverySettings,
25
26 #[serde(default)]
28 pub security: SecuritySettings,
29
30 #[serde(default)]
32 pub advanced: AdvancedSettings,
33}
34
35impl Config {
36 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 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 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#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct NodeSettings {
71 pub id: Option<String>,
73
74 pub name: Option<String>,
76
77 pub bind_ip: Option<String>,
79
80 pub router_port: u16,
82
83 pub api_port: u16,
85
86 pub data_dir: String,
88
89 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#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct RouterSettings {
110 pub global_timeout_ms: u64,
112
113 pub max_connections: usize,
115
116 pub enable_retries: bool,
118
119 pub default_retry_count: u32,
121
122 pub tcp_proxy: TcpProxySettings,
124
125 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#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct TcpProxySettings {
145 pub enabled: bool,
147
148 pub listen_port: u16,
150
151 pub connection_pooling: bool,
153
154 pub max_idle_time_secs: u64,
156
157 pub udp_enabled: bool,
159
160 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#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct DiscoverySettings {
180 pub health_check_interval_secs: u64,
182
183 pub health_check_timeout_ms: u64,
185
186 pub default_health_check_path: String,
188
189 pub service_ttl_secs: u64,
191
192 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#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct SecuritySettings {
211 pub tls_enabled: bool,
213
214 pub tls_cert_path: Option<String>,
216
217 pub tls_key_path: Option<String>,
219
220 pub require_client_certs: bool,
222
223 pub ca_cert_path: Option<String>,
225
226 pub api_auth_enabled: bool,
228
229 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#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct AdvancedSettings {
250 pub log_level: String,
252
253 pub metrics_enabled: bool,
255
256 pub metrics_port: u16,
258
259 pub profiling_enabled: bool,
261
262 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#[derive(Debug, Clone)]
280pub struct NodeConfig {
281 pub node_id: Option<String>,
283
284 pub bind_addr: Option<String>,
286
287 pub router_port: u16,
289
290 pub api_port: u16,
292
293 pub data_dir: PathBuf,
295
296 pub config_path: String,
298}
299
300impl NodeConfig {
301 pub fn node_id(&self) -> String {
303 self.node_id.clone().unwrap_or_else(|| Uuid::new_v4().to_string())
304 }
305
306 pub fn bind_addr(&self) -> String {
308 self.bind_addr.clone().unwrap_or_else(|| "0.0.0.0".to_string())
309 }
310
311 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 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}