//loads environment-based configuration. package config import ( "fmt" "os" "strconv" "strings" ) const ( defaultPort = 3001 defaultDBPort = 5432 defaultPollIntervalMS = 60000 minPollIntervalMS = 1000 defaultDigestIntervalHours = 24 ) type Config struct { Port int APIBasePath string DatabaseURL string DBHost string DBPort int DBUser string DBPassword string DBName string FirebaseProjectID string EthRPCURL string PollIntervalMS int NodeEnv string ResendAPIKey string EmailFrom string DigestIntervalHours int } // Load reads configuration from environment variables and returns a Config. func Load() (*Config, error) { cfg := &Config{ Port: getEnvInt("PORT", defaultPort), APIBasePath: getEnv("API_BASE_PATH", "/v1"), DatabaseURL: os.Getenv("DATABASE_URL"), DBHost: getEnv("DB_HOST", "localhost"), DBPort: getEnvInt("DB_PORT", defaultDBPort), DBUser: os.Getenv("DB_USER"), DBPassword: os.Getenv("DB_PASSWORD"), DBName: os.Getenv("DB_NAME"), FirebaseProjectID: os.Getenv("FIREBASE_PROJECT_ID"), EthRPCURL: os.Getenv("ETH_RPC_URL"), PollIntervalMS: getEnvInt("POLL_INTERVAL_MS", defaultPollIntervalMS), NodeEnv: getEnv("NODE_ENV", "development"), ResendAPIKey: os.Getenv("RESEND_API_KEY"), EmailFrom: getEnv("EMAIL_FROM", "Koin Ping "), DigestIntervalHours: getEnvInt("DIGEST_INTERVAL_HOURS", defaultDigestIntervalHours), } if cfg.PollIntervalMS < minPollIntervalMS { return nil, fmt.Errorf("POLL_INTERVAL_MS must be >= 1000, got %d", cfg.PollIntervalMS) //nolint:err113 } return cfg, nil } func (c *Config) DSN() string { if c.DatabaseURL != "" { // Append sslmode=disable if not already specified. if !strings.Contains(c.DatabaseURL, "sslmode=") { sep := "?" if strings.Contains(c.DatabaseURL, "?") { sep = "&" } return c.DatabaseURL + sep + "sslmode=disable" } return c.DatabaseURL } return fmt.Sprintf( "host=%s port=%d user=%s password=%s dbname=%s sslmode=disable", c.DBHost, c.DBPort, c.DBUser, c.DBPassword, c.DBName, ) } func getEnv(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func getEnvInt(key string, fallback int) int { if v := os.Getenv(key); v != "" { if n, err := strconv.Atoi(v); err == nil { return n } } return fallback }