otpm/config/config.go
2025-06-09 11:20:07 +08:00

162 lines
3.9 KiB
Go

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 ""
}
}