package middleware import ( "net/http" "os" "strings" "sync" "github.com/gin-gonic/gin" ) var ( maintenanceMode bool maintenanceModeOnce sync.Once maintenanceMu sync.RWMutex ) func init() { maintenanceModeOnce.Do(func() { v := os.Getenv("MAINTENANCE_MODE") maintenanceMode = v == "true" || v == "1" }) } // MaintenanceModeEnabled returns whether maintenance mode is active func MaintenanceModeEnabled() bool { maintenanceMu.RLock() defer maintenanceMu.RUnlock() return maintenanceMode } // SetMaintenanceMode sets maintenance mode (for admin toggle) func SetMaintenanceMode(enabled bool) { maintenanceMu.Lock() defer maintenanceMu.Unlock() maintenanceMode = enabled } // MaintenanceGin returns a Gin middleware for maintenance mode. // Exempt paths: /health, /healthz, /readyz, /api/v1/health, /api/v1/admin, /swagger, /docs func MaintenanceGin() gin.HandlerFunc { return func(c *gin.Context) { if !MaintenanceModeEnabled() { c.Next() return } path := c.Request.URL.Path if isMaintenanceExempt(path) { c.Next() return } c.AbortWithStatusJSON(http.StatusServiceUnavailable, gin.H{"error": "Platform is under maintenance"}) } } func isMaintenanceExempt(path string) bool { path = strings.TrimSuffix(path, "/") exempts := []string{"/health", "/healthz", "/readyz", "/health/deep", "/metrics", "/swagger", "/docs", "/api/versions"} for _, exempt := range exempts { if path == exempt || strings.HasPrefix(path, exempt+"/") { return true } } if strings.Contains(path, "/api/v1/health") { return true } if strings.Contains(path, "/api/v1/admin") { return true } return false }