1use std::env;
2
3#[derive(Debug, Clone)]
5pub struct Config {
6 pub port: u16,
8 pub environment: String,
9 pub log_level: String,
10
11 pub db_host: String,
13 pub db_port: u16,
14 pub db_name: String,
15 pub db_user: String,
16 pub db_password: String,
17 pub db_max_connections: u32,
18
19 pub jwt_secret: String,
21 pub jwt_expiration: i64, pub refresh_token_expiration: i64, pub cors_origins: Vec<String>,
26
27 pub upload_dir: String,
29 pub upload_max_size: u64, pub cdn_url: String,
33
34 pub mail_from: String,
36 pub smtp_host: String,
37 pub smtp_port: u16,
38 pub smtp_user: String,
39 pub smtp_password: String,
40
41 pub site_name: String,
43 pub site_url: String,
44 pub base_url: String,
45}
46
47impl Config {
48 pub fn from_env() -> Self {
50 Self {
51 port: env_parse("PORT", 3000),
53 environment: env_or("APP_ENV", "development"),
54 log_level: env_or("LOG_LEVEL", "info"),
55
56 db_host: env_or("DB_HOST", "127.0.0.1"),
58 #[cfg(feature = "mysql")]
59 db_port: env_parse("DB_PORT", 3306),
60 #[cfg(feature = "postgres")]
61 db_port: env_parse("DB_PORT", 5432),
62 db_name: env_required("DB_NAME"),
63 db_user: env_required("DB_USER"),
64 db_password: env_or("DB_PASSWORD", ""),
65 db_max_connections: env_parse("DB_MAX_CONNECTIONS", 10),
66
67 jwt_secret: env_required("JWT_SECRET"),
69 jwt_expiration: env_parse("JWT_EXPIRATION", 900), refresh_token_expiration: env_parse("REFRESH_TOKEN_EXPIRATION", 2_592_000), cors_origins: env_or("CORS_ORIGINS", "*")
74 .split(',')
75 .map(|s| s.trim().to_string())
76 .collect(),
77
78 upload_dir: env_or("UPLOAD_DIR", "./uploads"),
80 upload_max_size: env_parse("UPLOAD_MAX_SIZE", 10_485_760), cdn_url: env_or("CDN_URL", ""),
84
85 mail_from: env_or("MAIL_FROM", "noreply@localhost"),
87 smtp_host: env_or("SMTP_HOST", "localhost"),
88 smtp_port: env_parse("SMTP_PORT", 587),
89 smtp_user: env_or("SMTP_USER", ""),
90 smtp_password: env_or("SMTP_PASSWORD", ""),
91
92 site_name: env_or("SITE_NAME", "Karbon"),
94 site_url: env_or("SITE_URL", "http://localhost:3000"),
95 base_url: env_or("BASE_URL", "http://localhost:3000/"),
96 }
97 }
98
99 pub fn is_production(&self) -> bool {
101 self.environment == "production"
102 }
103
104 #[cfg(feature = "mysql")]
106 pub fn database_url(&self) -> String {
107 format!(
108 "mysql://{}:{}@{}:{}/{}",
109 self.db_user, self.db_password, self.db_host, self.db_port, self.db_name
110 )
111 }
112
113 #[cfg(feature = "postgres")]
115 pub fn database_url(&self) -> String {
116 format!(
117 "postgresql://{}:{}@{}:{}/{}",
118 self.db_user, self.db_password, self.db_host, self.db_port, self.db_name
119 )
120 }
121}
122
123fn env_or(key: &str, default: &str) -> String {
125 env::var(key).unwrap_or_else(|_| default.to_string())
126}
127
128fn env_required(key: &str) -> String {
130 env::var(key).unwrap_or_else(|_| panic!("Environment variable {} is required", key))
131}
132
133fn env_parse<T: std::str::FromStr>(key: &str, default: T) -> T {
135 env::var(key)
136 .ok()
137 .and_then(|v| v.parse().ok())
138 .unwrap_or(default)
139}