use oauth2::{AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl, basic::BasicClient}; use serde::{Deserialize, Serialize}; use std::path::Path; use crate::OidcClient; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { pub oidc: OidcConfig, pub server: ServerConfig, pub database: DatabaseConfig, pub session: SessionConfig, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct OidcConfig { pub client_id: String, pub client_secret: String, pub auth_url: String, pub token_url: String, pub redirect_url: String, } impl OidcConfig { pub fn to_client( &self, ) -> OidcClient { let client_id = ClientId::new(self.client_id.clone()); let client_secret = ClientSecret::new(self.client_secret.clone()); let auth_url = AuthUrl::new(self.auth_url.clone()).unwrap(); let token_url = TokenUrl::new(self.token_url.clone()).unwrap(); let redirect_url = RedirectUrl::new(self.redirect_url.clone()).unwrap(); BasicClient::new(client_id) .set_client_secret(client_secret) .set_auth_uri(auth_url) .set_token_uri(token_url) .set_redirect_uri(redirect_url) } } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ServerConfig { pub host: String, pub port: u16, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DatabaseConfig { pub url: String, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SessionConfig { pub secret: String, } impl Config { /// Load configuration from a TOML file and environment variables /// config-rs merges file config with environment variables automatically pub fn load(path: &str) -> Result> { let mut builder = config::Config::builder(); // Add default values first let defaults = Self::defaults(); builder = builder.add_source( config::Config::try_from(&defaults) .map_err(|e| Box::new(e) as Box)?, ); // Load from file if it exists if Path::new(path).exists() { builder = builder.add_source(config::File::with_name(path).required(false)); } // Override with environment variables // Environment variables should be prefixed with the app name and use __ for nesting // e.g., WEIGHT_TRACKER_OIDC__CLIENT_ID for oidc.client_id builder = builder.add_source( config::Environment::with_prefix("WEIGHT_TRACKER") .try_parsing(true) .separator("__"), ); let config = builder.build()?; let result: Config = config.try_deserialize()?; Ok(result) } /// Get default configuration values fn defaults() -> Self { Config { oidc: OidcConfig { client_id: "your_client_id".to_string(), client_secret: "your_client_secret".to_string(), auth_url: "https://your-provider.com/auth".to_string(), token_url: "https://your-provider.com/token".to_string(), redirect_url: "http://localhost:3000/auth/callback".to_string(), }, server: ServerConfig { host: "127.0.0.1".to_string(), port: 3000, }, database: DatabaseConfig { url: "sqlite:weight_tracker.db".to_string(), }, session: SessionConfig { secret: "your_secret_key_that_is_long_enough_so_the_library_does_not_complain" .to_string(), }, } } }