package config import ( "fmt" "os" "strconv" "time" ) type Config struct { AppEnv string HTTP HTTPConfig Database DatabaseConfig } type HTTPConfig struct { Addr string ReadTimeout time.Duration WriteTimeout time.Duration IdleTimeout time.Duration } type DatabaseConfig struct { URL string MaxOpenConns int MaxIdleConns int ConnMaxLifetime time.Duration } func Load() (Config, error) { cfg := Config{ AppEnv: getString("APP_ENV", "development"), HTTP: HTTPConfig{ Addr: getString("HTTP_ADDR", ":8080"), ReadTimeout: getDuration("HTTP_READ_TIMEOUT", 5*time.Second), WriteTimeout: getDuration("HTTP_WRITE_TIMEOUT", 10*time.Second), IdleTimeout: getDuration("HTTP_IDLE_TIMEOUT", 60*time.Second), }, Database: DatabaseConfig{ URL: getString("DATABASE_URL", ""), MaxOpenConns: getInt("DB_MAX_OPEN_CONNS", 25), MaxIdleConns: getInt("DB_MAX_IDLE_CONNS", 25), ConnMaxLifetime: getDuration("DB_CONN_MAX_LIFETIME", 5*time.Minute), }, } if err := cfg.Validate(); err != nil { return Config{}, err } return cfg, nil } func (c Config) Validate() error { if c.AppEnv == "" { return fmt.Errorf("APP_ENV is required") } switch c.AppEnv { case "development", "test", "staging", "production": default: return fmt.Errorf("APP_ENV must be one of: development, test, staging, production") } if c.HTTP.Addr == "" { return fmt.Errorf("HTTP_ADDR is required") } if c.Database.URL == "" && c.AppEnv != "test" { return fmt.Errorf("DATABASE_URL is required") } if c.Database.MaxOpenConns < 1 { return fmt.Errorf("DB_MAX_OPEN_CONNS must be at least 1") } if c.Database.MaxIdleConns < 1 { return fmt.Errorf("DB_MAX_IDLE_CONNS must be at least 1") } if c.Database.MaxIdleConns > c.Database.MaxOpenConns { return fmt.Errorf("DB_MAX_IDLE_CONNS cannot exceed DB_MAX_OPEN_CONNS") } return nil } func getString(key, fallback string) string { value, ok := os.LookupEnv(key) if !ok || value == "" { return fallback } return value } func getInt(key string, fallback int) int { value, ok := os.LookupEnv(key) if !ok || value == "" { return fallback } parsed, err := strconv.Atoi(value) if err != nil { return fallback } return parsed } func getDuration(key string, fallback time.Duration) time.Duration { value, ok := os.LookupEnv(key) if !ok || value == "" { return fallback } parsed, err := time.ParseDuration(value) if err != nil { return fallback } return parsed }