Files
2026-05-28 15:43:05 +08:00

91 lines
2.4 KiB
Go

package middleware
import (
"context"
"encoding/json"
"net/http"
"strconv"
"strings"
"time"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/qianfree/team-api/internal/logic/common"
)
// MaintenanceMode is a non-blocking middleware that sets maintenance-related
// response headers when maintenance mode is enabled. It never blocks requests.
func MaintenanceMode(r *ghttp.Request) {
ctx := r.Context()
if common.Config().GetBool(ctx, "maintenance_mode") {
r.Response.Header().Set("X-Maintenance-Mode", "true")
r.Response.Header().Set("X-Maintenance-Message", common.Config().GetString(ctx, "maintenance_message"))
r.Response.Header().Set("X-Maintenance-Duration", common.Config().GetString(ctx, "maintenance_duration"))
}
r.Middleware.Next()
}
// ApiMaintenance is a blocking middleware that rejects API requests with 503
// when API maintenance mode is enabled.
func ApiMaintenance(r *ghttp.Request) {
ctx := r.Context()
if !common.Config().GetBool(ctx, "api_maintenance_enabled") {
r.Middleware.Next()
return
}
durationStr := common.Config().GetString(ctx, "maintenance_duration")
retryAfter := parseMaintenanceDuration(ctx, durationStr)
message := common.Config().GetString(ctx, "maintenance_message")
if message == "" {
message = "系统正在维护中,请稍后再试"
}
r.Response.Header().Set("Retry-After", strconv.Itoa(retryAfter))
r.Response.Header().Set("Content-Type", "application/json")
r.Response.WriteHeader(http.StatusServiceUnavailable)
if strings.HasPrefix(r.URL.Path, "/v1/messages") {
body, _ := json.Marshal(map[string]any{
"type": "error",
"error": map[string]string{
"type": "server_error",
"message": message,
},
})
r.Response.Write(body)
} else {
body, _ := json.Marshal(map[string]any{
"error": map[string]string{
"type": "server_error",
"message": message,
},
})
r.Response.Write(body)
}
}
// parseMaintenanceDuration parses a duration string like "2h", "30m", "1h30m"
// and returns the total number of seconds. Returns 300 (5 minutes) as default
// if parsing fails or the string is empty.
func parseMaintenanceDuration(ctx context.Context, s string) int {
if s == "" {
return 300
}
d, err := time.ParseDuration(s)
if err != nil {
g.Log().Warningf(ctx, "failed to parse maintenance duration %q: %v, using default 300s", s, err)
return 300
}
seconds := int(d.Seconds())
if seconds <= 0 {
return 300
}
return seconds
}