// Package config 提供从环境变量加载 FuelLog API 配置的能力。 // // 加载顺序: // 1. 当前工作目录下的 .env(若存在),由 main 显式调用 godotenv.Load // 2. 进程环境变量(覆盖 .env) // // 所有字段都有合理的默认值,适合本地开发与容器部署。 package config import ( "fmt" "os" "strconv" "strings" "time" ) // Config 持有 API 启动所需的全部配置。 type Config struct { ServerAddr string // 监听地址,例如 ":8080" GinMode string // gin 模式:debug | release | test DBPath string // SQLite 文件路径 TimeZone string // IANA 时区,例如 "Asia/Shanghai" LogLevel string // debug | info | warn | error CORSOrigins []string // 允许的跨域来源 Now func() time.Time // 用于测试的时间源,生产为 time.Now } // Load 读取环境变量并返回填充好的 Config。 // 调用方负责在调用本函数前先执行 godotenv.Load()。 func Load() (*Config, error) { cfg := &Config{ ServerAddr: getEnv("SERVER_ADDR", ":8080"), GinMode: getEnv("GIN_MODE", "debug"), DBPath: getEnv("DB_PATH", "./data/fuellog.db"), TimeZone: getEnv("TZ", "Asia/Shanghai"), LogLevel: getEnv("LOG_LEVEL", "info"), Now: time.Now, } origins := getEnv("CORS_ORIGINS", "*") for _, o := range strings.Split(origins, ",") { if o = strings.TrimSpace(o); o != "" { cfg.CORSOrigins = append(cfg.CORSOrigins, o) } } if _, err := time.LoadLocation(cfg.TimeZone); err != nil { return nil, fmt.Errorf("invalid TZ %q: %w", cfg.TimeZone, err) } switch cfg.GinMode { case "debug", "release", "test": default: return nil, fmt.Errorf("invalid GIN_MODE %q (debug|release|test)", cfg.GinMode) } return cfg, nil } // Location 返回配置时区对应的 *time.Location。 func (c *Config) Location() *time.Location { loc, _ := time.LoadLocation(c.TimeZone) return loc } func getEnv(key, def string) string { if v, ok := os.LookupEnv(key); ok && v != "" { return v } return def } // getEnvInt 解析 int 环境变量,失败回退到默认值。 func getEnvInt(key string, def int) int { if v, ok := os.LookupEnv(key); ok && v != "" { if n, err := strconv.Atoi(v); err == nil { return n } } return def }