package config import ( "fmt" "time" "github.com/spf13/viper" ) // Config holds all configuration for our application type Config struct { Server ServerConfig Database DatabaseConfig Security SecurityConfig CORS CORSConfig Wechat WechatConfig } // ServerConfig holds all server related configuration type ServerConfig struct { Port int Timeout time.Duration } // DatabaseConfig holds all database related configuration type DatabaseConfig struct { Driver string SQLite SQLiteConfig Postgres PostgresConfig } // SQLiteConfig holds SQLite specific configuration type SQLiteConfig struct { Path string } // PostgresConfig holds PostgreSQL specific configuration type PostgresConfig struct { Host string Port int User string Password string DBName string SSLMode string } // SecurityConfig holds all security related configuration type SecurityConfig struct { EncryptionKey string JWTSigningKey string TokenExpiry time.Duration RefreshTokenExpiry time.Duration } // CORSConfig holds CORS related configuration type CORSConfig struct { AllowedOrigins []string AllowedMethods []string AllowedHeaders []string } // WechatConfig holds WeChat related configuration type WechatConfig struct { AppID string `mapstructure:"app_id"` AppSecret string `mapstructure:"app_secret"` } // LoadConfig reads configuration from file or environment variables func LoadConfig(configPath string) (*Config, error) { v := viper.New() v.SetConfigName("config") v.SetConfigType("yaml") v.AddConfigPath(configPath) v.AddConfigPath(".") // Read environment variables v.AutomaticEnv() // Allow environment variables to override config file v.SetEnvPrefix("OTP") v.BindEnv("security.encryption_key", "OTP_ENCRYPTION_KEY") v.BindEnv("security.jwt_signing_key", "OTP_JWT_SIGNING_KEY") v.BindEnv("database.postgres.password", "OTP_DB_PASSWORD") v.BindEnv("wechat.app_id", "OTP_WECHAT_APPID") v.BindEnv("wechat.app_secret", "OTP_WECHAT_SECRET") if err := v.ReadInConfig(); err != nil { return nil, fmt.Errorf("failed to read config file: %w", err) } var config Config if err := v.Unmarshal(&config); err != nil { return nil, fmt.Errorf("failed to unmarshal config: %w", err) } // Validate required configurations if err := validateConfig(&config); err != nil { return nil, fmt.Errorf("config validation failed: %w", err) } return &config, nil } // validateConfig ensures all required configuration values are provided func validateConfig(config *Config) error { if config.Security.EncryptionKey == "" { return fmt.Errorf("encryption key is required") } if config.Security.JWTSigningKey == "" { return fmt.Errorf("JWT signing key is required") } if config.Database.Driver == "" { return fmt.Errorf("database driver is required") } switch config.Database.Driver { case "sqlite3": if config.Database.SQLite.Path == "" { return fmt.Errorf("SQLite database path is required") } case "postgres": if config.Database.Postgres.Host == "" || config.Database.Postgres.User == "" || config.Database.Postgres.Password == "" || config.Database.Postgres.DBName == "" { return fmt.Errorf("incomplete PostgreSQL configuration") } default: return fmt.Errorf("unsupported database driver: %s", config.Database.Driver) } // Validate WeChat configuration if config.Wechat.AppID == "" { return fmt.Errorf("WeChat AppID is required") } if config.Wechat.AppSecret == "" { return fmt.Errorf("WeChat AppSecret is required") } return nil } // GetDSN returns the appropriate database connection string based on the driver func (c *DatabaseConfig) GetDSN() string { switch c.Driver { case "sqlite3": return c.SQLite.Path case "postgres": return fmt.Sprintf("host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", c.Postgres.Host, c.Postgres.Port, c.Postgres.User, c.Postgres.Password, c.Postgres.DBName, c.Postgres.SSLMode) default: return "" } }