add branch v1

This commit is contained in:
“xHuPo” 2025-06-09 11:20:07 +08:00
parent 5d370e1077
commit 01b8951dd5
53 changed files with 1079 additions and 6481 deletions

View file

@ -7,122 +7,156 @@ import (
"github.com/spf13/viper"
)
// Config holds all configuration for the application
// Config holds all configuration for our application
type Config struct {
Server ServerConfig `mapstructure:"server"`
Database DatabaseConfig `mapstructure:"database"`
JWT JWTConfig `mapstructure:"jwt"`
WeChat WeChatConfig `mapstructure:"wechat"`
Server ServerConfig
Database DatabaseConfig
Security SecurityConfig
CORS CORSConfig
Wechat WechatConfig
}
// 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
Port int
Timeout time.Duration
}
// 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"`
Driver string
SQLite SQLiteConfig
Postgres PostgresConfig
}
// 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"`
// SQLiteConfig holds SQLite specific configuration
type SQLiteConfig struct {
Path string
}
// WeChatConfig holds all WeChat related configuration
type WeChatConfig struct {
// 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 loads the configuration from file and environment variables
func LoadConfig() (*Config, error) {
// Set default values
setDefaults()
// LoadConfig reads configuration from file or environment variables
func LoadConfig(configPath string) (*Config, error) {
v := viper.New()
// Read config file
if err := viper.ReadInConfig(); err != nil {
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 := viper.Unmarshal(&config); err != nil {
if err := v.Unmarshal(&config); err != nil {
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
}
// Validate config
// Validate required configurations
if err := validateConfig(&config); err != nil {
return nil, fmt.Errorf("invalid configuration: %w", err)
return nil, fmt.Errorf("config validation failed: %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", 1) // SQLite only needs 1 connection
viper.SetDefault("database.max_idle_conns", 1) // SQLite only needs 1 connection
viper.SetDefault("database.max_lifetime", "0") // SQLite doesn't benefit from connection recycling
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
// validateConfig ensures all required configuration values are provided
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.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")
}
if config.Database.DSN == "" {
return fmt.Errorf("database DSN 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)
}
if config.JWT.Secret == "" {
return fmt.Errorf("JWT secret is required")
}
if config.WeChat.AppID == "" {
// Validate WeChat configuration
if config.Wechat.AppID == "" {
return fmt.Errorf("WeChat AppID is required")
}
if config.WeChat.AppSecret == "" {
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 ""
}
}