Files
mce/internal/router/admin.go
Victor_Jay e7092254ab
Some checks failed
CI / Lint + Build (push) Has been cancelled
feat(theme): 管理后台新增模板选择器,支持运行时动态切换主题
新增功能:
- 主题发现模块:扫描 templates/ 目录下含 theme.json 的目录
- 站点设置新增 site.theme 键,DB 持久化 + 内存缓存
- 运行时切换主题:保存后即时重载 Gin 模板 + 静态资源哈希
- 管理后台 '外观主题' 页面:列表展示、选择、一键切换
- 动态静态资源服务:根据当前主题目录提供 CSS/JS

修复:
- account.html 残留孤儿 {{end}} 模板语法错误
- 动态静态资源 handler 误判 Gin *filepath 路径前缀为绝对路径

变更文件(12 modified + 3 new):
- cmd/server/main.go: 动态主题加载 + 运行时重载 + 动态静态服务
- internal/theme/discovery.go: 新增主题扫描
- internal/config/site_settings.go: ThemeName()
- internal/service/site_setting_service.go: ThemeReloader + UpdateTheme
- internal/controller/admin/: ISP 扩展 + GetThemes/UpdateTheme/ThemePage
- internal/controller/auth_controller.go: 准则路径动态化
- internal/router/: InjectThemeReloader + 新路由
- templates/: 外观主题页面 + JS + CSS + 子导航
2026-06-30 17:43:08 +08:00

145 lines
5.8 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 router
import (
"metazone.cc/mce/internal/config"
"metazone.cc/mce/internal/middleware"
"metazone.cc/mce/internal/model"
"github.com/gin-gonic/gin"
)
// setupAdminRoutes 注册管理后台页面和 API 路由
func setupAdminRoutes(r *gin.Engine, cfg *config.Config, d *dependencies) {
// --- 管理后台 SSR 页面 ---
adminPages := r.Group("/admin")
adminPages.Use(d.authMdw.AdminAuth())
adminPages.Use(middleware.RequirePageRole(model.RoleModerator))
adminPages.Use(middleware.CSRF(cfg))
adminPages.Use(func(c *gin.Context) {
middleware.SetCSRFToken(c, cfg)
c.Next()
})
{
adminPages.GET("/", d.adminCtrl.Dashboard)
adminPages.GET("/users", d.adminCtrl.UsersPage,
middleware.RequirePageRole(model.RoleAdmin))
adminPages.GET("/audits", d.auditCtrl.AuditPage,
middleware.RequirePageRole(model.RoleAdmin))
adminPages.GET("/posts", d.adminPostCtrl.PostsPage)
adminPages.GET("/site-settings", d.siteSettingCtrl.SiteSettingsPage,
middleware.RequirePageRole(model.RoleOwner))
adminPages.GET("/settings/brand", d.siteSettingCtrl.BrandPage,
middleware.RequirePageRole(model.RoleOwner))
adminPages.GET("/settings/security", d.siteSettingCtrl.SecurityPage,
middleware.RequirePageRole(model.RoleOwner))
adminPages.GET("/settings/registration", d.siteSettingCtrl.RegistrationPage,
middleware.RequirePageRole(model.RoleOwner))
adminPages.GET("/settings/content", d.siteSettingCtrl.ContentPage,
middleware.RequirePageRole(model.RoleOwner))
adminPages.GET("/settings/theme", d.siteSettingCtrl.ThemePage,
middleware.RequirePageRole(model.RoleOwner))
adminPages.GET("/energy", d.adminEnergyCtrl.EnergyPage)
adminPages.GET("/energy/logs", d.adminEnergyCtrl.EnergyLogPage)
adminPages.GET("/energy/fund-logs", d.adminEnergyCtrl.FundLogPage)
adminPages.GET("/comments", d.adminCommentCtrl.CommentsPage)
adminPages.GET("/announcements", d.announcementCtrl.Page)
adminPages.GET("/categories", d.categoryCtrl.Page)
}
// --- 管理后台 API ---
adminAPI := r.Group("/api/admin")
adminAPI.Use(d.authMdw.Required())
adminAPI.Use(middleware.RequireMinRole(model.RoleAdmin))
adminAPI.Use(d.authMdw.BannedWriteGuard())
adminAPI.Use(middleware.CSRF(cfg))
{
adminAPI.GET("/users", d.adminCtrl.ListUsers)
adminAPI.PUT("/users/:uid/status", d.adminCtrl.UpdateUserStatus)
adminAPI.POST("/users/:uid/reset-token", d.adminCtrl.ResetToken)
adminAPI.GET("/audits", d.auditCtrl.ListAudits)
adminAPI.POST("/audits/:id/approve", d.auditCtrl.Approve)
adminAPI.POST("/audits/:id/reject", d.auditCtrl.Reject)
}
// --- 站点设置 API仅 owner ---
siteSettingsAPI := r.Group("/api/admin/site-settings")
siteSettingsAPI.Use(d.authMdw.Required())
siteSettingsAPI.Use(middleware.RequireMinRole(model.RoleOwner))
siteSettingsAPI.Use(d.authMdw.BannedWriteGuard())
siteSettingsAPI.Use(middleware.CSRF(cfg))
{
siteSettingsAPI.GET("", d.siteSettingCtrl.GetSettings)
siteSettingsAPI.PUT("", d.siteSettingCtrl.UpdateSetting)
siteSettingsAPI.PUT("/bool", d.siteSettingCtrl.UpdateBoolSetting)
siteSettingsAPI.GET("/bool", d.siteSettingCtrl.GetBoolSetting)
siteSettingsAPI.GET("/themes", d.siteSettingCtrl.GetThemes)
siteSettingsAPI.PUT("/theme", d.siteSettingCtrl.UpdateTheme)
}
// --- 帖子管理 APIadmin+ ---
adminPostAPI := r.Group("/api/admin/posts")
adminPostAPI.Use(d.authMdw.Required())
adminPostAPI.Use(middleware.RequireMinRole(model.RoleAdmin))
adminPostAPI.Use(d.authMdw.BannedWriteGuard())
adminPostAPI.Use(middleware.CSRF(cfg))
{
adminPostAPI.POST("/:id/approve", d.adminPostCtrl.Approve)
adminPostAPI.POST("/:id/reject", d.adminPostCtrl.Reject)
adminPostAPI.POST("/:id/lock", d.adminPostCtrl.Lock)
adminPostAPI.POST("/:id/unlock", d.adminPostCtrl.Unlock)
adminPostAPI.POST("/:id/pin", d.adminPostCtrl.Pin)
adminPostAPI.POST("/:id/unpin", d.adminPostCtrl.Unpin)
adminPostAPI.POST("/:id/restore", d.adminPostCtrl.Restore)
}
// --- 域能管理 APIadmin+system_operation 仅 owner ---
adminEnergyAPI := r.Group("/api/admin/energy")
adminEnergyAPI.Use(d.authMdw.Required())
adminEnergyAPI.Use(middleware.RequireMinRole(model.RoleAdmin))
adminEnergyAPI.Use(d.authMdw.BannedWriteGuard())
adminEnergyAPI.Use(middleware.CSRF(cfg))
{
adminEnergyAPI.POST("/adjust", d.adminEnergyCtrl.AdjustEnergy)
adminEnergyAPI.GET("/logs", d.adminEnergyCtrl.ListEnergyLogs)
adminEnergyAPI.GET("/fund-balance", d.adminEnergyCtrl.GetFundBalance)
adminEnergyAPI.GET("/fund-logs", d.adminEnergyCtrl.ListFundLogs)
}
// --- 评论管理 APIadmin+ ---
adminCommentAPI := r.Group("/api/admin/comments")
adminCommentAPI.Use(d.authMdw.Required())
adminCommentAPI.Use(middleware.RequireMinRole(model.RoleAdmin))
adminCommentAPI.Use(d.authMdw.BannedWriteGuard())
adminCommentAPI.Use(middleware.CSRF(cfg))
{
adminCommentAPI.GET("", d.adminCommentCtrl.ListComments)
adminCommentAPI.DELETE("/:id", d.adminCommentCtrl.Delete)
}
// --- 公告管理 APImoderator+ ---
announcementAPI := r.Group("/api/admin/announcements")
announcementAPI.Use(d.authMdw.Required())
announcementAPI.Use(middleware.RequireMinRole(model.RoleModerator))
announcementAPI.Use(d.authMdw.BannedWriteGuard())
announcementAPI.Use(middleware.CSRF(cfg))
{
announcementAPI.GET("", d.announcementCtrl.List)
announcementAPI.POST("", d.announcementCtrl.Create)
announcementAPI.PUT("/:id", d.announcementCtrl.Update)
announcementAPI.DELETE("/:id", d.announcementCtrl.Delete)
}
// --- 分类管理 APImoderator+ ---
categoryAPI := r.Group("/api/admin/categories")
categoryAPI.Use(d.authMdw.Required())
categoryAPI.Use(middleware.RequireMinRole(model.RoleModerator))
categoryAPI.Use(d.authMdw.BannedWriteGuard())
categoryAPI.Use(middleware.CSRF(cfg))
{
categoryAPI.GET("", d.categoryCtrl.List)
categoryAPI.POST("", d.categoryCtrl.Create)
categoryAPI.PUT("/:id", d.categoryCtrl.Update)
categoryAPI.DELETE("/:id", d.categoryCtrl.Delete)
}
}