package config import ( "log" "strings" "github.com/spf13/viper" ) type Config struct { Server ServerConfig Database DatabaseConfig JWT JWTConfig License LicenseConfig Storage StorageConfig } type ServerConfig struct { Port string `mapstructure:"port"` Mode string `mapstructure:"mode"` // debug | release CORSOrigin string `mapstructure:"cors_origin"` // 允许的 CORS 来源,生产设为具体域名 } type DatabaseConfig struct { DSN string `mapstructure:"dsn"` MaxIdleConns int `mapstructure:"max_idle_conns"` MaxOpenConns int `mapstructure:"max_open_conns"` } type JWTConfig struct { Secret string `mapstructure:"secret"` AccessExpireMin int `mapstructure:"access_expire_min"` // Access Token 有效分钟数 RefreshExpireH int `mapstructure:"refresh_expire_h"` // Refresh Token 有效小时数 } type LicenseConfig struct { HMACSecret string `mapstructure:"hmac_secret"` // legacy, kept for backward compat Ed25519PublicKey string `mapstructure:"ed25519_public_key"` // base64 Ed25519 public key for token verification Ed25519PrivateKey string `mapstructure:"ed25519_private_key"` // base64 Ed25519 private key for token signing (keep in Bitwarden) } type StorageConfig struct { UploadDir string `mapstructure:"upload_dir"` BaseURL string `mapstructure:"base_url"` PublicURL string `mapstructure:"public_url"` // 商品公开页基础 URL,用于生成二维码 } var C Config func Load() { viper.SetConfigName("config") viper.SetConfigType("yaml") viper.AddConfigPath(".") viper.AddConfigPath("./config") // 环境变量覆盖(生产部署时使用) viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) viper.AutomaticEnv() // 显式绑定没有默认值的 key,确保 AutomaticEnv 能找到对应 env var _ = viper.BindEnv("database.dsn", "DATABASE_DSN") _ = viper.BindEnv("jwt.secret", "JWT_SECRET") _ = viper.BindEnv("license.hmac_secret", "LICENSE_HMAC_SECRET") _ = viper.BindEnv("license.ed25519_public_key", "LICENSE_ED25519_PUBLIC_KEY") _ = viper.BindEnv("license.ed25519_private_key", "LICENSE_ED25519_PRIVATE_KEY") _ = viper.BindEnv("storage.upload_dir", "STORAGE_UPLOAD_DIR") _ = viper.BindEnv("storage.base_url", "STORAGE_BASE_URL") _ = viper.BindEnv("storage.public_url", "STORAGE_PUBLIC_URL") // 默认值 viper.SetDefault("server.port", "8080") viper.SetDefault("server.mode", "debug") viper.SetDefault("server.cors_origin", "*") viper.SetDefault("jwt.access_expire_min", 60) viper.SetDefault("jwt.refresh_expire_h", 168) // 7天 viper.SetDefault("database.max_idle_conns", 10) viper.SetDefault("database.max_open_conns", 100) viper.SetDefault("storage.upload_dir", "./uploads/images") viper.SetDefault("storage.base_url", "http://localhost:8080/images") viper.SetDefault("storage.public_url", "http://localhost:8081") if err := viper.ReadInConfig(); err != nil { log.Println("[config] no config file found, using defaults and env vars") } if err := viper.Unmarshal(&C); err != nil { log.Fatalf("[config] failed to unmarshal config: %v", err) } }