Skip to main content

fbc_starter/
config.rs

1use serde::{Deserialize, Serialize};
2use std::net::SocketAddr;
3
4/// 应用配置
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct Config {
7    /// 服务器配置
8    pub server: ServerConfig,
9    /// 日志配置
10    pub log: LogConfig,
11    /// CORS 配置
12    pub cors: CorsConfig,
13    /// 数据库配置(可选,需要启用 mysql/postgres/sqlite 任一特性)
14    #[serde(default)]
15    #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
16    pub database: Option<DatabaseConfig>,
17    /// Redis 配置(可选,需要启用 redis 特性)
18    #[serde(default)]
19    #[cfg(feature = "redis")]
20    pub redis: Option<RedisConfig>,
21    /// Nacos 配置(可选,需要启用 nacos 特性)
22    #[serde(default)]
23    #[cfg(feature = "nacos")]
24    pub nacos: Option<NacosConfig>,
25    /// Kafka 配置(可选,需要启用 kafka 特性)
26    #[serde(default)]
27    #[cfg(feature = "kafka")]
28    pub kafka: Option<KafkaConfig>,
29}
30
31/// 服务器配置
32#[derive(Debug, Clone, Serialize, Deserialize)]
33pub struct ServerConfig {
34    /// 监听地址
35    pub addr: String,
36    /// 端口
37    pub port: u16,
38    /// 工作线程数(0 表示使用默认值)
39    pub workers: Option<usize>,
40    /// 上下文路径(可选),例如 "/api",如果不配置则为空
41    #[serde(default)]
42    pub context_path: Option<String>,
43}
44
45impl ServerConfig {
46    /// 获取完整的 SocketAddr
47    pub fn socket_addr(&self) -> Result<SocketAddr, std::net::AddrParseError> {
48        format!("{}:{}", self.addr, self.port).parse()
49    }
50}
51
52/// 日志配置
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct LogConfig {
55    /// 日志级别 (trace, debug, info, warn, error)
56    pub level: String,
57    /// 是否使用 JSON 格式
58    pub json: bool,
59    /// 时区偏移(小时)。默认 8(东八区)。例如:8 表示 UTC+8, -5 表示 UTC-5
60    #[serde(default = "default_log_timezone")]
61    pub timezone: i32,
62    /// 文件日志配置(可选)
63    #[serde(default)]
64    pub file: Option<FileLogConfig>,
65}
66
67fn default_log_timezone() -> i32 {
68    8 // 默认东八区
69}
70
71/// 文件日志配置
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct FileLogConfig {
74    /// 日志目录(例如:./logs)。默认 ./logs
75    #[serde(default = "default_log_directory")]
76    pub directory: String,
77    /// 日志文件名前缀(例如:app,最终文件名会是 app.log)
78    #[serde(default = "default_log_filename")]
79    pub filename: String,
80    /// 日志输出格式:plain 或 json
81    #[serde(default = "default_log_format")]
82    pub format: String,
83    /// 单个日志文件大小限制(MB)。0 表示不限制。默认 100MB
84    #[serde(default = "default_log_size_limit_mb")]
85    pub size_limit_mb: u64,
86    /// 保留的最大日志文件数量。0 表示不限制。默认 10 个
87    #[serde(default = "default_log_count_limit")]
88    pub count_limit: u32,
89    /// 滚动策略:daily(按天) 或 size(按大小)。默认 daily
90    #[serde(default = "default_log_rotation")]
91    pub rotation: String,
92}
93
94fn default_log_directory() -> String {
95    "./logs".to_string()
96}
97
98fn default_log_filename() -> String {
99    "app".to_string()
100}
101
102fn default_log_format() -> String {
103    "plain".to_string()
104}
105
106fn default_log_size_limit_mb() -> u64 {
107    100
108}
109
110fn default_log_count_limit() -> u32 {
111    10
112}
113
114fn default_log_rotation() -> String {
115    "daily".to_string()
116}
117
118/// CORS 配置
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct CorsConfig {
121    /// 允许的源(* 表示允许所有)
122    pub allowed_origins: Vec<String>,
123    /// 允许的方法
124    pub allowed_methods: Vec<String>,
125    /// 允许的请求头
126    pub allowed_headers: Vec<String>,
127    /// 是否允许凭证
128    pub allow_credentials: bool,
129}
130
131/// 数据库配置(需要启用 mysql/postgres/sqlite 任一特性)
132#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
133#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct DatabaseConfig {
135    /// 数据库 URL(例如:postgres://user:password@localhost/dbname)
136    pub url: String,
137    /// 最大连接数
138    #[serde(default = "default_max_connections")]
139    pub max_connections: u32,
140    /// 最小连接数
141    #[serde(default = "default_min_connections")]
142    pub min_connections: u32,
143}
144
145#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
146fn default_max_connections() -> u32 {
147    100
148}
149
150#[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
151fn default_min_connections() -> u32 {
152    10
153}
154
155/// Redis 配置(需要启用 redis 特性)
156#[cfg(feature = "redis")]
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct RedisConfig {
159    /// Redis URL(例如:redis://127.0.0.1:6379 或 redis://:password@127.0.0.1:6379)
160    pub url: String,
161    /// Redis 密码(可选,如果 URL 中已包含密码则不需要)
162    #[serde(default)]
163    pub password: Option<String>,
164    /// 连接池大小
165    #[serde(default = "default_pool_size")]
166    pub pool_size: usize,
167}
168
169#[cfg(feature = "redis")]
170fn default_pool_size() -> usize {
171    10
172}
173
174/// Nacos 配置(需要启用 nacos 特性)
175#[cfg(feature = "nacos")]
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct NacosConfig {
178    /// Nacos 服务器地址列表(例如:["http://127.0.0.1:8848"])
179    #[serde(default = "default_nacos_server_addrs")]
180    pub server_addrs: Vec<String>,
181    /// 全局命名空间(可选,向后兼容)
182    /// 当 naming_namespace / config_namespace 未设置时,回退到此字段
183    pub namespace: Option<String>,
184    /// 服务注册/发现专用命名空间(优先级高于 namespace)
185    #[serde(default)]
186    pub naming_namespace: Option<String>,
187    /// 配置管理专用命名空间(优先级高于 namespace)
188    #[serde(default)]
189    pub config_namespace: Option<String>,
190    /// 用户名(可选,用于认证,默认为 "nacos")
191    #[serde(default = "default_nacos_username")]
192    pub username: Option<String>,
193    /// 密码(可选,用于认证,默认为 "nacos")
194    #[serde(default = "default_nacos_password")]
195    pub password: Option<String>,
196    /// 服务名称(用于服务注册,如果为空则使用环境变量 CARGO_PKG_NAME)
197    #[serde(default)]
198    pub service_name: String,
199    /// 全局服务组名(可选,默认为 DEFAULT_GROUP)
200    /// 当 naming_group / config_group 未设置时,回退到此字段
201    #[serde(default = "default_nacos_group")]
202    pub group_name: String,
203    /// 服务注册/发现专用组名(优先级高于 group_name)
204    #[serde(default)]
205    pub naming_group: Option<String>,
206    /// 配置管理专用组名(优先级高于 group_name)
207    #[serde(default)]
208    pub config_group: Option<String>,
209    /// 服务 IP(可选,默认使用服务器配置的地址)
210    #[serde(default)]
211    pub service_ip: Option<String>,
212    /// 服务端口(可选,默认使用服务器配置的端口)
213    #[serde(default)]
214    pub service_port: Option<u32>,
215    /// 健康检查路径(可选,默认为 "/health")
216    #[serde(default = "default_nacos_health_check_path")]
217    pub health_check_path: Option<String>,
218    /// 元数据(可选)
219    #[serde(default)]
220    pub metadata: Option<std::collections::HashMap<String, String>>,
221    /// 订阅的服务列表(可选,用于服务发现)
222    /// 环境变量支持逗号分隔:APP__NACOS__SUBSCRIBE_SERVICES=im-server,user-service
223    #[serde(
224        default,
225        deserialize_with = "crate::utils::serde_helpers::deserialize_string_or_vec"
226    )]
227    pub subscribe_services: Vec<String>,
228    /// 订阅的配置列表(可选,用于配置管理)
229    #[serde(default)]
230    pub subscribe_configs: Vec<NacosConfigItem>,
231}
232
233#[cfg(feature = "nacos")]
234impl NacosConfig {
235    /// 获取服务注册/发现使用的有效命名空间
236    /// 优先级: naming_namespace > namespace > None
237    pub fn effective_naming_namespace(&self) -> Option<&String> {
238        self.naming_namespace.as_ref().or(self.namespace.as_ref())
239    }
240
241    /// 获取配置管理使用的有效命名空间
242    /// 优先级: config_namespace > namespace > None
243    pub fn effective_config_namespace(&self) -> Option<&String> {
244        self.config_namespace.as_ref().or(self.namespace.as_ref())
245    }
246
247    /// 获取服务注册/发现使用的有效组名
248    /// 优先级: naming_group > group_name
249    pub fn effective_naming_group(&self) -> &str {
250        self.naming_group.as_deref().unwrap_or(&self.group_name)
251    }
252
253    /// 获取配置管理使用的有效组名
254    /// 优先级: config_group > group_name
255    pub fn effective_config_group(&self) -> &str {
256        self.config_group.as_deref().unwrap_or(&self.group_name)
257    }
258
259    /// 命名空间是否分离(naming 和 config 使用不同命名空间)
260    pub fn is_namespace_separated(&self) -> bool {
261        let naming_ns = self.effective_naming_namespace();
262        let config_ns = self.effective_config_namespace();
263        naming_ns != config_ns
264    }
265}
266
267/// Nacos 配置项
268#[cfg(feature = "nacos")]
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct NacosConfigItem {
271    /// 配置的 Data ID
272    pub data_id: String,
273    /// 配置的 Group(可选,默认为 DEFAULT_GROUP)
274    #[serde(default = "default_nacos_group")]
275    pub group: String,
276    /// 命名空间(可选)
277    #[serde(default = "default_nacos_namespace")]
278    pub namespace: String,
279}
280
281#[cfg(feature = "nacos")]
282fn default_nacos_group() -> String {
283    "DEFAULT_GROUP".to_string()
284}
285
286#[cfg(feature = "nacos")]
287fn default_nacos_server_addrs() -> Vec<String> {
288    vec!["127.0.0.1:8848".to_string()]
289}
290
291#[cfg(feature = "nacos")]
292fn default_nacos_health_check_path() -> Option<String> {
293    Some("/health".to_string())
294}
295
296#[cfg(feature = "nacos")]
297fn default_nacos_namespace() -> String {
298    "public".to_string()
299}
300
301#[cfg(feature = "nacos")]
302fn default_nacos_username() -> Option<String> {
303    Some("nacos".to_string())
304}
305
306#[cfg(feature = "nacos")]
307fn default_nacos_password() -> Option<String> {
308    Some("nacos".to_string())
309}
310
311/// Kafka 配置(需要启用 kafka 特性)
312#[cfg(feature = "kafka")]
313#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct KafkaConfig {
315    /// Kafka 集群地址(例如:localhost:9092 或 10.0.0.1:9092,10.0.0.2:9092)
316    pub brokers: String,
317    /// 生产者配置(可选,需要启用 producer 特性)
318    #[serde(default)]
319    pub producer: Option<KafkaProducerConfig>,
320    /// 消费者配置(可选,需要启用 consumer 特性)
321    #[serde(default)]
322    pub consumer: Option<KafkaConsumerConfig>,
323}
324
325/// Kafka 生产者配置
326#[cfg(feature = "kafka")]
327#[derive(Debug, Clone, Serialize, Deserialize)]
328pub struct KafkaProducerConfig {
329    /// 生产者重试次数
330    #[serde(default = "default_producer_retries")]
331    pub retries: i32,
332    /// 是否启用幂等性
333    #[serde(default = "default_producer_idempotence")]
334    pub enable_idempotence: bool,
335    /// ACK 模式 (all, 1, 0)
336    #[serde(default = "default_producer_acks")]
337    pub acks: String,
338}
339
340/// Kafka 消费者配置
341#[cfg(feature = "kafka")]
342#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct KafkaConsumerConfig {
344    /// 是否自动提交偏移量
345    #[serde(default = "default_consumer_auto_commit")]
346    pub enable_auto_commit: bool,
347}
348
349// Kafka 生产者默认值
350#[cfg(feature = "kafka")]
351fn default_producer_retries() -> i32 {
352    3
353}
354
355#[cfg(feature = "kafka")]
356fn default_producer_idempotence() -> bool {
357    true
358}
359
360#[cfg(feature = "kafka")]
361fn default_producer_acks() -> String {
362    "all".to_string()
363}
364
365// Kafka 消费者默认值
366#[cfg(feature = "kafka")]
367fn default_consumer_auto_commit() -> bool {
368    false
369}
370
371impl Default for Config {
372    fn default() -> Self {
373        Self {
374            server: ServerConfig {
375                addr: "127.0.0.1".to_string(),
376                port: 3000,
377                workers: None,
378                context_path: None,
379            },
380            log: LogConfig {
381                level: "info".to_string(),
382                json: false,
383                timezone: 8,
384                file: None,
385            },
386            cors: CorsConfig {
387                allowed_origins: vec!["*".to_string()],
388                allowed_methods: vec![
389                    "GET".to_string(),
390                    "POST".to_string(),
391                    "PUT".to_string(),
392                    "DELETE".to_string(),
393                    "PATCH".to_string(),
394                    "OPTIONS".to_string(),
395                ],
396                allowed_headers: vec!["*".to_string()],
397                // 注意:当 allowed_origins 或 allowed_headers 为 * 时,allow_credentials 会自动设置为 false
398                allow_credentials: false,
399            },
400            #[cfg(any(feature = "mysql", feature = "postgres", feature = "sqlite"))]
401            database: None,
402            #[cfg(feature = "redis")]
403            redis: None,
404            #[cfg(feature = "nacos")]
405            nacos: None,
406            #[cfg(feature = "kafka")]
407            kafka: None,
408        }
409    }
410}
411
412impl Config {
413    /// 获取本机 IP 地址
414    /// 返回第一个非回环的 IPv4 地址,如果获取失败则返回 None
415    fn get_local_ip() -> Option<String> {
416        match local_ip_address::local_ip() {
417            Ok(ip) => {
418                // 只返回 IPv4 地址,跳过回环地址
419                if ip.is_ipv4() && !ip.is_loopback() {
420                    Some(ip.to_string())
421                } else {
422                    None
423                }
424            }
425            Err(_) => None,
426        }
427    }
428
429    /// 从 .env 文件和环境变量加载配置
430    ///
431    /// 配置项命名规则:
432    /// - APP__SERVER__ADDR -> server.addr (如果不配置,自动获取本机 IP,获取不到则使用 127.0.0.1)
433    /// - APP__SERVER__PORT -> server.port
434    /// - APP__SERVER__CONTEXT_PATH -> server.context_path (可选,例如 "/api")
435    /// - APP__LOG__LEVEL -> log.level
436    /// - APP__LOG__JSON -> log.json
437    /// - APP__CORS__ALLOWED_ORIGINS -> cors.allowed_origins (逗号分隔)
438    /// - APP__CORS__ALLOWED_METHODS -> cors.allowed_methods (逗号分隔)
439    /// - APP__CORS__ALLOWED_HEADERS -> cors.allowed_headers (逗号分隔)
440    /// - APP__CORS__ALLOW_CREDENTIALS -> cors.allow_credentials
441    /// - APP__DATABASE__URL -> database.url (可选,需要启用 database 特性)
442    /// - APP__DATABASE__MAX_CONNECTIONS -> database.max_connections (可选,默认 100)
443    /// - APP__DATABASE__MIN_CONNECTIONS -> database.min_connections (可选,默认 10)
444    /// - APP__REDIS__URL -> redis.url (可选,需要启用 redis 特性)
445    /// - APP__REDIS__PASSWORD -> redis.password (可选,如果 URL 中已包含密码则不需要)
446    /// - APP__REDIS__POOL_SIZE -> redis.pool_size (可选,默认 10)
447    /// 查找项目根目录(通过查找 Cargo.toml 或 .env 文件)
448    ///
449    /// 查找策略(按优先级):
450    /// 1. 从可执行文件路径推断项目目录(例如 target/debug/im-server -> im-server/)
451    /// 2. 从可执行文件所在目录向上查找 .env 文件
452    /// 3. 从当前工作目录向上查找 .env 文件
453    fn find_project_root() -> Option<std::path::PathBuf> {
454        // 策略 1: 从可执行文件路径推断项目目录
455        // 例如:/path/to/hula-server/target/debug/im-server -> /path/to/hula-server/im-server/
456        if let Ok(exe_path) = std::env::current_exe() {
457            // 获取可执行文件名(例如 "im-server")
458            if let Some(exe_name) = exe_path.file_stem().and_then(|s| s.to_str()) {
459                // 从可执行文件路径向上查找,直到找到 workspace 根目录或项目根目录
460                if let Some(exe_dir) = exe_path.parent() {
461                    let mut path = exe_dir.to_path_buf();
462                    loop {
463                        // 检查当前目录的父目录是否包含与可执行文件同名的目录
464                        if let Some(parent) = path.parent() {
465                            let project_dir = parent.join(exe_name);
466                            // 如果找到同名目录且包含 .env 文件,这就是项目根目录
467                            if project_dir.join(".env").exists() {
468                                return Some(project_dir);
469                            }
470                            // 如果找到同名目录且包含 Cargo.toml(非 workspace),这也是项目根目录
471                            let cargo_toml = project_dir.join("Cargo.toml");
472                            if cargo_toml.exists() {
473                                if let Ok(content) = std::fs::read_to_string(&cargo_toml) {
474                                    if !content.contains("[workspace]") {
475                                        return Some(project_dir);
476                                    }
477                                }
478                            }
479                        }
480                        // 检查当前目录是否有 .env 文件
481                        if path.join(".env").exists() {
482                            return Some(path);
483                        }
484                        // 向上查找
485                        match path.parent() {
486                            Some(parent) => path = parent.to_path_buf(),
487                            None => break,
488                        }
489                    }
490                }
491            }
492        }
493
494        // 策略 2: 从当前工作目录向上查找 .env 文件
495        if let Ok(mut current_dir) = std::env::current_dir() {
496            loop {
497                if current_dir.join(".env").exists() {
498                    // 检查是否是 workspace 根目录
499                    let cargo_toml = current_dir.join("Cargo.toml");
500                    if cargo_toml.exists() {
501                        if let Ok(content) = std::fs::read_to_string(&cargo_toml) {
502                            if content.contains("[workspace]") {
503                                // 这是 workspace 根目录,但找到了 .env,返回当前目录
504                                return Some(current_dir);
505                            }
506                        }
507                    }
508                    return Some(current_dir);
509                }
510                match current_dir.parent() {
511                    Some(parent) => current_dir = parent.to_path_buf(),
512                    None => break,
513                }
514            }
515        }
516
517        None
518    }
519
520    pub fn from_env() -> Result<Self, config::ConfigError> {
521        // 加载 .env 文件(如果存在)
522        // 优先从项目根目录加载,确保当库被其他项目使用时能正确找到 .env 文件
523
524        // 方法 1: 使用 CARGO_MANIFEST_DIR 环境变量(编译时设置,最可靠)
525        if let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") {
526            let env_path = std::path::Path::new(&manifest_dir).join(".env");
527            if env_path.exists() {
528                if dotenvy::from_path(&env_path).is_ok() {
529                    eprintln!(
530                        "✓ 从 CARGO_MANIFEST_DIR 加载 .env 文件: {}",
531                        env_path.display()
532                    );
533                    return Self::load_config_from_env();
534                }
535            }
536        }
537
538        // 方法 2: 从项目根目录加载(通过查找逻辑)
539        if let Some(project_root) = Self::find_project_root() {
540            let env_path = project_root.join(".env");
541            if env_path.exists() {
542                if dotenvy::from_path(&env_path).is_ok() {
543                    eprintln!("✓ 从项目根目录加载 .env 文件: {}", env_path.display());
544                    return Self::load_config_from_env();
545                }
546            }
547        }
548
549        // 方法 3: 使用 dotenvy::dotenv() 从当前工作目录向上查找(备用方案)
550        match dotenvy::dotenv() {
551            Ok(path) => {
552                eprintln!("✓ 从当前工作目录向上查找加载 .env 文件: {}", path.display());
553            }
554            Err(_) => {
555                eprintln!("⚠ 未找到 .env 文件,将使用环境变量和默认配置");
556            }
557        }
558
559        Self::load_config_from_env()
560    }
561
562    /// 从环境变量加载配置(内部方法)
563    fn load_config_from_env() -> Result<Self, config::ConfigError> {
564        // 先手动处理数组类型的配置项,设置默认值
565        let mut default_origins = vec!["*".to_string()];
566        let mut default_methods = vec![
567            "GET".to_string(),
568            "POST".to_string(),
569            "PUT".to_string(),
570            "DELETE".to_string(),
571            "PATCH".to_string(),
572            "OPTIONS".to_string(),
573        ];
574        let mut default_headers = vec!["*".to_string()];
575
576        // 从环境变量读取数组配置
577        if let Ok(origins_str) = std::env::var("APP__CORS__ALLOWED_ORIGINS") {
578            default_origins = origins_str
579                .split(',')
580                .map(|s| s.trim().to_string())
581                .collect();
582        }
583
584        if let Ok(methods_str) = std::env::var("APP__CORS__ALLOWED_METHODS") {
585            default_methods = methods_str
586                .split(',')
587                .map(|s| s.trim().to_string())
588                .collect();
589        }
590
591        if let Ok(headers_str) = std::env::var("APP__CORS__ALLOWED_HEADERS") {
592            default_headers = headers_str
593                .split(',')
594                .map(|s| s.trim().to_string())
595                .collect();
596        }
597
598        // 处理 Nacos server_addrs 数组类型配置(同 CORS 数组处理逻辑)
599        let mut nacos_server_addrs_override: Option<Vec<String>> = None;
600        if let Ok(addrs_str) = std::env::var("APP__NACOS__SERVER_ADDRS") {
601            nacos_server_addrs_override = Some(
602                addrs_str
603                    .split(',')
604                    .map(|s| s.trim().to_string())
605                    .filter(|s| !s.is_empty())
606                    .collect(),
607            );
608        }
609
610        // 临时移除这些环境变量,避免 config crate 尝试解析它们(Vec<String> 类型无法直接反序列化)
611        let origins_backup = std::env::var("APP__CORS__ALLOWED_ORIGINS").ok();
612        let methods_backup = std::env::var("APP__CORS__ALLOWED_METHODS").ok();
613        let headers_backup = std::env::var("APP__CORS__ALLOWED_HEADERS").ok();
614        let nacos_addrs_backup = std::env::var("APP__NACOS__SERVER_ADDRS").ok();
615
616        if origins_backup.is_some() {
617            std::env::remove_var("APP__CORS__ALLOWED_ORIGINS");
618        }
619        if methods_backup.is_some() {
620            std::env::remove_var("APP__CORS__ALLOWED_METHODS");
621        }
622        if headers_backup.is_some() {
623            std::env::remove_var("APP__CORS__ALLOWED_HEADERS");
624        }
625        if nacos_addrs_backup.is_some() {
626            std::env::remove_var("APP__NACOS__SERVER_ADDRS");
627        }
628
629        // 如果未配置 APP__SERVER__ADDR,则自动获取本机 IP
630        // 注意:如果环境变量 APP__SERVER__ADDR 存在,config crate 会优先使用环境变量的值,set_default 的值不会被使用
631        let default_server_addr = if std::env::var("APP__SERVER__ADDR").is_ok() {
632            // 环境变量已存在,set_default 的值不会被使用,但 API 要求提供一个值
633            // 这里返回任意值都可以,因为不会被使用
634            "127.0.0.1".to_string()
635        } else {
636            // 环境变量不存在,尝试获取本机 IP 作为默认值
637            match Self::get_local_ip() {
638                Some(ip) => {
639                    eprintln!("✓ 自动获取本机 IP 地址: {}", ip);
640                    ip
641                }
642                None => {
643                    eprintln!("⚠ 无法获取本机 IP 地址,将使用 127.0.0.1");
644                    "127.0.0.1".to_string()
645                }
646            }
647        };
648
649        let builder = config::Config::builder()
650            .set_default("server.addr", default_server_addr.as_str())?
651            .set_default("server.port", 3000)?
652            .set_default("log.level", "info")?
653            .set_default("log.json", false)?
654            .set_default("log.timezone", 8)?
655            // 文件日志配置默认值
656            .set_default("log.file.directory", "./logs")?
657            .set_default("log.file.filename", "app")?
658            .set_default("log.file.format", "plain")?
659            .set_default("log.file.size_limit_mb", 100u64)?
660            .set_default("log.file.count_limit", 10u32)?
661            .set_default("log.file.rotation", "daily")?
662            .set_default("cors.allowed_origins", default_origins.clone())?
663            .set_default("cors.allowed_methods", default_methods.clone())?
664            .set_default("cors.allowed_headers", default_headers.clone())?
665            .set_default("cors.allow_credentials", false)?;
666
667        // Nacos 配置默认值
668        #[cfg(feature = "nacos")]
669        let builder = {
670            // 如果环境变量提供了 server_addrs,使用手动解析的值(因为已从环境中移除)
671            let nacos_addrs = nacos_server_addrs_override
672                .clone()
673                .unwrap_or_else(default_nacos_server_addrs);
674            builder
675                .set_default("nacos.server_addrs", nacos_addrs)?
676                .set_default("nacos.service_name", String::new())?
677                .set_default("nacos.group_name", default_nacos_group())?
678                .set_default("nacos.namespace", default_nacos_namespace())?
679                .set_default("nacos.username", default_nacos_username())?
680                .set_default("nacos.password", default_nacos_password())?
681                .set_default("nacos.health_check_path", default_nacos_health_check_path())?
682        };
683
684        // Kafka 配置默认值
685        #[cfg(feature = "kafka")]
686        let builder = builder.set_default("kafka.brokers", "localhost:9092")?;
687
688        #[cfg(feature = "producer")]
689        let builder = builder
690            .set_default("kafka.producer.retries", default_producer_retries())?
691            .set_default(
692                "kafka.producer.enable_idempotence",
693                default_producer_idempotence(),
694            )?
695            .set_default("kafka.producer.acks", default_producer_acks())?;
696
697        #[cfg(feature = "consumer")]
698        let builder = builder.set_default(
699            "kafka.consumer.enable_auto_commit",
700            default_consumer_auto_commit(),
701        )?;
702
703        let builder = builder.add_source(config::Environment::with_prefix("APP").separator("__"));
704
705        let config = builder.build()?;
706        let result: Config = config.try_deserialize()?;
707
708        // 恢复被临时移除的环境变量
709        if let Some(v) = origins_backup {
710            std::env::set_var("APP__CORS__ALLOWED_ORIGINS", v);
711        }
712        if let Some(v) = methods_backup {
713            std::env::set_var("APP__CORS__ALLOWED_METHODS", v);
714        }
715        if let Some(v) = headers_backup {
716            std::env::set_var("APP__CORS__ALLOWED_HEADERS", v);
717        }
718        if let Some(v) = nacos_addrs_backup {
719            std::env::set_var("APP__NACOS__SERVER_ADDRS", v);
720        }
721
722        Ok(result)
723    }
724}