package config import ( "fmt" "time" "github.com/kelseyhightower/envconfig" ) // Config holds all application configuration type Config struct { // Database configuration DatabaseURL string `envconfig:"DATABASE_URL" required:"true"` // Server configuration Port int `envconfig:"PORT" default:"8080"` // Location configuration LocationLat float64 `envconfig:"LOCATION_LAT" default:"37.7749"` LocationLon float64 `envconfig:"LOCATION_LON" default:"-122.4194"` LocationName string `envconfig:"LOCATION_NAME" default:"San Francisco"` // Timezone configuration Timezone string `envconfig:"TIMEZONE" default:"America/Los_Angeles"` // Weather fetcher configuration FetchInterval time.Duration `envconfig:"FETCH_INTERVAL" default:"15m"` // Cache configuration CacheTTL time.Duration `envconfig:"CACHE_TTL" default:"10m"` } // Load reads configuration from environment variables func Load() (*Config, error) { var cfg Config if err := envconfig.Process("", &cfg); err != nil { return nil, fmt.Errorf("failed to process environment config: %w", err) } // Validate configuration if err := cfg.validate(); err != nil { return nil, fmt.Errorf("invalid configuration: %w", err) } return &cfg, nil } // validate checks that configuration values are valid func (c *Config) validate() error { if c.Port < 1 || c.Port > 65535 { return fmt.Errorf("port must be between 1 and 65535, got %d", c.Port) } if c.LocationLat < -90 || c.LocationLat > 90 { return fmt.Errorf("location latitude must be between -90 and 90, got %f", c.LocationLat) } if c.LocationLon < -180 || c.LocationLon > 180 { return fmt.Errorf("location longitude must be between -180 and 180, got %f", c.LocationLon) } if c.FetchInterval < time.Minute { return fmt.Errorf("fetch interval must be at least 1 minute, got %s", c.FetchInterval) } if c.CacheTTL < time.Second { return fmt.Errorf("cache TTL must be at least 1 second, got %s", c.CacheTTL) } return nil } // Addr returns the server address in host:port format func (c *Config) Addr() string { return fmt.Sprintf(":%d", c.Port) }