admin_config/
app_config.rs1use crate::LlmConfig;
26use crate::{
27 AuthConfig, CosConfig, DatabaseConfig, DevelopmentConfig, EmailConfig, RateLimitConfig, RedisConfig,
28 SecurityConfig, ServerConfig, SessionConfig, SmsConfig, UploadConfig, VerificationCodeConfig,
29};
30use anyhow::{Context, Result};
31use serde::{Deserialize, Serialize};
32use std::path::{Path, PathBuf};
33
34#[derive(Debug, Clone, Serialize, Deserialize, Default)]
35pub struct AppConfig {
36 #[serde(default)]
38 pub server: ServerConfig,
39 #[serde(default)]
41 pub database: DatabaseConfig,
42 #[serde(default)]
44 pub redis: RedisConfig,
45 #[serde(default)]
47 pub auth: AuthConfig,
48 #[serde(default)]
50 pub email: EmailConfig,
51 #[serde(default)]
53 pub sms: SmsConfig,
54 #[serde(default)]
56 pub verification_code: VerificationCodeConfig,
57 #[serde(default)]
59 pub cos: CosConfig,
60 #[serde(default)]
62 pub security: SecurityConfig,
63 #[serde(default)]
65 pub session: SessionConfig,
66 #[serde(default)]
68 pub upload: UploadConfig,
69 #[serde(default)]
71 pub rate_limit: RateLimitConfig,
72 #[serde(default)]
74 pub development: DevelopmentConfig,
75 #[serde(default)]
77 pub llm: Vec<LlmConfig>,
78}
79
80impl AppConfig {
81 pub fn load() -> Result<Self> {
83 Self::load_from_default_path()
84 }
85
86 pub fn load_from_default_path() -> Result<Self> {
88 let config_path = std::env::var("CONFIG_PATH").unwrap_or_else(|_| "config.toml".to_string());
89
90 let path = if Path::new(&config_path).exists() {
91 PathBuf::from(config_path)
92 } else {
93 match Self::find_config_file() {
94 Ok(p) => p,
95 Err(_) => {
96 log::warn!("Config file not found. Using default configuration with environment variables.");
97 return Ok(Self::default_with_env());
98 }
99 }
100 };
101
102 Self::load_from_path(&path)
103 }
104
105 fn load_from_path(path: &Path) -> Result<Self> {
107 let config_content = std::fs::read_to_string(path).with_context(|| format!("无法读取配置文件: {:?}", path))?;
108 let mut config: Self = toml::from_str(&config_content).with_context(|| "解析配置文件失败")?;
109 config.apply_env_overrides();
110 Ok(config)
111 }
112
113 fn default_with_env() -> Self {
115 let mut config = Self::default();
116 config.apply_env_overrides();
117 config
118 }
119
120 pub fn from_file(path: &str) -> Result<Self> {
122 Self::load_from_path(Path::new(path))
123 }
124
125 fn find_config_file() -> Result<PathBuf> {
133 if let Ok(config_path) = std::env::var("CONFIG_PATH") {
135 let path = Path::new(&config_path);
136 if path.exists() {
137 return Ok(path.to_path_buf());
138 }
139 }
140
141 let possible_paths = vec![
143 PathBuf::from("config.toml"),
144 PathBuf::from("apps/admin-server/config.toml"),
145 ];
146
147 for path in &possible_paths {
148 if path.exists() {
149 return Ok(path.clone());
150 }
151 }
152
153 if let Ok(exe_path) = std::env::current_exe()
155 && let Some(exe_dir) = exe_path.parent()
156 {
157 let config_path = exe_dir.join("config.toml");
158 if config_path.exists() {
159 return Ok(config_path);
160 }
161 }
162
163 let cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
165 Err(anyhow::anyhow!(
166 "无法找到配置文件 config.toml\n\
167 当前工作目录: {:?}\n\
168 已尝试以下路径:\n{}\n\
169 请确保:\n\
170 1. 设置 CONFIG_PATH 环境变量指向配置文件\n\
171 2. 在项目根目录运行程序\n\
172 3. 或将 config.toml 放在可执行文件同目录",
173 cwd,
174 possible_paths
175 .iter()
176 .map(|p| format!(" - {:?}", p))
177 .collect::<Vec<_>>()
178 .join("\n")
179 ))
180 }
181
182 fn apply_env_overrides(&mut self) {
184 if let Ok(port) = std::env::var("PORT").or_else(|_| std::env::var("SERVER__PORT"))
186 && let Ok(p) = port.parse()
187 {
188 self.server.port = p;
189 }
190 if let Ok(log_level) = std::env::var("RUST_LOG").or_else(|_| std::env::var("SERVER__LOG_LEVEL")) {
191 self.server.log_level = log_level;
192 }
193
194 if let Ok(host) = std::env::var("REDIS_IP").or_else(|_| std::env::var("REDIS__HOST")) {
196 self.redis.host = host;
197 }
198 if let Ok(port) = std::env::var("REDIS_PORT").or_else(|_| std::env::var("REDIS__PORT"))
199 && let Ok(p) = port.parse()
200 {
201 self.redis.port = p;
202 }
203 if let Ok(password) = std::env::var("REDIS_PASSWORD").or_else(|_| std::env::var("REDIS__PASSWORD")) {
204 self.redis.password = Some(password);
205 }
206
207 if let Ok(host) = std::env::var("MONGODB_IP").or_else(|_| std::env::var("DATABASE__MONGODB__HOST")) {
209 self.database.mongodb.host = host;
210 }
211 if let Ok(port) = std::env::var("MONGODB_PORT").or_else(|_| std::env::var("DATABASE__MONGODB__PORT"))
212 && let Ok(p) = port.parse()
213 {
214 self.database.mongodb.port = p;
215 }
216 if let Ok(user) = std::env::var("MONGODB_USER").or_else(|_| std::env::var("DATABASE__MONGODB__USERNAME")) {
217 self.database.mongodb.username = user;
218 }
219 if let Ok(password) =
220 std::env::var("MONGODB_PASSWORD").or_else(|_| std::env::var("DATABASE__MONGODB__PASSWORD"))
221 {
222 self.database.mongodb.password = password;
223 }
224 if let Ok(database) =
225 std::env::var("MONGODB_DATABASE").or_else(|_| std::env::var("DATABASE__MONGODB__DATABASE"))
226 {
227 self.database.mongodb.database = database;
228 }
229
230 if let Ok(host) = std::env::var("MYSQL_HOST").or_else(|_| std::env::var("DATABASE__MYSQL__HOST")) {
232 self.database.mysql.host = host;
233 }
234 if let Ok(port) = std::env::var("MYSQL_PORT").or_else(|_| std::env::var("DATABASE__MYSQL__PORT"))
235 && let Ok(p) = port.parse()
236 {
237 self.database.mysql.port = p;
238 }
239 if let Ok(user) = std::env::var("MYSQL_USER").or_else(|_| std::env::var("DATABASE__MYSQL__USERNAME")) {
240 self.database.mysql.username = user;
241 }
242 if let Ok(password) = std::env::var("MYSQL_PASSWORD").or_else(|_| std::env::var("DATABASE__MYSQL__PASSWORD")) {
243 self.database.mysql.password = password;
244 }
245 if let Ok(database) = std::env::var("MYSQL_DATABASE").or_else(|_| std::env::var("DATABASE__MYSQL__DATABASE")) {
246 self.database.mysql.database = database;
247 }
248
249 if let Ok(host) = std::env::var("POSTGRES_HOST").or_else(|_| std::env::var("DATABASE__POSTGRESQL__HOST")) {
251 self.database.postgresql.host = host;
252 }
253 if let Ok(port) = std::env::var("POSTGRES_PORT").or_else(|_| std::env::var("DATABASE__POSTGRESQL__PORT"))
254 && let Ok(p) = port.parse()
255 {
256 self.database.postgresql.port = p;
257 }
258 if let Ok(user) = std::env::var("POSTGRES_USER").or_else(|_| std::env::var("DATABASE__POSTGRESQL__USERNAME")) {
259 self.database.postgresql.username = user;
260 }
261 if let Ok(password) =
262 std::env::var("POSTGRES_PASSWORD").or_else(|_| std::env::var("DATABASE__POSTGRESQL__PASSWORD"))
263 {
264 self.database.postgresql.password = password;
265 }
266 if let Ok(database) =
267 std::env::var("POSTGRES_DATABASE").or_else(|_| std::env::var("DATABASE__POSTGRESQL__DATABASE"))
268 {
269 self.database.postgresql.database = database;
270 }
271
272 if let Ok(secret) = std::env::var("TOKEN_SECRET").or_else(|_| std::env::var("AUTH__TOKEN_SECRET")) {
274 self.auth.token_secret = secret;
275 }
276
277 if let Ok(host) = std::env::var("SMTP_HOST").or_else(|_| std::env::var("EMAIL__SMTP_HOST")) {
279 self.email.smtp_host = host;
280 }
281 if let Ok(port) = std::env::var("SMTP_PORT").or_else(|_| std::env::var("EMAIL__SMTP_PORT"))
282 && let Ok(p) = port.parse()
283 {
284 self.email.smtp_port = p;
285 }
286 if let Ok(user) = std::env::var("SMTP_USERNAME").or_else(|_| std::env::var("EMAIL__SMTP_USERNAME")) {
287 self.email.smtp_username = user;
288 }
289 if let Ok(password) = std::env::var("SMTP_PASSWORD").or_else(|_| std::env::var("EMAIL__SMTP_PASSWORD")) {
290 self.email.smtp_password = password;
291 }
292
293 if let Ok(provider) = std::env::var("SMS_PROVIDER").or_else(|_| std::env::var("SMS__PROVIDER")) {
295 self.sms.provider = provider;
296 }
297 if let Ok(app_id) = std::env::var("SMS_APP_ID").or_else(|_| std::env::var("SMS__APP_ID")) {
298 self.sms.app_id = app_id;
299 }
300 if let Ok(app_key) = std::env::var("SMS_APP_KEY").or_else(|_| std::env::var("SMS__APP_KEY")) {
301 self.sms.app_key = app_key;
302 }
303
304 if let Ok(secret_id) = std::env::var("COS_SECRET_ID").or_else(|_| std::env::var("COS__SECRET_ID")) {
306 self.cos.secret_id = secret_id;
307 }
308 if let Ok(secret_key) = std::env::var("COS_SECRET_KEY").or_else(|_| std::env::var("COS__SECRET_KEY")) {
309 self.cos.secret_key = secret_key;
310 }
311 if let Ok(bucket) = std::env::var("COS_BUCKET").or_else(|_| std::env::var("COS__BUCKET")) {
312 self.cos.bucket = bucket;
313 }
314 if let Ok(region) = std::env::var("COS_REGION").or_else(|_| std::env::var("COS__REGION")) {
315 self.cos.region = region;
316 }
317
318 if let Some(llm) = self.llm.first_mut() {
320 if let Ok(provider) = std::env::var("LLM_PROVIDER").or_else(|_| std::env::var("LLM__PROVIDER")) {
321 llm.provider = provider;
322 }
323 if let Ok(api_key) = std::env::var("LLM_API_KEY").or_else(|_| std::env::var("LLM__API_KEY")) {
324 llm.api_key = api_key;
325 }
326 if let Ok(model) = std::env::var("LLM_MODEL").or_else(|_| std::env::var("LLM__MODEL")) {
327 llm.model = model;
328 }
329 if let Ok(api_base) = std::env::var("LLM_API_BASE").or_else(|_| std::env::var("LLM__API_BASE")) {
330 llm.api_base = Some(api_base);
331 }
332 }
333 }
334}