128 lines
2.5 KiB
Go
128 lines
2.5 KiB
Go
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
|
|
}
|