package configs import ( "fmt" "os" "strings" "time" "github.com/spf13/viper" ) type Config struct { Server ServerConfig `mapstructure:"server"` Redis RedisConfig `mapstructure:"redis"` RateLimit RateLimitConfig `mapstructure:"rate_limit"` Session SessionConfig `mapstructure:"session"` Log LogConfig `mapstructure:"log"` CORS CORSConfig `mapstructure:"cors"` } type ServerConfig struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` ReadTimeout time.Duration `mapstructure:"read_timeout"` WriteTimeout time.Duration `mapstructure:"write_timeout"` IdleTimeout time.Duration `mapstructure:"idle_timeout"` } type RedisConfig struct { Addr string `mapstructure:"addr"` Password string `mapstructure:"password"` DB int `mapstructure:"db"` PoolSize int `mapstructure:"pool_size"` } type RateLimitConfig struct { GlobalRPS float64 `mapstructure:"global_rps"` PerIPRPS float64 `mapstructure:"per_ip_rps"` PerIPBurst int `mapstructure:"burst"` } type SessionConfig struct { TTL int `mapstructure:"ttl"` MaxTextLength int `mapstructure:"max_text_length"` } type LogConfig struct { Level string `mapstructure:"level"` Format string `mapstructure:"format"` } type CORSConfig struct { AllowedOrigins []string `mapstructure:"allowed_origins"` } func Load(configPath ...string) (*Config, error) { v := viper.New() if len(configPath) > 0 && configPath[0] != "" { v.SetConfigFile(configPath[0]) } else { v.SetConfigName("config") v.SetConfigType("yaml") v.AddConfigPath("configs/") v.AddConfigPath("./configs/") v.AddConfigPath(".") } v.SetDefault("server.host", "0.0.0.0") v.SetDefault("server.port", 8080) v.SetDefault("server.read_timeout", "15s") v.SetDefault("server.write_timeout", "15s") v.SetDefault("server.idle_timeout", "60s") v.SetDefault("redis.addr", "localhost:6379") v.SetDefault("redis.db", 0) v.SetDefault("redis.pool_size", 100) v.SetDefault("rate_limit.global_rps", 1000) v.SetDefault("rate_limit.per_ip_rps", 50) v.SetDefault("rate_limit.burst", 100) v.SetDefault("session.ttl", 900) v.SetDefault("session.max_text_length", 10000) v.SetDefault("log.level", "info") v.SetDefault("log.format", "json") // 环境变量覆盖(前缀 DUALCHECK_) // mapstructure 的 "." 和 "_" 都需要替换成 "_",并转大写 v.SetEnvPrefix("DUALCHECK") v.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")) v.AutomaticEnv() // 尝试读取配置文件(文件不存在不报错,用默认值+环境变量) if err := v.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); !ok { fmt.Fprintf(os.Stderr, "[config] warning: config file error: %v\n", err) } else { fmt.Fprintf(os.Stderr, "[config] config file not found, using defaults + env vars\n") } } var cfg Config if err := v.Unmarshal(&cfg); err != nil { return nil, fmt.Errorf("unmarshal config: %w", err) } return &cfg, nil }