package iam import ( "os" "strconv" "strings" "time" ) type Config struct { AccessSecret string RefreshSecret string RefreshPepper string AccessTTL time.Duration RefreshTTL time.Duration SessionTTL time.Duration RefreshCookieName string CookieSecure bool CookieDomain string CookiePath string CookieSameSite string RedisAddr string RedisPassword string RedisDB int } func LoadConfig() Config { cfg := Config{ AccessSecret: firstNonEmpty(strings.TrimSpace(os.Getenv("ACCESS_SECRET")), strings.TrimSpace(os.Getenv("AUTH_SECRET")), "dev-access-secret-change-me"), RefreshSecret: firstNonEmpty(strings.TrimSpace(os.Getenv("REFRESH_SECRET")), strings.TrimSpace(os.Getenv("AUTH_SECRET")), "dev-refresh-secret-change-me"), RefreshPepper: firstNonEmpty(strings.TrimSpace(os.Getenv("REFRESH_PEPPER")), "dev-refresh-pepper-change-me"), AccessTTL: parseDuration("ACCESS_TTL", 60*time.Minute), RefreshTTL: parseDuration("REFRESH_TTL", 7*24*time.Hour), RefreshCookieName: firstNonEmpty(strings.TrimSpace(os.Getenv("REFRESH_COOKIE_NAME")), "rt"), CookieSecure: parseBool("COOKIE_SECURE", isProduction()), CookieDomain: strings.TrimSpace(os.Getenv("COOKIE_DOMAIN")), CookiePath: firstNonEmpty(strings.TrimSpace(os.Getenv("COOKIE_PATH")), "/"), CookieSameSite: firstNonEmpty(strings.TrimSpace(os.Getenv("COOKIE_SAMESITE")), "Lax"), RedisAddr: strings.TrimSpace(os.Getenv("REDIS_ADDR")), RedisPassword: os.Getenv("REDIS_PASSWORD"), RedisDB: parseInt("REDIS_DB", 0), } cfg.SessionTTL = parseDuration("SESSION_TTL", cfg.RefreshTTL) return cfg } func parseDuration(key string, fallback time.Duration) time.Duration { value := strings.TrimSpace(os.Getenv(key)) if value == "" { return fallback } d, err := time.ParseDuration(value) if err != nil || d <= 0 { return fallback } return d } func parseBool(key string, fallback bool) bool { value := strings.TrimSpace(os.Getenv(key)) if value == "" { return fallback } parsed, err := strconv.ParseBool(value) if err != nil { return fallback } return parsed } func parseInt(key string, fallback int) int { value := strings.TrimSpace(os.Getenv(key)) if value == "" { return fallback } parsed, err := strconv.Atoi(value) if err != nil { return fallback } return parsed } func isProduction() bool { env := strings.ToLower(strings.TrimSpace(os.Getenv("APP_ENV"))) return env == "prod" || env == "production" } func firstNonEmpty(values ...string) string { for _, v := range values { if strings.TrimSpace(v) != "" { return v } } return "" }