package config import ( "fmt" "time" "github.com/spf13/viper" ) // Config holds all configuration for the application type Config struct { Server ServerConfig `mapstructure:"server"` Database DatabaseConfig `mapstructure:"database"` JWT JWTConfig `mapstructure:"jwt"` WeChat WeChatConfig `mapstructure:"wechat"` } // ServerConfig holds all server related configuration type ServerConfig struct { Host string `mapstructure:"host"` Port int `mapstructure:"port"` ReadTimeout time.Duration `mapstructure:"read_timeout"` WriteTimeout time.Duration `mapstructure:"write_timeout"` ShutdownTimeout time.Duration `mapstructure:"shutdown_timeout"` Timeout time.Duration `mapstructure:"timeout"` // Request processing timeout } // DatabaseConfig holds all database related configuration type DatabaseConfig struct { Driver string `mapstructure:"driver"` DSN string `mapstructure:"dsn"` MaxOpenConns int `mapstructure:"max_open_conns"` MaxIdleConns int `mapstructure:"max_idle_conns"` MaxLifetime time.Duration `mapstructure:"max_lifetime"` SkipMigration bool `mapstructure:"skip_migration"` } // JWTConfig holds all JWT related configuration type JWTConfig struct { Secret string `mapstructure:"secret"` ExpireDelta time.Duration `mapstructure:"expire_delta"` RefreshDelta time.Duration `mapstructure:"refresh_delta"` SigningMethod string `mapstructure:"signing_method"` Issuer string `mapstructure:"issuer"` Audience string `mapstructure:"audience"` } // WeChatConfig holds all WeChat related configuration type WeChatConfig struct { AppID string `mapstructure:"app_id"` AppSecret string `mapstructure:"app_secret"` } // LoadConfig loads the configuration from file and environment variables func LoadConfig() (*Config, error) { // Set default values setDefaults() // Read config file if err := viper.ReadInConfig(); err != nil { return nil, fmt.Errorf("failed to read config file: %w", err) } var config Config if err := viper.Unmarshal(&config); err != nil { return nil, fmt.Errorf("failed to unmarshal config: %w", err) } // Validate config if err := validateConfig(&config); err != nil { return nil, fmt.Errorf("invalid configuration: %w", err) } return &config, nil } // setDefaults sets default values for configuration func setDefaults() { // Server defaults viper.SetDefault("server.port", 8080) viper.SetDefault("server.read_timeout", "15s") viper.SetDefault("server.write_timeout", "15s") viper.SetDefault("server.shutdown_timeout", "5s") viper.SetDefault("server.timeout", "30s") // Default request processing timeout // Database defaults viper.SetDefault("database.driver", "sqlite3") viper.SetDefault("database.max_open_conns", 25) viper.SetDefault("database.max_idle_conns", 25) viper.SetDefault("database.max_lifetime", "5m") viper.SetDefault("database.skip_migration", false) // JWT defaults viper.SetDefault("jwt.expire_delta", "24h") viper.SetDefault("jwt.refresh_delta", "168h") // 7 days viper.SetDefault("jwt.signing_method", "HS256") viper.SetDefault("jwt.issuer", "otpm") viper.SetDefault("jwt.audience", "otpm-client") } // validateConfig validates the configuration func validateConfig(config *Config) error { if config.Server.Port < 1 || config.Server.Port > 65535 { return fmt.Errorf("invalid port number: %d", config.Server.Port) } if config.Database.Driver == "" { return fmt.Errorf("database driver is required") } if config.Database.DSN == "" { return fmt.Errorf("database DSN is required") } if config.JWT.Secret == "" { return fmt.Errorf("JWT secret is required") } if config.WeChat.AppID == "" { return fmt.Errorf("WeChat AppID is required") } if config.WeChat.AppSecret == "" { return fmt.Errorf("WeChat AppSecret is required") } return nil }