package config import ( "log" "os" "strconv" "strings" "time" ) type Config struct { Addr string SessionTTL time.Duration MaxUploadSize int64 CookieSecure bool LogLevel string GitAllowedHosts []string } func Load() Config { cfg := Config{ Addr: envOrDefault("ADDR", ":8080"), SessionTTL: durationEnvOrDefault("SESSION_TTL", 30*time.Minute), MaxUploadSize: int64(intEnvOrDefault("MAX_UPLOAD_SIZE", 5*1024*1024)), CookieSecure: boolEnvOrDefault("COOKIE_SECURE", true), LogLevel: envOrDefault("LOG_LEVEL", "info"), GitAllowedHosts: csvEnvOrDefault("GIT_ALLOWED_HOSTS", []string{ "github.com", "gitlab.com", "bitbucket.org", }), } return cfg } func envOrDefault(key, fallback string) string { if value := os.Getenv(key); value != "" { return value } return fallback } func intEnvOrDefault(key string, fallback int) int { if value := os.Getenv(key); value != "" { parsed, err := strconv.Atoi(value) if err != nil { log.Printf("invalid integer for %s: %v", key, err) return fallback } return parsed } return fallback } func boolEnvOrDefault(key string, fallback bool) bool { if value := os.Getenv(key); value != "" { parsed, err := strconv.ParseBool(value) if err != nil { log.Printf("invalid boolean for %s: %v", key, err) return fallback } return parsed } return fallback } func durationEnvOrDefault(key string, fallback time.Duration) time.Duration { if value := os.Getenv(key); value != "" { parsed, err := time.ParseDuration(value) if err != nil { log.Printf("invalid duration for %s: %v", key, err) return fallback } return parsed } return fallback } func csvEnvOrDefault(key string, fallback []string) []string { raw := strings.TrimSpace(os.Getenv(key)) if raw == "" { return fallback } var out []string for _, part := range strings.Split(raw, ",") { host := strings.ToLower(strings.TrimSpace(part)) if host == "" { continue } out = append(out, host) } if len(out) == 0 { return fallback } return out }