// Package config is responsible for one thing only: reading configuration // from environment variables and handing back a single, typed Config struct // that the rest of the app can use. // // Why centralize this instead of calling os.Getenv() all over the codebase? // - One place to see every setting the app needs. // - One place to define sane defaults for local development. // - Easy to swap the *source* later (e.g. read from a file, from Vault, // from AWS Secrets Manager) without touching any other package. package config import ( "os" "strings" ) // Config holds every piece of runtime configuration the application needs. // It is built once, in main(), and then passed down (by value - it's a // small, read-only struct) into whichever package needs it: the router, // the database connector, the session manager, the OAuth config, etc. type Config struct { // Port is the TCP port the HTTP server listens on. Port string // Env distinguishes "development" from "production". Currently used // to decide whether the session cookie requires HTTPS (Secure flag). Env string // --- MySQL connection settings --- DBHost string DBPort string DBUser string DBPassword string DBName string // --- Redis connection settings (used for session storage) --- RedisAddr string // --- Google OAuth2 settings --- GoogleClientID string GoogleClientSecret string GoogleRedirectURL string // AllowedOrigins is the CORS allowlist: which frontend origins are // permitted to call this API from browser JavaScript. AllowedOrigins []string } // Load reads every setting from the process environment, falling back to // sensible local-development defaults when a variable isn't set. In // production you would set all of these explicitly (e.g. via // docker-compose "environment:", a systemd unit, or your orchestrator's // secret/config mechanism) rather than relying on the defaults. func Load() Config { return Config{ Port: getEnv("PORT", "8080"), Env: getEnv("ENV", "development"), DBHost: getEnv("DB_HOST", "127.0.0.1"), DBPort: getEnv("DB_PORT", "3306"), DBUser: getEnv("DB_USER", "root"), DBPassword: getEnv("DB_PASSWORD", "devpass"), DBName: getEnv("DB_NAME", "go_simple_api"), RedisAddr: getEnv("REDIS_ADDR", "127.0.0.1:6379"), GoogleClientID: getEnv("GOOGLE_CLIENT_ID", ""), GoogleClientSecret: getEnv("GOOGLE_CLIENT_SECRET", ""), GoogleRedirectURL: getEnv("GOOGLE_REDIRECT_URL", "http://localhost:8080/auth/google/callback"), // ALLOWED_ORIGINS is a comma-separated list, e.g.: // ALLOWED_ORIGINS=http://localhost:3000,https://myapp.com // strings.Split on a single-value default still works fine and // yields a one-element slice. AllowedOrigins: strings.Split(getEnv("ALLOWED_ORIGINS", "http://localhost:3000"), ","), } } // getEnv reads a single environment variable, returning fallback if it's // unset or empty. It's unexported (lowercase) because nothing outside this // package needs to read raw env vars directly - everyone else should go // through the Config struct instead. func getEnv(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback }