diff --git a/internal/config/config.go b/internal/config/config.go index ee4eb86..c319321 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -4,19 +4,16 @@ import ( "fmt" "log/slog" "os" + "strconv" + "strings" "go.uber.org/fx" "sneak.berlin/go/webhooker/internal/globals" "sneak.berlin/go/webhooker/internal/logger" pkgconfig "sneak.berlin/go/webhooker/pkg/config" - // spooky action at a distance! - // this populates the environment - // from a ./.env file automatically - // for development configuration. - // .env contents should be things like - // `DBURL=postgres://user:pass@.../` - // (without the backticks, of course) + // Populates the environment from a ./.env file automatically for + // development configuration. Kept in one place only (here). _ "github.com/joho/godotenv/autoload" ) @@ -64,6 +61,40 @@ func (c *Config) IsProd() bool { return c.Environment == EnvironmentProd } +// envString returns the env var value if set, otherwise falls back to pkgconfig. +func envString(envKey, configKey string) string { + if v := os.Getenv(envKey); v != "" { + return v + } + return pkgconfig.GetString(configKey) +} + +// envSecretString returns the env var value if set, otherwise falls back to pkgconfig secrets. +func envSecretString(envKey, configKey string) string { + if v := os.Getenv(envKey); v != "" { + return v + } + return pkgconfig.GetSecretString(configKey) +} + +// envBool returns the env var value parsed as bool, otherwise falls back to pkgconfig. +func envBool(envKey, configKey string) bool { + if v := os.Getenv(envKey); v != "" { + return strings.EqualFold(v, "true") || v == "1" + } + return pkgconfig.GetBool(configKey) +} + +// envInt returns the env var value parsed as int, otherwise falls back to pkgconfig. +func envInt(envKey, configKey string, defaultValue ...int) int { + if v := os.Getenv(envKey); v != "" { + if i, err := strconv.Atoi(v); err == nil { + return i + } + } + return pkgconfig.GetInt(configKey, defaultValue...) +} + // nolint:revive // lc parameter is required by fx even if unused func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) { log := params.Logger.Get() @@ -80,30 +111,30 @@ func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) { EnvironmentDev, EnvironmentProd, environment) } - // Set the environment in the config package + // Set the environment in the config package (for fallback resolution) pkgconfig.SetEnvironment(environment) - // Load configuration values + // Load configuration values — env vars take precedence over config.yaml s := &Config{ - DBURL: pkgconfig.GetString("dburl"), - Debug: pkgconfig.GetBool("debug"), - MaintenanceMode: pkgconfig.GetBool("maintenanceMode"), - DevelopmentMode: pkgconfig.GetBool("developmentMode"), - DevAdminUsername: pkgconfig.GetString("devAdminUsername"), - DevAdminPassword: pkgconfig.GetString("devAdminPassword"), - Environment: pkgconfig.GetString("environment", environment), - MetricsUsername: pkgconfig.GetString("metricsUsername"), - MetricsPassword: pkgconfig.GetString("metricsPassword"), - Port: pkgconfig.GetInt("port", 8080), - SentryDSN: pkgconfig.GetSecretString("sentryDSN"), - SessionKey: pkgconfig.GetSecretString("sessionKey"), + DBURL: envString("DBURL", "dburl"), + Debug: envBool("DEBUG", "debug"), + MaintenanceMode: envBool("MAINTENANCE_MODE", "maintenanceMode"), + DevelopmentMode: envBool("DEVELOPMENT_MODE", "developmentMode"), + DevAdminUsername: envString("DEV_ADMIN_USERNAME", "devAdminUsername"), + DevAdminPassword: envString("DEV_ADMIN_PASSWORD", "devAdminPassword"), + Environment: environment, + MetricsUsername: envString("METRICS_USERNAME", "metricsUsername"), + MetricsPassword: envString("METRICS_PASSWORD", "metricsPassword"), + Port: envInt("PORT", "port", 8080), + SentryDSN: envSecretString("SENTRY_DSN", "sentryDSN"), + SessionKey: envSecretString("SESSION_KEY", "sessionKey"), log: log, params: ¶ms, } // Validate database URL if s.DBURL == "" { - return nil, fmt.Errorf("database URL (dburl) is required") + return nil, fmt.Errorf("database URL (DBURL) is required") } // In production, require session key