go 49 lines · 8 steps

A maintenance-mode gate in Gin

A toggleable Gin middleware that returns 503 for most routes while a maintenance flag is on, with an allowlist for exceptions.

Explained by highlit
1package middleware
2 
3import (
4 "net/http"
5 "strings"
6 "sync/atomic"
7 
8 "github.com/gin-gonic/gin"
9)
10 
11type MaintenanceGate struct {
12 enabled atomic.Bool
13 allowlist []string
14 retryAfter string
15}
16 
17func NewMaintenanceGate(allowlist []string, retryAfter string) *MaintenanceGate {
18 return &MaintenanceGate{allowlist: allowlist, retryAfter: retryAfter}
19}
20 
21func (g *MaintenanceGate) SetEnabled(on bool) {
22 g.enabled.Store(on)
23}
24 
25func (g *MaintenanceGate) allowed(path string) bool {
26 for _, prefix := range g.allowlist {
27 if path == prefix || strings.HasPrefix(path, prefix+"/") {
28 return true
29 }
30 }
31 return false
32}
33 
34func (g *MaintenanceGate) Handler() gin.HandlerFunc {
35 return func(c *gin.Context) {
36 if !g.enabled.Load() || g.allowed(c.FullPath()) {
37 c.Next()
38 return
39 }
40 
41 if g.retryAfter != "" {
42 c.Header("Retry-After", g.retryAfter)
43 }
44 c.AbortWithStatusJSON(http.StatusServiceUnavailable, gin.H{
45 "error": "service_unavailable",
46 "message": "The service is temporarily down for maintenance.",
47 })
48 }
49}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1An atomic.Bool lets you flip a middleware's behavior at runtime without locks or restarts.
  2. 2Returning a closure from a factory method gives middleware access to shared, mutable state.
  3. 3Pairing a 503 with a Retry-After header tells clients both that you're down and when to come back.

Related explainers

Share this explainer

Here's the card — post it anywhere.

A maintenance-mode gate in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code