Files
mce/internal/middleware/maintenance.go
Victor_Jay 6229c81f6e feat: 新增维护模式 + 修复注册关闭页面标题显示问题
- 修复注册关闭时标题“创建账号”和副标题仍显示的问题,三态互斥(维护中 > 关闭注册 > 正常)
- 新增 maintenance.enabled 站点设置,默认关闭,仅 Owner 可见可调节
- 维护中间件:全局拦截,维护期间仅站长可访问,白名单放行登录相关路径
- 登录服务:维护期间仅 Owner 角色可登录,Register 优先检查维护状态
- Nav 模板新增维护通知条(黄色横幅)
- 管理后台站点设置新增“启用维护模式”开关
- BuildPageData 自动注入 IsMaintenance 到所有页面模板
2026-05-30 20:54:23 +08:00

98 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package middleware
import (
"net/http"
"strings"
"metazone.cc/metalab/internal/common"
"metazone.cc/metalab/internal/config"
"metazone.cc/metalab/internal/model"
"github.com/gin-gonic/gin"
)
// MaintenanceMiddleware 维护模式中间件维护期间仅站长Owner可访问
type MaintenanceMiddleware struct {
cfg *config.Config
siteSettings *config.SiteSettings
}
// NewMaintenanceMiddleware 构造函数
func NewMaintenanceMiddleware(cfg *config.Config, siteSettings *config.SiteSettings) *MaintenanceMiddleware {
return &MaintenanceMiddleware{cfg: cfg, siteSettings: siteSettings}
}
// Handler 返回 Gin 中间件处理函数
func (mm *MaintenanceMiddleware) Handler() gin.HandlerFunc {
return func(c *gin.Context) {
// 未启用维护模式 → 放行
if !mm.siteSettings.IsMaintenanceEnabled() {
c.Next()
return
}
path := c.Request.URL.Path
// 白名单:登录相关页面和 API 始终可访问
if isMaintenanceWhitelist(path) {
c.Next()
return
}
// 尝试读取 token 并解析角色
tokenStr, err := c.Cookie(common.CookieName)
if err != nil {
// 未登录 → 重定向到登录页API 返回 403
blockAccess(c, path)
return
}
claims, err := parseToken(tokenStr, mm.cfg.JWT.Secret)
if err != nil {
blockAccess(c, path)
return
}
role, _ := claims["role"].(string)
if !model.HasMinRole(role, model.RoleOwner) {
blockAccess(c, path)
return
}
// 注入用户信息到 context后续 auth 中间件可能还需要)
injectUserContext(c, claims)
c.Next()
}
}
// isMaintenanceWhitelist 判断路径是否在维护白名单中
func isMaintenanceWhitelist(path string) bool {
// 登录页面和认证 API
if path == "/auth/login" || strings.HasPrefix(path, "/api/auth/") {
return true
}
// 静态资源
if strings.HasPrefix(path, "/static/") || strings.HasPrefix(path, "/shared/") {
return true
}
// favicon
if path == "/favicon.ico" || path == "/favicon.svg" {
return true
}
return false
}
// blockAccess 根据请求类型阻止访问页面重定向API 返回 403 JSON
func blockAccess(c *gin.Context, path string) {
if strings.HasPrefix(path, "/api/") || strings.HasPrefix(path, "/admin/api/") {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
"success": false,
"message": "社区正在维护中,仅站长可访问",
})
return
}
c.Redirect(http.StatusFound, "/auth/login")
c.Abort()
}