1pub mod builder;
6pub mod models;
7pub use models::*;
11use crate::utils::error::{GatewayError, Result};
15use std::path::Path;
16use tracing::{debug, info};
17
18#[derive(Debug, Clone, Default)]
20pub struct Config {
21 pub gateway: GatewayConfig,
23}
24
25#[allow(dead_code)]
26impl Config {
27 pub async fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
29 let path = path.as_ref();
30 info!("Loading configuration from: {:?}", path);
31
32 let content = tokio::fs::read_to_string(path)
33 .await
34 .map_err(|e| GatewayError::Config(format!("Failed to read config file: {}", e)))?;
35
36 let gateway: GatewayConfig = serde_yaml::from_str(&content)
37 .map_err(|e| GatewayError::Config(format!("Failed to parse config: {}", e)))?;
38
39 let config = Self { gateway };
40
41 config.validate()?;
43
44 debug!("Configuration loaded successfully");
45 Ok(config)
46 }
47
48 pub fn from_env() -> Result<Self> {
50 info!("Loading configuration from environment variables");
51
52 let gateway = GatewayConfig::from_env()?;
53 let config = Self { gateway };
54
55 config.validate()?;
56 Ok(config)
57 }
58
59 pub fn server(&self) -> &ServerConfig {
61 &self.gateway.server
62 }
63
64 pub fn providers(&self) -> &[ProviderConfig] {
66 &self.gateway.providers
67 }
68
69 pub fn router(&self) -> &RouterConfig {
71 &self.gateway.router
72 }
73
74 pub fn storage(&self) -> &StorageConfig {
76 &self.gateway.storage
77 }
78
79 pub fn auth(&self) -> &AuthConfig {
81 &self.gateway.auth
82 }
83
84 pub fn monitoring(&self) -> &MonitoringConfig {
86 &self.gateway.monitoring
87 }
88
89 pub fn validate(&self) -> Result<()> {
91 debug!("Validating configuration");
92
93 self.gateway
95 .server
96 .validate()
97 .map_err(|e| GatewayError::Config(format!("Server config error: {}", e)))?;
98
99 self.gateway
101 .auth
102 .validate()
103 .map_err(|e| GatewayError::Config(format!("Auth config error: {}", e)))?;
104
105 self.gateway
107 .server
108 .cors
109 .validate()
110 .map_err(|e| GatewayError::Config(format!("CORS config error: {}", e)))?;
111
112 crate::config::models::auth::warn_insecure_config(&self.gateway.auth);
114
115 debug!("Configuration validation completed");
116 Ok(())
117 }
118
119 pub fn merge(mut self, other: Self) -> Self {
121 self.gateway = self.gateway.merge(other.gateway);
122 self
123 }
124
125 pub fn to_json(&self) -> Result<String> {
127 serde_json::to_string_pretty(&self.gateway)
128 .map_err(|e| GatewayError::Config(format!("Failed to serialize config to JSON: {}", e)))
129 }
130
131 pub fn to_yaml(&self) -> Result<String> {
133 serde_yaml::to_string(&self.gateway)
134 .map_err(|e| GatewayError::Config(format!("Failed to serialize config to YAML: {}", e)))
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::*;
141 use std::io::Write;
142 use tempfile::NamedTempFile;
143
144 #[tokio::test]
145 async fn test_config_from_file() {
146 let config_content = r#"
147server:
148 host: "127.0.0.1"
149 port: 8080
150 workers: 4
151
152providers:
153 - name: "openai"
154 provider_type: "openai"
155 api_key: "test-key"
156 api_base: "https://api.openai.com/v1"
157
158router:
159 strategy:
160 type: "round_robin"
161 circuit_breaker:
162 failure_threshold: 5
163 recovery_timeout: 30
164
165storage:
166 database:
167 url: "postgresql://localhost/gateway"
168 redis:
169 url: "redis://localhost:6379"
170
171auth:
172 jwt_secret: "test-secret-that-is-at-least-32-characters-long-for-security"
173 api_key_header: "Authorization"
174
175monitoring:
176 metrics:
177 enabled: true
178 port: 9090
179"#;
180
181 let mut temp_file = NamedTempFile::new().unwrap();
182 temp_file.write_all(config_content.as_bytes()).unwrap();
183
184 let config = Config::from_file(temp_file.path()).await.unwrap();
185
186 assert_eq!(config.server().host, "127.0.0.1");
187 assert_eq!(config.server().port, 8080);
188 assert_eq!(config.providers().len(), 1);
189 assert_eq!(config.providers()[0].name, "openai");
190 }
191
192 #[test]
193 fn test_default_config() {
194 let config = Config::default();
195 assert!(config.validate().is_ok());
196 }
197
198 #[test]
199 fn test_config_serialization() {
200 let config = Config::default();
201
202 let json = config.to_json().unwrap();
203 assert!(!json.is_empty());
204
205 let yaml = config.to_yaml().unwrap();
206 assert!(!yaml.is_empty());
207 }
208}