- 新增附件模型与上传服务,拆分头像上传/管理逻辑 - 重构用户资料接口,移除直接修改头像字段,改用专用上传接口 - 添加站点公开设置API与管理员外观配置页 - 全局替换hover背景为accent-soft,优化交互一致性 - 完善头像组件,支持图片加载失败回退与src更新重置 - 新增用户状态事件总线,解决同页头像更新不及时问题 - 调整依赖包顺序与CI依赖版本 - 重构板块列表接口,新增帖子计数 - 迁移账号设置到用户主页tab,合并重复代码
152 lines
5.2 KiB
Go
152 lines
5.2 KiB
Go
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)
|
||
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,
|
||
}
|
||
|
||
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)
|
||
}
|
||
|
||
// 需登录 API(先鉴权,再 CSRF 防护)
|
||
api := r.Group("/api", authMW.RequireAuth(), middleware.CSRFMiddleware())
|
||
{
|
||
api.POST("/logout", h.Logout)
|
||
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(同样需要 CSRF 防护)
|
||
adminAPI := r.Group("/api/admin", authMW.RequireAuth(), middleware.CSRFMiddleware(), authMW.RequireAdmin())
|
||
{
|
||
adminAPI.GET("/dashboard", func(c *gin.Context) {
|
||
c.JSON(200, gin.H{"message": "admin dashboard"})
|
||
})
|
||
// 站点公告文章管理
|
||
adminAPI.GET("/announcements", h.AdminListAnnouncements)
|
||
adminAPI.POST("/announcements", h.AdminCreateAnnouncement)
|
||
adminAPI.PUT("/announcements/:id", h.AdminUpdateAnnouncement)
|
||
adminAPI.DELETE("/announcements/:id", h.AdminDeleteAnnouncement)
|
||
// 站点外观设置(主题色)
|
||
adminAPI.PUT("/settings", h.UpdateSettings)
|
||
}
|
||
|
||
r.NoRoute(func(c *gin.Context) {
|
||
c.JSON(404, gin.H{"error": "not found"})
|
||
})
|
||
|
||
return r, nil
|
||
}
|