//! Ortam değişkenlerinden uygulama ayarlarını okur. use std::{env, path::PathBuf}; #[derive(Debug, Clone)] pub struct Config { pub mongodb_uri: String, pub jwt_secret: String, pub app_url: String, pub port: u16, pub registration: RegistrationMode, /// Derlenmiş frontend klasörü (`STATIC_DIR`). Tanımlıysa Axum arayüzü de sunar /// (canlıda tek servis). Geliştirmede boş: arayüzü Vite sunar. pub static_dir: Option, /// Otomatik kur yenileme aralığı (dakika, `RATES_REFRESH_MINUTES`, varsayılan 60); 0 → kapalı pub rates_refresh_minutes: u64, } /// Yeni hane kaydı politikası (`REGISTRATION` ortam değişkeni). #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RegistrationMode { /// Varsayılan. Sistemde hiç hane yokken ilk kayıt yeni hane kurar; /// sonrasında yalnızca davet koduyla katılınır. InviteOnly, /// Herkes yeni hane kurabilir. Open, } #[derive(Debug, thiserror::Error)] pub enum ConfigError { #[error("{0} ortam değişkeni tanımlı değil (.env dosyasını kontrol edin)")] Missing(&'static str), #[error("{0} geçersiz: {1}")] Invalid(&'static str, &'static str), } impl Config { pub fn from_env() -> Result { let mongodb_uri = required("MONGODB_URI")?; let jwt_secret = required("JWT_SECRET")?; if jwt_secret.len() < 32 { return Err(ConfigError::Invalid( "JWT_SECRET", "en az 32 karakter olmalı (openssl rand -hex 32)", )); } let app_url = env::var("APP_URL").unwrap_or_else(|_| "http://localhost:5173".into()); let registration = match env::var("REGISTRATION").as_deref() { Err(_) | Ok("") | Ok("invite_only") => RegistrationMode::InviteOnly, Ok("open") => RegistrationMode::Open, Ok(_) => { return Err(ConfigError::Invalid( "REGISTRATION", "\"invite_only\" veya \"open\" olmalı", )); } }; let static_dir = env::var("STATIC_DIR") .ok() .filter(|v| !v.trim().is_empty()) .map(PathBuf::from); let port = match env::var("PORT") { Ok(p) => p .parse() .map_err(|_| ConfigError::Invalid("PORT", "sayı olmalı"))?, Err(_) => 8080, }; let rates_refresh_minutes = match env::var("RATES_REFRESH_MINUTES") { Ok(m) if !m.trim().is_empty() => m.trim().parse().map_err(|_| { ConfigError::Invalid("RATES_REFRESH_MINUTES", "dakika (sayı) olmalı") })?, _ => 60, }; Ok(Self { mongodb_uri, jwt_secret, app_url, port, registration, static_dir, rates_refresh_minutes, }) } /// Cookie'lerde `Secure` bayrağı yalnızca HTTPS'te açılır. pub fn is_https(&self) -> bool { self.app_url.starts_with("https://") } } fn required(key: &'static str) -> Result { env::var(key) .ok() .filter(|v| !v.trim().is_empty()) .ok_or(ConfigError::Missing(key)) }