Files
jiang13-bbs/backend/router/router.go
freefire a7b6421840 feat: 实现完整的板块级RBAC内容审核系统
- 新增站长/超级管理员/管理员/板块管理员四级角色体系
- 实现实时权限快照加载与细粒度权限校验
- 新增内容审核队列与前后端页面
- 重构用户权限管理与站点管理逻辑
- 新增评论/帖子审核状态与通知推送
- 优化前端权限控制与角色徽章展示
- 修正数据库迁移与默认值问题
2026-09-15 04:47:34 +08:00

177 lines
6.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 (
"path/filepath"
"time"
"github.com/freefire/jiang13-bbs/config"
"github.com/freefire/jiang13-bbs/handler"
"github.com/freefire/jiang13-bbs/middleware"
"github.com/freefire/jiang13-bbs/model"
"github.com/freefire/jiang13-bbs/service"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
)
// Setup 初始化路由
func Setup(cfg *config.Config) (*gin.Engine, error) {
if cfg.DevMode {
gin.SetMode(gin.DebugMode)
} else {
gin.SetMode(gin.ReleaseMode)
}
r := gin.New()
r.Use(gin.Recovery())
r.Use(gin.Logger())
// 全局安全响应头
r.Use(middleware.SecurityHeaders())
// CORS
r.Use(cors.New(cors.Config{
AllowOrigins: []string{"http://localhost:3000", "http://127.0.0.1:3000"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization", "X-CSRF-Token"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}))
// 服务
authSvc := service.NewAuthService(model.DB, cfg.JWTSecret)
boardSvc := service.NewBoardService(model.DB)
postSvc := service.NewPostService(model.DB)
commentSvc := service.NewCommentService(model.DB)
likeSvc := service.NewLikeService(model.DB)
notifSvc := service.NewNotificationService(model.DB)
overviewSvc := service.NewOverviewService(model.DB)
checkinSvc := service.NewCheckinService(model.DB)
announcementSvc := service.NewAnnouncementService(model.DB)
uploadSvc := service.NewUploadService(model.DB, filepath.Join(cfg.DataDir, "uploads"))
settingSvc := service.NewSettingService(model.DB)
adminUserSvc := service.NewAdminUserService(model.DB)
moderationSvc := service.NewModerationService(model.DB, notifSvc)
if err := uploadSvc.EnsureDir(); err != nil {
return nil, err
}
limiter := service.DefaultRateLimiter()
h := &handler.Handlers{
Cfg: cfg,
Auth: authSvc,
Board: boardSvc,
Post: postSvc,
Comment: commentSvc,
Like: likeSvc,
Notification: notifSvc,
OverviewSvc: overviewSvc,
Checkin: checkinSvc,
Announcement: announcementSvc,
Upload: uploadSvc,
Setting: settingSvc,
AdminUser: adminUserSvc,
Moderation: moderationSvc,
}
authMW := middleware.NewAuthMiddleware(authSvc)
// 上传文件静态服务(data/uploads → /uploads)
r.Static("/uploads", filepath.Join(cfg.DataDir, "uploads"))
// 健康检查 & SEO
r.GET("/health", h.Health)
r.GET("/robots.txt", h.RobotsTxt)
r.GET("/sitemap.xml", h.SitemapXML)
// 公开 API(可选登录)
pubAPI := r.Group("/api", authMW.OptionalAuth())
{
pubAPI.GET("/me", h.Me)
pubAPI.GET("/boards", h.Boards)
pubAPI.GET("/overview", h.Overview)
pubAPI.GET("/posts", h.Posts)
pubAPI.GET("/posts/:id", h.PostDetail)
pubAPI.GET("/posts/:id/comments", h.PostComments)
pubAPI.GET("/users/:id", h.UserProfile)
pubAPI.GET("/users/:id/comments", h.UserComments)
pubAPI.GET("/announcements", h.AnnouncementsList)
pubAPI.GET("/announcements/:id", h.AnnouncementDetail)
pubAPI.GET("/settings", h.PublicSettings)
pubAPI.POST("/register", middleware.RateLimitMiddleware(limiter, service.RateRegister), h.Register)
pubAPI.POST("/login", middleware.RateLimitMiddleware(limiter, service.RateLogin), h.Login)
// refresh token 端点:access 过期后用 refresh 换新 token(需 CSRF 防护)
pubAPI.POST("/auth/refresh", middleware.CSRFMiddleware(), h.Refresh)
// 登出不依赖有效登录态:封禁/登录态失效后前端仍需凭它清除 cookie(CSRF 仍校验)
pubAPI.POST("/logout", middleware.CSRFMiddleware(), h.Logout)
}
// 需登录 API(先鉴权,再 CSRF 防护)
api := r.Group("/api", authMW.RequireAuth(), middleware.CSRFMiddleware())
{
api.POST("/change-password", h.ChangePassword)
api.PUT("/profile", h.UpdateProfile)
api.POST("/posts", middleware.RateLimitMiddleware(limiter, service.RatePost), h.CreatePost)
api.PUT("/posts/:id", h.UpdatePost)
api.DELETE("/posts/:id", h.DeletePost)
api.PUT("/posts/:id/pin", h.TogglePin)
api.PUT("/posts/:id/recommend", h.ToggleRecommend)
api.POST("/posts/:id/like", h.ToggleLike)
api.POST("/posts/:id/comments", middleware.RateLimitMiddleware(limiter, service.RateComment), h.CreateComment)
api.DELETE("/posts/:id/comments/:cid", h.DeleteComment)
// 通知
api.GET("/notifications", h.Notifications)
api.GET("/notifications/unread-count", h.UnreadCount)
api.PUT("/notifications/:id/read", h.MarkRead)
api.PUT("/notifications/read-all", h.MarkAllRead)
// 每日签到
api.GET("/checkin", h.GetCheckin)
api.POST("/checkin", middleware.RateLimitMiddleware(limiter, service.RateComment), h.DoCheckin)
// 头像上传(裁剪后的 WebP)/ 媒体库 / 附件删除
api.POST("/upload/avatar", h.UploadAvatar)
api.PUT("/avatar/use", h.UseAvatar)
api.GET("/my/media", h.MyMedia)
api.DELETE("/my/attachments/:id", h.DeleteAttachment)
}
// 管理后台 API:RequireStaff 基础鉴权(板块管理员及以上,Actor 实时从 DB 现取),
// 各功能点再由 RequirePerm 按角色放行;板块范围在 service 层按 Actor.BoardIDs 隔离
staffAPI := r.Group("/api/admin", authMW.RequireStaff(), middleware.CSRFMiddleware())
{
staffAPI.GET("/dashboard", func(c *gin.Context) {
c.JSON(200, gin.H{"message": "admin dashboard"})
})
// 内容审核队列(任意管理角色;板块管理员只见授权板块)
staffAPI.GET("/moderation/pending-posts", h.AdminPendingPosts)
staffAPI.GET("/moderation/pending-comments", h.AdminPendingComments)
staffAPI.GET("/moderation/counts", h.AdminPendingCounts)
staffAPI.PUT("/moderation/posts/:id/approve", h.AdminApprovePost)
staffAPI.PUT("/moderation/posts/:id/reject", h.AdminRejectPost)
staffAPI.PUT("/moderation/comments/:id/approve", h.AdminApproveComment)
staffAPI.PUT("/moderation/comments/:id/reject", h.AdminRejectComment)
// 站点公告文章管理(管理员及以上)
announceAPI := staffAPI.Group("", authMW.RequirePerm(service.PermAnnouncements))
announceAPI.GET("/announcements", h.AdminListAnnouncements)
announceAPI.POST("/announcements", h.AdminCreateAnnouncement)
announceAPI.PUT("/announcements/:id", h.AdminUpdateAnnouncement)
announceAPI.DELETE("/announcements/:id", h.AdminDeleteAnnouncement)
// 站点外观设置(超级管理员/站长)
staffAPI.PUT("/settings", authMW.RequirePerm(service.PermSettings), h.UpdateSettings)
// 用户与权限管理(超级管理员/站长):列表、角色授权、封禁、登录历史
usersAPI := staffAPI.Group("", authMW.RequirePerm(service.PermUsers))
usersAPI.GET("/users", h.AdminListUsers)
usersAPI.PUT("/users/:id/role", h.AdminUpdateUserRole)
usersAPI.PUT("/users/:id/ban", h.AdminSetUserBan)
usersAPI.GET("/users/:id/login-logs", h.AdminUserLoginLogs)
}
r.NoRoute(func(c *gin.Context) {
c.JSON(404, gin.H{"error": "not found"})
})
return r, nil
}