package config import ( "fmt" "os" "strings" "github.com/spf13/viper" ) type Config struct { Server ServerConfig `mapstructure:"server"` Database DatabaseConfig `mapstructure:"database"` Redis RedisConfig `mapstructure:"redis"` JWT JWTConfig `mapstructure:"jwt"` Log LogConfig `mapstructure:"log"` Plugin PluginConfig `mapstructure:"plugin"` } type ServerConfig struct { Port int `mapstructure:"port"` Mode string `mapstructure:"mode"` } type DatabaseConfig struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` User string `mapstructure:"user"` Password string `mapstructure:"password"` DBName string `mapstructure:"dbname"` SSLMode string `mapstructure:"sslmode"` } type RedisConfig struct { Addr string `mapstructure:"addr"` Password string `mapstructure:"password"` DB int `mapstructure:"db"` } type JWTConfig struct { Secret string `mapstructure:"secret"` AccessExpiryMinutes int `mapstructure:"access_expiry_minutes"` RefreshExpiryDays int `mapstructure:"refresh_expiry_days"` Issuer string `mapstructure:"issuer"` } type LogConfig struct { Level string `mapstructure:"level"` Format string `mapstructure:"format"` } type PluginConfig struct { Enabled []string `mapstructure:"enabled"` } func Load() (*Config, error) { v := viper.New() v.SetConfigName("config") v.SetConfigType("yaml") v.AddConfigPath("./configs/") v.AddConfigPath(".") v.SetEnvPrefix("MENGSTACK") v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) v.AutomaticEnv() setDefaults(v) if err := v.ReadInConfig(); err != nil { return nil, fmt.Errorf("failed to read config: %w", err) } // Check env var for mode override before merging env config env := v.GetString("server.mode") if envOverride := os.Getenv("MENGSTACK_SERVER_MODE"); envOverride != "" { env = envOverride } mergeEnvConfig(v, env) var cfg Config if err := v.Unmarshal(&cfg); err != nil { return nil, fmt.Errorf("failed to unmarshal config: %w", err) } return &cfg, nil } func setDefaults(v *viper.Viper) { v.SetDefault("server.port", 8080) v.SetDefault("server.mode", "debug") v.SetDefault("database.host", "127.0.0.1") v.SetDefault("database.port", 5432) v.SetDefault("database.user", "postgres") v.SetDefault("database.dbname", "mengstack") v.SetDefault("database.sslmode", "disable") v.SetDefault("redis.addr", "127.0.0.1:6379") v.SetDefault("redis.db", 0) v.SetDefault("jwt.access_expiry_minutes", 30) v.SetDefault("jwt.refresh_expiry_days", 7) v.SetDefault("jwt.issuer", "mengstack") v.SetDefault("log.level", "info") v.SetDefault("log.format", "json") } func mergeEnvConfig(v *viper.Viper, env string) { envFile := fmt.Sprintf("config.%s", env) v.SetConfigName(envFile) if err := v.MergeInConfig(); err != nil { // environment-specific config is optional } v.SetConfigName("config") }