// Package config loads the server configuration from environment // variables with sane defaults for local development. package config import ( "fmt" "os" "strings" ) // Config holds all runtime configuration for the API server. type Config struct { // HTTPAddr is the listen address, e.g. ":8080". HTTPAddr string // StaticDir points to the built frontend (React/Vite dist). // Empty disables static file serving. StaticDir string // Environment is "development", "staging" or "production". Environment string // AllowedOrigins is a comma-separated list of CORS origins // allowed to call the API from a browser. AllowedOrigins []string // DatabaseURL is the PostgreSQL DSN. DatabaseURL string // JWTSecret signs access tokens. Must be set in production. JWTSecret string // MediaDir is the local disk location for uploaded attachments. MediaDir string } // Load reads configuration from the environment, falling back to // development-friendly defaults. Unknown values are validated. func Load() (*Config, error) { cfg := &Config{ HTTPAddr: fromEnv("MESSENGER_HTTP_ADDR", ":8080"), StaticDir: os.Getenv("MESSENGER_STATIC_DIR"), Environment: fromEnv("MESSENGER_ENV", "development"), DatabaseURL: fromEnv("MESSENGER_DATABASE_URL", "postgres://messenger:messenger@localhost:5432/messenger?sslmode=disable"), JWTSecret: fromEnv("MESSENGER_JWT_SECRET", "dev-secret-change-me"), MediaDir: fromEnv("MESSENGER_MEDIA_DIR", "./media"), } for _, origin := range strings.Split(fromEnv("MESSENGER_ALLOWED_ORIGINS", ""), ",") { if origin = strings.TrimSpace(origin); origin != "" { cfg.AllowedOrigins = append(cfg.AllowedOrigins, origin) } } switch cfg.Environment { case "development", "staging", "production": default: return nil, fmt.Errorf("MESSENGER_ENV must be one of development|staging|production, got %q", cfg.Environment) } if cfg.Environment == "production" && cfg.JWTSecret == "dev-secret-change-me" { return nil, fmt.Errorf("MESSENGER_JWT_SECRET must be set in production") } return cfg, nil } // fromEnv returns the value of the environment variable name, or // fallback if the variable is unset or empty. func fromEnv(name, fallback string) string { if v := os.Getenv(name); v != "" { return v } return fallback }