package config import ( "os" "path/filepath" "strconv" "strings" "time" ) type SmtpSettings struct { Login string Sender string Password string Host string Port int } type AuthRateLimitSettings struct { MaxAttempts int Window time.Duration } type Settings struct { Environment string CorsAllowedOrigins []string PublicURL string UploadDir string JWTSecret string DBFile string Database DatabaseSettings Storage StorageSettings DataDirPath string Port string Smtp SmtpSettings AuthRateLimit AuthRateLimitSettings TrustedProxies []string Version string UpdateFeedURL string Container bool ImageVariantMaxSourceBytes int64 ImageVariantMaxSourcePixels int64 ImageVariantMaxConcurrentRenders int } func (s Settings) DataDir() string { if s.DataDirPath != "" { return s.DataDirPath } return filepath.Dir(s.DBFile) } func (s Settings) IsProduction() bool { return s.Environment == "production" } func NewSettingsFromEnv() (Settings, error) { smtpPort, err := intEnv("SMTP_PORT") if err != nil { return Settings{}, err } authRateLimitMaxAttempts, err := intEnv("AUTH_RATE_LIMIT_MAX_ATTEMPTS") if err != nil { return Settings{}, err } authRateLimitWindowSeconds, err := intEnv("AUTH_RATE_LIMIT_WINDOW_SECONDS") if err != nil { return Settings{}, err } database, err := databaseSettingsFromEnv() if err != nil { return Settings{}, err } storage, err := storageSettingsFromEnv() if err != nil { return Settings{}, err } maxSourceBytes, err := imageVariantMaxSourceBytesFromEnv() if err != nil { return Settings{}, err } maxSourcePixels, err := imageVariantMaxSourcePixelsFromEnv() if err != nil { return Settings{}, err } maxConcurrentRenders, err := imageVariantMaxConcurrentRendersFromEnv() if err != nil { return Settings{}, err } return Settings{ Environment: os.Getenv("ENVIRONMENT"), CorsAllowedOrigins: parseSemicolonSeparatedList(os.Getenv("CORS_ALLOWED_ORIGINS")), PublicURL: os.Getenv("PUBLIC_URL"), UploadDir: os.Getenv("UPLOAD_DIR"), JWTSecret: os.Getenv("JWT_SECRET"), DBFile: os.Getenv("DB_FILE"), Database: database, Storage: storage, DataDirPath: os.Getenv("DATA_DIR"), Port: os.Getenv("PORT"), Smtp: SmtpSettings{ Login: os.Getenv("SMTP_LOGIN"), Sender: os.Getenv("SMTP_SENDER"), Password: os.Getenv("SMTP_PASSWORD"), Host: os.Getenv("SMTP_HOST"), Port: smtpPort, }, AuthRateLimit: AuthRateLimitSettings{ MaxAttempts: authRateLimitMaxAttempts, Window: time.Duration(authRateLimitWindowSeconds) * time.Second, }, TrustedProxies: parseSemicolonSeparatedList(os.Getenv("TRUSTED_PROXIES")), UpdateFeedURL: os.Getenv("UPDATE_FEED_URL"), Container: strings.EqualFold(os.Getenv("RENEWCMS_CONTAINER"), "true"), ImageVariantMaxSourceBytes: maxSourceBytes, ImageVariantMaxSourcePixels: maxSourcePixels, ImageVariantMaxConcurrentRenders: maxConcurrentRenders, }, nil } func intEnv(name string) (int, error) { return strconv.Atoi(os.Getenv(name)) } func parseSemicolonSeparatedList(raw string) []string { if raw == "" { return nil } return strings.Split(raw, ";") }