172 lines
4.5 KiB
Go
172 lines
4.5 KiB
Go
package config
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/joho/godotenv"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
// DatabaseConfig contains the configuration data for the database connection
|
|
type DatabaseConfig struct {
|
|
Host string
|
|
Port int
|
|
User string
|
|
Password string
|
|
DBName string
|
|
SSLMode string
|
|
MaxIdleConns int // Maximum number of idle connections
|
|
MaxOpenConns int // Maximum number of open connections
|
|
MaxLifetime time.Duration // Maximum lifetime of a connection
|
|
LogLevel logger.LogLevel
|
|
}
|
|
|
|
// DefaultDatabaseConfig returns a default configuration with sensible values
|
|
func DefaultDatabaseConfig() DatabaseConfig {
|
|
return DatabaseConfig{
|
|
Host: "localhost",
|
|
Port: 5432,
|
|
User: "timetracker",
|
|
Password: "password",
|
|
DBName: "timetracker",
|
|
SSLMode: "disable",
|
|
MaxIdleConns: 10,
|
|
MaxOpenConns: 100,
|
|
MaxLifetime: time.Hour,
|
|
LogLevel: logger.Info,
|
|
}
|
|
}
|
|
|
|
// JWTConfig represents the configuration for JWT authentication
|
|
type JWTConfig struct {
|
|
Secret string
|
|
TokenDuration time.Duration
|
|
KeyGenerate bool
|
|
KeyDir string
|
|
PrivKeyFile string
|
|
PubKeyFile string
|
|
KeyBits int
|
|
}
|
|
|
|
// Config represents the application configuration
|
|
type Config struct {
|
|
Database DatabaseConfig
|
|
JWTConfig JWTConfig
|
|
APIKey string
|
|
}
|
|
|
|
// LoadConfig loads configuration from environment variables and .env file
|
|
func LoadConfig() (*Config, error) {
|
|
// Try loading .env file, but don't fail if it doesn't exist
|
|
_ = godotenv.Load()
|
|
|
|
cfg := &Config{
|
|
Database: DefaultDatabaseConfig(),
|
|
JWTConfig: JWTConfig{},
|
|
}
|
|
|
|
// Load database configuration
|
|
if err := loadDatabaseConfig(cfg); err != nil {
|
|
return nil, fmt.Errorf("failed to load database config: %w", err)
|
|
}
|
|
|
|
// Load JWT configuration
|
|
if err := loadJWTConfig(cfg); err != nil {
|
|
return nil, fmt.Errorf("failed to load JWT config: %w", err)
|
|
}
|
|
|
|
// Load API key
|
|
cfg.APIKey = getEnv("API_KEY", "")
|
|
|
|
return cfg, nil
|
|
}
|
|
|
|
// loadJWTConfig loads JWT configuration from environment
|
|
func loadJWTConfig(cfg *Config) error {
|
|
cfg.JWTConfig.Secret = getEnv("JWT_SECRET", "")
|
|
defaultDuration := 24 * time.Hour
|
|
durationStr := getEnv("JWT_TOKEN_DURATION", defaultDuration.String())
|
|
|
|
duration, err := time.ParseDuration(durationStr)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid JWT_TOKEN_DURATION: %w", err)
|
|
}
|
|
cfg.JWTConfig.TokenDuration = duration
|
|
|
|
keyGenerateStr := getEnv("JWT_KEY_GENERATE", "true")
|
|
keyGenerate, err := strconv.ParseBool(keyGenerateStr)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid JWT_KEY_GENERATE: %w", err)
|
|
}
|
|
cfg.JWTConfig.KeyGenerate = keyGenerate
|
|
|
|
cfg.JWTConfig.KeyDir = getEnv("JWT_KEY_DIR", "./keys")
|
|
cfg.JWTConfig.PrivKeyFile = getEnv("JWT_PRIV_KEY_FILE", "jwt.key")
|
|
cfg.JWTConfig.PubKeyFile = getEnv("JWT_PUB_KEY_FILE", "jwt.key.pub")
|
|
|
|
keyBitsStr := getEnv("JWT_KEY_BITS", "2048")
|
|
keyBits, err := strconv.Atoi(keyBitsStr)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid JWT_KEY_BITS: %w", err)
|
|
}
|
|
cfg.JWTConfig.KeyBits = keyBits
|
|
|
|
return nil
|
|
}
|
|
|
|
// loadDatabaseConfig loads database configuration from environment
|
|
func loadDatabaseConfig(cfg *Config) error {
|
|
// Required fields
|
|
cfg.Database.Host = getEnv("DB_HOST", cfg.Database.Host)
|
|
cfg.Database.User = getEnv("DB_USER", cfg.Database.User)
|
|
cfg.Database.Password = getEnv("DB_PASSWORD", cfg.Database.Password)
|
|
cfg.Database.DBName = getEnv("DB_NAME", cfg.Database.DBName)
|
|
cfg.Database.SSLMode = getEnv("DB_SSLMODE", cfg.Database.SSLMode)
|
|
|
|
// Optional fields with parsing
|
|
if port := getEnv("DB_PORT", ""); port != "" {
|
|
portInt, err := strconv.Atoi(port)
|
|
if err != nil || portInt <= 0 {
|
|
return errors.New("invalid DB_PORT value")
|
|
}
|
|
cfg.Database.Port = portInt
|
|
}
|
|
|
|
// Log level based on environment
|
|
if os.Getenv("ENVIRONMENT") == "production" {
|
|
cfg.Database.LogLevel = logger.Error
|
|
} else {
|
|
cfg.Database.LogLevel = logger.Info
|
|
}
|
|
|
|
// Validate required fields
|
|
if cfg.Database.Host == "" || cfg.Database.User == "" ||
|
|
cfg.Database.Password == "" || cfg.Database.DBName == "" {
|
|
return errors.New("missing required database configuration")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// getEnv gets an environment variable with fallback
|
|
func getEnv(key, fallback string) string {
|
|
if value, exists := os.LookupEnv(key); exists {
|
|
return value
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
// MustLoadConfig loads configuration or panics on failure
|
|
func MustLoadConfig() *Config {
|
|
cfg, err := LoadConfig()
|
|
if err != nil {
|
|
log.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
return cfg
|
|
}
|