veza/veza-backend-api/internal/middleware/maintenance.go

71 lines
1.6 KiB
Go

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
}