use log::{info, warn}; use once_cell::sync::Lazy; use std::env; #[derive(Clone)] pub struct Env { pub port: u16, pub access_token_secret: String, pub refresh_token_secret: String, pub database_url: String, pub pool_size: u32, pub connect_timeout: u64, pub idle_timeout: u64, pub max_lifetime: Option, pub statement_timeout: Option, pub idle_in_transaction_session_timeout: Option, pub sslmode: String, pub retry_attempts: u32, pub retry_delay: u64, pub smtp_email: String, pub smtp_password: String, pub smtp_name: String, pub smtp_host: String, pub redisdb_url: String, pub fe_url: String, pub rust_env: String, pub minio_endpoint: String, pub minio_bucket_name: String, pub minio_access_key: String, pub minio_secret_key: String, pub minio_region: String, pub minio_secure: bool, pub google_client_id: String, pub google_client_secret: String, pub google_redirect_url: String, pub cdn_url: String, pub cors_allowed_origins: Vec, } impl std::fmt::Debug for Env { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Env") .field("port", &self.port) .field("access_token_secret", &"***") .field("refresh_token_secret", &"***") .field("database_url", &"***") .field("pool_size", &self.pool_size) .field("connect_timeout", &self.connect_timeout) .field("idle_timeout", &self.idle_timeout) .field("max_lifetime", &self.max_lifetime) .field("statement_timeout", &self.statement_timeout) .field( "idle_in_transaction_session_timeout", &self.idle_in_transaction_session_timeout, ) .field("sslmode", &self.sslmode) .field("retry_attempts", &self.retry_attempts) .field("retry_delay", &self.retry_delay) .field("smtp_email", &self.smtp_email) .field("smtp_password", &"***") .field("smtp_name", &self.smtp_name) .field("smtp_host", &self.smtp_host) .field("redisdb_url", &self.redisdb_url) .field("fe_url", &self.fe_url) .field("rust_env", &self.rust_env) .field("minio_endpoint", &self.minio_endpoint) .field("minio_bucket_name", &self.minio_bucket_name) .field("minio_access_key", &"***") .field("minio_secret_key", &"***") .field("minio_region", &self.minio_region) .field("minio_secure", &self.minio_secure) .field("google_client_id", &self.google_client_id) .field("google_client_secret", &"***") .field("google_redirect_url", &self.google_redirect_url) .field("cdn_url", &self.cdn_url) .field("cors_allowed_origins", &self.cors_allowed_origins) .finish() } } fn get_env_with_warning(key: &str, default: &str) -> String { match env::var(key) { Ok(val) => val, Err(_) => { warn!( "Environment variable '{}' is not set. Using default: '{}'", key, default ); default.to_string() } } } fn get_env_u16_with_warning(key: &str, default: u16) -> u16 { match env::var(key) { Ok(val) => val.parse().unwrap_or_else(|_| { warn!( "Environment variable '{}' has invalid value '{}'. Using default: {}", key, val, default ); default }), Err(_) => { warn!( "Environment variable '{}' is not set. Using default: {}", key, default ); default } } } fn get_env_bool_with_warning(key: &str, default: bool) -> bool { match env::var(key) { Ok(val) => val.parse().unwrap_or_else(|_| { warn!( "Environment variable '{}' has invalid value '{}'. Using default: {}", key, val, default ); default }), Err(_) => { warn!( "Environment variable '{}' is not set. Using default: {}", key, default ); default } } } pub static ENV: Lazy = Lazy::new(|| { load_dotenv_file(); let env = Env { port: get_env_u16_with_warning("PORT", 3000), access_token_secret: get_env_with_warning( "ACCESS_TOKEN_SECRET", "default_access_secret", ), refresh_token_secret: get_env_with_warning( "REFRESH_TOKEN_SECRET", "default_refresh_secret", ), database_url: get_env_with_warning( "DATABASE_URL", "postgres://postgres:postgres@localhost:5432/imphnen", ), pool_size: get_env_with_warning("POOL_SIZE", "10") .parse() .unwrap_or(10), connect_timeout: get_env_with_warning("CONNECT_TIMEOUT", "30") .parse() .unwrap_or(30), idle_timeout: get_env_with_warning("IDLE_TIMEOUT", "60") .parse() .unwrap_or(60), max_lifetime: get_env_with_warning("MAX_LIFETIME", "1800").parse().ok(), statement_timeout: get_env_with_warning("STATEMENT_TIMEOUT", "30000") .parse() .ok(), idle_in_transaction_session_timeout: get_env_with_warning( "IDLE_IN_TRANSACTION_SESSION_TIMEOUT", "60000", ) .parse() .ok(), sslmode: get_env_with_warning("SSLMODE", "require"), retry_attempts: get_env_with_warning("RETRY_ATTEMPTS", "3") .parse() .unwrap_or(3), retry_delay: get_env_with_warning("RETRY_DELAY", "1") .parse() .unwrap_or(1), smtp_email: get_env_with_warning("SMTP_EMAIL", "no-reply@example.com"), smtp_password: get_env_with_warning("SMTP_PASSWORD", "default_smtp_password"), smtp_name: get_env_with_warning("SMTP_NAME", "MyApp SMTP"), smtp_host: get_env_with_warning("SMTP_HOST", "smtp.gmail.com"), redisdb_url: get_env_with_warning("REDISDB_URL", "localhost"), fe_url: get_env_with_warning("FE_URL", "http://localhost"), rust_env: get_env_with_warning("RUST_ENV", "development"), minio_endpoint: get_env_with_warning("MINIO_ENDPOINT", "http://localhost:9000"), minio_bucket_name: get_env_with_warning("MINIO_BUCKET_NAME", "imphnen-uploads"), minio_access_key: get_env_with_warning("MINIO_ACCESS_KEY", "minio_access"), minio_secret_key: get_env_with_warning("MINIO_SECRET_KEY", "minio_secret"), minio_region: get_env_with_warning("MINIO_REGION", "us-east-1"), minio_secure: get_env_bool_with_warning("MINIO_SECURE", false), google_client_id: get_env_with_warning( "GOOGLE_CLIENT_ID", "default_google_client_id", ), google_client_secret: get_env_with_warning( "GOOGLE_CLIENT_SECRET", "default_google_client_secret", ), google_redirect_url: get_env_with_warning( "GOOGLE_REDIRECT_URL", "http://localhost:8000/api/v1/auth/google/callback", ), cdn_url: get_env_with_warning("CDN_URL", "https://cdn.asepharyana.tech"), cors_allowed_origins: get_env_with_warning( "CORS_ALLOWED_ORIGINS", "https://gacha.imphnen.dev,https://imphnen.dev,https://dimentorin.imphnen.dev", ) .split(',') .map(|s| s.trim().to_string()) .filter(|s| !s.is_empty()) .collect(), }; info!("Environment configuration loaded successfully"); env }); fn load_dotenv_file() { match dotenvy::dotenv() { Ok(path) => info!("Loaded environment file: {:?}", path), Err(dotenvy::Error::Io(ref e)) if e.kind() == std::io::ErrorKind::NotFound => { warn!(".env file not found, falling back to system environment variables"); } Err(e) => { warn!( "Failed to load .env file: {}. Falling back to system environment variables", e ); } } }