package config import ( "fmt" "os" "strconv" "time" ) type Config struct { Env string HTTPAddr string DatabaseURL string NATSURL string FraudGRPCAddr string FraudGRPCTimeout time.Duration ShutdownTimeout time.Duration TokenSecret string TokenTTL time.Duration } func Load() (*Config, error) { cfg := &Config{ Env: getenv("GG_ENV", "development"), HTTPAddr: getenv("GG_HTTP_ADDR", ":8080"), DatabaseURL: getenv("GG_DATABASE_URL", "postgres://guestguard:guestguard@localhost:5432/guestguard?sslmode=disable"), NATSURL: getenv("GG_NATS_URL", "nats://localhost:4222"), FraudGRPCAddr: getenv("GG_FRAUD_GRPC_ADDR", "fraud-engine:9091"), FraudGRPCTimeout: getenvDuration("GG_FRAUD_GRPC_TIMEOUT", 250*time.Millisecond), ShutdownTimeout: getenvDuration("GG_SHUTDOWN_TIMEOUT", 15*time.Second), TokenSecret: os.Getenv("GG_TOKEN_SECRET"), TokenTTL: getenvDuration("GG_TOKEN_TTL", 30*24*time.Hour), } if cfg.Env == "production" && cfg.TokenSecret == "" { return nil, fmt.Errorf("GG_TOKEN_SECRET is required in production") } if cfg.TokenSecret == "" { cfg.TokenSecret = "dev-only-insecure-secret-change-me" } return cfg, nil } func getenv(key, fallback string) string { if v, ok := os.LookupEnv(key); ok && v != "" { return v } return fallback } func getenvDuration(key string, fallback time.Duration) time.Duration { v, ok := os.LookupEnv(key) if !ok || v == "" { return fallback } if d, err := time.ParseDuration(v); err == nil { return d } if secs, err := strconv.Atoi(v); err == nil { return time.Duration(secs) * time.Second } return fallback }