// Package config loads application configuration from the environment. package config import ( "fmt" "time" "github.com/caarlos0/env/v11" "github.com/joho/godotenv" ) // Config holds all configuration for the application. Values are read from // environment variables; a local .env file (if present) is loaded first. type Config struct { HTTP HTTPConfig DB DBConfig S3 S3Config RabbitMQ RabbitMQConfig } // HTTPConfig configures the HTTP server. type HTTPConfig struct { Port int `env:"PORT" envDefault:"8080"` ReadHeaderTimeout time.Duration `env:"HTTP_READ_HEADER_TIMEOUT" envDefault:"5s"` ReadTimeout time.Duration `env:"HTTP_READ_TIMEOUT" envDefault:"120s"` WriteTimeout time.Duration `env:"HTTP_WRITE_TIMEOUT" envDefault:"120s"` IdleTimeout time.Duration `env:"HTTP_IDLE_TIMEOUT" envDefault:"60s"` ShutdownTimeout time.Duration `env:"HTTP_SHUTDOWN_TIMEOUT" envDefault:"10s"` } // Addr returns the listen address for the HTTP server. func (c HTTPConfig) Addr() string { return fmt.Sprintf(":%d", c.Port) } // DBConfig configures the Postgres connection. type DBConfig struct { URL string `env:"DB_URL,required"` // Schema is the Postgres schema migrations operate on. It is used by // `migrate fresh` to know which schema to drop and recreate; in production // this may be something other than "public". Schema string `env:"DB_SCHEMA" envDefault:"public"` } // S3Config configures the S3/MinIO object store. type S3Config struct { Endpoint string `env:"S3_ENDPOINT,required"` AccessKey string `env:"S3_ACCESS_KEY,required"` SecretKey string `env:"S3_SECRET_KEY,required"` Bucket string `env:"S3_BUCKET" envDefault:"geofiles"` UseSSL bool `env:"S3_USE_SSL" envDefault:"false"` } // RabbitMQConfig configures the RabbitMQ connection and example topology. type RabbitMQConfig struct { URL string `env:"RABBITMQ_URL,required"` Exchange string `env:"RABBITMQ_EXCHANGE" envDefault:"gis.events"` Queue string `env:"RABBITMQ_QUEUE" envDefault:"gis.events.example"` } // Load reads configuration from the environment, loading an optional .env file // from the current working directory first. func Load() (*Config, error) { // A missing .env file is not an error: in production we rely on real env vars. _ = godotenv.Load() cfg := &Config{} if err := env.Parse(cfg); err != nil { return nil, fmt.Errorf("parse config: %w", err) } return cfg, nil }