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 test_config(jwt_secret: &str) -> Self {
101 Self {
102 port: 3005,
103 environment: "test".into(),
104 log_level: "error".into(),
105 db_host: "127.0.0.1".into(),
106 db_port: 3306,
107 db_name: "test".into(),
108 db_user: "test".into(),
109 db_password: "test".into(),
110 db_max_connections: 1,
111 jwt_secret: jwt_secret.into(),
112 jwt_expiration: 3600,
113 refresh_token_expiration: 86400,
114 cors_origins: vec!["*".into()],
115 upload_dir: "/tmp".into(),
116 upload_max_size: 10_485_760,
117 cdn_url: "".into(),
118 mail_from: "test@test.com".into(),
119 smtp_host: "localhost".into(),
120 smtp_port: 587,
121 smtp_user: "".into(),
122 smtp_password: "".into(),
123 site_name: "Test".into(),
124 site_url: "http://localhost:3005".into(),
125 base_url: "http://localhost:3005/".into(),
126 }
127 }
128
129 pub fn is_production(&self) -> bool {
131 self.environment == "production"
132 }
133
134 #[cfg(feature = "mysql")]
136 pub fn database_url(&self) -> String {
137 format!(
138 "mysql://{}:{}@{}:{}/{}",
139 self.db_user, self.db_password, self.db_host, self.db_port, self.db_name
140 )
141 }
142
143 #[cfg(feature = "postgres")]
145 pub fn database_url(&self) -> String {
146 format!(
147 "postgresql://{}:{}@{}:{}/{}",
148 self.db_user, self.db_password, self.db_host, self.db_port, self.db_name
149 )
150 }
151}
152
153fn env_or(key: &str, default: &str) -> String {
155 env::var(key).unwrap_or_else(|_| default.to_string())
156}
157
158fn env_required(key: &str) -> String {
160 env::var(key).unwrap_or_else(|_| panic!("Environment variable {} is required", key))
161}
162
163fn env_parse<T: std::str::FromStr>(key: &str, default: T) -> T {
165 env::var(key)
166 .ok()
167 .and_then(|v| v.parse().ok())
168 .unwrap_or(default)
169}