Files
webhooker/internal/config/config.go
clawbot 69bbc958a7
All checks were successful
check / check (push) Successful in 1m57s
rework: migrate module path, fix healthcheck URL, add architecture notes
- Migrate Go module path from git.eeqj.de/sneak/webhooker to
  sneak.berlin/go/webhooker (go.mod, pkg/config/go.mod, all imports)
- Drop .json extension from healthcheck endpoint: /.well-known/healthcheck
  (routes.go, Dockerfile HEALTHCHECK, README)
- Add Rate Limiting section to README Design: global rate limiting must
  not apply to webhook endpoints; per-webhook configurable limits instead
- Add Database Architecture section to README Design: separate SQLite
  files for main app config vs per-processor data (input logs, processor
  logs, output queues)

Addresses review feedback from sneak on PR #6.
2026-03-01 09:57:32 -08:00

139 lines
4.0 KiB
Go

package config
import (
"fmt"
"log/slog"
"os"
"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)
_ "github.com/joho/godotenv/autoload"
)
const (
// EnvironmentDev represents development environment
EnvironmentDev = "dev"
// EnvironmentProd represents production environment
EnvironmentProd = "prod"
// DevSessionKey is an insecure default session key for development
// This is "webhooker-dev-session-key-insecure!" base64 encoded
DevSessionKey = "d2ViaG9va2VyLWRldi1zZXNzaW9uLWtleS1pbnNlY3VyZSE="
)
// nolint:revive // ConfigParams is a standard fx naming convention
type ConfigParams struct {
fx.In
Globals *globals.Globals
Logger *logger.Logger
}
type Config struct {
DBURL string
Debug bool
MaintenanceMode bool
DevelopmentMode bool
DevAdminUsername string
DevAdminPassword string
Environment string
MetricsPassword string
MetricsUsername string
Port int
SentryDSN string
SessionKey string
params *ConfigParams
log *slog.Logger
}
// IsDev returns true if running in development environment
func (c *Config) IsDev() bool {
return c.Environment == EnvironmentDev
}
// IsProd returns true if running in production environment
func (c *Config) IsProd() bool {
return c.Environment == EnvironmentProd
}
// nolint:revive // lc parameter is required by fx even if unused
func New(lc fx.Lifecycle, params ConfigParams) (*Config, error) {
log := params.Logger.Get()
// Determine environment from WEBHOOKER_ENVIRONMENT env var, default to dev
environment := os.Getenv("WEBHOOKER_ENVIRONMENT")
if environment == "" {
environment = EnvironmentDev
}
// Validate environment
if environment != EnvironmentDev && environment != EnvironmentProd {
return nil, fmt.Errorf("WEBHOOKER_ENVIRONMENT must be either '%s' or '%s', got '%s'",
EnvironmentDev, EnvironmentProd, environment)
}
// Set the environment in the config package
pkgconfig.SetEnvironment(environment)
// Load configuration values
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"),
log: log,
params: &params,
}
// Validate database URL
if s.DBURL == "" {
return nil, fmt.Errorf("database URL (dburl) is required")
}
// In production, require session key
if s.IsProd() && s.SessionKey == "" {
return nil, fmt.Errorf("SESSION_KEY is required in production environment")
}
// In development mode, warn if using default session key
if s.IsDev() && s.SessionKey == DevSessionKey {
log.Warn("Using insecure default session key for development mode")
}
if s.Debug {
params.Logger.EnableDebugLogging()
s.log = params.Logger.Get()
log.Debug("Debug mode enabled")
}
// Log configuration summary (without secrets)
log.Info("Configuration loaded",
"environment", s.Environment,
"port", s.Port,
"debug", s.Debug,
"maintenanceMode", s.MaintenanceMode,
"developmentMode", s.DevelopmentMode,
"hasSessionKey", s.SessionKey != "",
"hasSentryDSN", s.SentryDSN != "",
"hasMetricsAuth", s.MetricsUsername != "" && s.MetricsPassword != "",
)
return s, nil
}