litellm_rs/config/
mod.rs

1//! Configuration management for the Gateway
2//!
3//! This module handles loading, validation, and management of all gateway configuration.
4
5pub mod builder;
6pub mod models;
7// pub mod loader;
8// pub mod validation;
9
10pub use models::*;
11// pub use builder::*;  // Commented out until actually used
12// pub use loader::*;
13
14use crate::utils::error::{GatewayError, Result};
15use std::path::Path;
16use tracing::{debug, info};
17
18/// Main configuration struct for the Gateway
19#[derive(Debug, Clone, Default)]
20pub struct Config {
21    /// Gateway configuration
22    pub gateway: GatewayConfig,
23}
24
25#[allow(dead_code)]
26impl Config {
27    /// Load configuration from file
28    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        // Configuration
42        config.validate()?;
43
44        debug!("Configuration loaded successfully");
45        Ok(config)
46    }
47
48    /// Load configuration from environment variables
49    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    /// Get server configuration
60    pub fn server(&self) -> &ServerConfig {
61        &self.gateway.server
62    }
63
64    /// Get providers configuration
65    pub fn providers(&self) -> &[ProviderConfig] {
66        &self.gateway.providers
67    }
68
69    /// Get router settings
70    pub fn router(&self) -> &RouterConfig {
71        &self.gateway.router
72    }
73
74    /// Get storage configuration
75    pub fn storage(&self) -> &StorageConfig {
76        &self.gateway.storage
77    }
78
79    /// Get auth configuration
80    pub fn auth(&self) -> &AuthConfig {
81        &self.gateway.auth
82    }
83
84    /// Get monitoring configuration
85    pub fn monitoring(&self) -> &MonitoringConfig {
86        &self.gateway.monitoring
87    }
88
89    /// Validate the entire configuration
90    pub fn validate(&self) -> Result<()> {
91        debug!("Validating configuration");
92
93        // Validate server configuration
94        self.gateway
95            .server
96            .validate()
97            .map_err(|e| GatewayError::Config(format!("Server config error: {}", e)))?;
98
99        // Validate auth configuration
100        self.gateway
101            .auth
102            .validate()
103            .map_err(|e| GatewayError::Config(format!("Auth config error: {}", e)))?;
104
105        // Validate CORS configuration
106        self.gateway
107            .server
108            .cors
109            .validate()
110            .map_err(|e| GatewayError::Config(format!("CORS config error: {}", e)))?;
111
112        // Warn about insecure configurations
113        crate::config::models::auth::warn_insecure_config(&self.gateway.auth);
114
115        debug!("Configuration validation completed");
116        Ok(())
117    }
118
119    /// Merge with another configuration (other takes precedence)
120    pub fn merge(mut self, other: Self) -> Self {
121        self.gateway = self.gateway.merge(other.gateway);
122        self
123    }
124
125    /// Convert to JSON string
126    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    /// Convert to YAML string
132    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}