138 lines
5.2 KiB
Go
138 lines
5.2 KiB
Go
package router
|
||
|
||
import (
|
||
"net/http"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"gorm.io/gorm"
|
||
|
||
"nl-pms-api/internal/config"
|
||
"nl-pms-api/internal/controller"
|
||
"nl-pms-api/internal/middleware"
|
||
"nl-pms-api/internal/service"
|
||
)
|
||
|
||
// New 组装路由:公开 healthz/auth,JWT 保护同步/设置/资料/团队/文件/后台。
|
||
// 若配置了 base_path(如 /pms-api),全部路由挂在该前缀下,适配不剥路径的反代。
|
||
func New(cfg *config.Config, db *gorm.DB) *gin.Engine {
|
||
if cfg.Env != "dev" {
|
||
gin.SetMode(gin.ReleaseMode)
|
||
}
|
||
r := gin.New()
|
||
r.Use(gin.Logger(), gin.Recovery(), middleware.AttachClientIP())
|
||
r.MaxMultipartMemory = 32 << 20
|
||
|
||
if len(cfg.TrustedProxies) > 0 {
|
||
_ = r.SetTrustedProxies(cfg.TrustedProxies)
|
||
} else {
|
||
// 空切片=不信任任何反代,ClientIP 用 RemoteAddr(避免默认信任全部)
|
||
_ = r.SetTrustedProxies([]string{})
|
||
}
|
||
|
||
root := r.Group(cfg.BasePath)
|
||
|
||
root.GET("/healthz", func(c *gin.Context) {
|
||
c.JSON(http.StatusOK, gin.H{"ok": true, "service": "nl-pms-api"})
|
||
})
|
||
|
||
authSvc := &service.AuthService{DB: db, Cfg: cfg}
|
||
authC := &controller.AuthController{Svc: authSvc}
|
||
syncC := &controller.SyncController{Svc: &service.SyncService{DB: db}}
|
||
settingsC := &controller.SettingsController{Svc: &service.SettingsService{DB: db}}
|
||
profileC := &controller.ProfileController{
|
||
Svc: &service.ProfileService{DB: db},
|
||
AvatarHist: &service.AvatarHistoryService{DB: db},
|
||
}
|
||
noticeC := &controller.NoticeController{Svc: &service.NoticeService{DB: db}}
|
||
fileC := &controller.FileController{Svc: &service.FileService{DB: db, Cfg: cfg}}
|
||
teamC := &controller.TeamController{Svc: &service.TeamService{DB: db}}
|
||
|
||
adminSvc := &service.AdminService{DB: db}
|
||
secSvc := &service.AdminSecurityService{DB: db}
|
||
relSvc := &service.ReleaseService{DB: db, Cfg: cfg}
|
||
actSvc := &service.ActivityService{DB: db}
|
||
adminC := &controller.AdminController{Admin: adminSvc, Sec: secSvc, Rel: relSvc, Act: actSvc}
|
||
actC := &controller.ActivityController{Svc: actSvc}
|
||
aiC := &controller.AIController{Admin: adminSvc}
|
||
appC := &controller.AppController{Rel: relSvc}
|
||
|
||
pub := root.Group("/api/v1")
|
||
pub.POST("/auth/register", authC.Register)
|
||
pub.POST("/auth/login", authC.Login)
|
||
pub.POST("/auth/refresh", authC.Refresh)
|
||
|
||
api := root.Group("/api/v1", middleware.RequireJWT(cfg.JWTSecret), middleware.RejectDisabled(db))
|
||
api.POST("/auth/change-password", authC.ChangePassword)
|
||
|
||
api.POST("/sync/push", syncC.Push)
|
||
api.GET("/sync/pull", syncC.Pull)
|
||
|
||
api.GET("/settings", settingsC.Get) // ?prefix=fest_img:
|
||
api.GET("/settings/global/:name", settingsC.GetGlobal)
|
||
api.GET("/settings/:name", settingsC.Get)
|
||
api.PUT("/settings/:name", middleware.RequireStepUpForGlobalSetting(db), settingsC.Put)
|
||
|
||
api.GET("/profile", profileC.Get)
|
||
api.PUT("/profile", profileC.Put)
|
||
api.GET("/profile/avatars", profileC.ListAvatars)
|
||
api.POST("/profile/avatars", profileC.PushAvatar)
|
||
api.DELETE("/profile/avatars/:id", profileC.DeleteAvatar)
|
||
|
||
api.GET("/notices", noticeC.List)
|
||
|
||
api.POST("/teams", teamC.Create)
|
||
api.GET("/teams", teamC.List)
|
||
api.PUT("/teams/:id", teamC.Rename)
|
||
api.PUT("/teams/:id/digest-time", teamC.SetDigestTime)
|
||
api.DELETE("/teams/:id", teamC.Dissolve)
|
||
api.POST("/teams/:id/leave", teamC.Leave)
|
||
api.GET("/teams/:id/members", teamC.Members)
|
||
api.POST("/teams/:id/invite", teamC.Invite)
|
||
api.PUT("/teams/:id/members/:userId/role", teamC.SetRole)
|
||
api.DELETE("/teams/:id/members/:userId", teamC.RemoveMember)
|
||
api.POST("/teams/:id/tasks", teamC.TaskSave)
|
||
api.PUT("/teams/:id/tasks/:taskId", teamC.TaskSave)
|
||
api.PUT("/teams/:id/tasks/:taskId/status", teamC.TaskSetStatus)
|
||
api.POST("/teams/:id/tasks/:taskId/urge", teamC.TaskUrge)
|
||
api.DELETE("/teams/:id/tasks/:taskId", teamC.TaskDelete)
|
||
api.GET("/teams/:id/tasks", teamC.TaskList)
|
||
api.GET("/teams/:id/shared", teamC.SharedItems)
|
||
api.POST("/teams/:id/shared/urge", teamC.UrgeShared)
|
||
api.POST("/teams/:id/reports", teamC.ReportSubmit)
|
||
api.GET("/teams/:id/reports/:date", teamC.ReportBoardGet)
|
||
api.POST("/teams/:id/reports/urge", teamC.ReportUrge)
|
||
api.PUT("/teams/:id/digests/:date", teamC.DigestSave)
|
||
|
||
api.POST("/files", fileC.Upload)
|
||
api.GET("/files", fileC.List)
|
||
api.DELETE("/files/:id", fileC.Delete)
|
||
|
||
api.POST("/activity/ping", actC.Ping)
|
||
api.GET("/ai/policy", aiC.Policy)
|
||
api.POST("/ai/usage", aiC.Usage)
|
||
api.GET("/app/latest", appC.Latest)
|
||
api.GET("/app/download/:version", appC.Download)
|
||
|
||
admin := api.Group("/admin", middleware.RequireAdmin())
|
||
admin.GET("/totp/status", adminC.TOTPStatus)
|
||
admin.POST("/totp/setup", adminC.TOTPSetupBegin)
|
||
admin.POST("/totp/confirm", adminC.TOTPSetupConfirm)
|
||
admin.POST("/stepup", adminC.StepUp)
|
||
admin.GET("/stats/overview", adminC.Overview)
|
||
admin.GET("/users", adminC.ListUsers)
|
||
admin.GET("/teams", adminC.ListTeams)
|
||
admin.GET("/releases", adminC.ListReleases)
|
||
|
||
adminWrite := admin.Group("", middleware.RequireAdminStepUp(db))
|
||
adminWrite.PATCH("/users/:id", adminC.PatchUser)
|
||
adminWrite.PATCH("/teams/:id", adminC.PatchTeam)
|
||
adminWrite.POST("/releases", adminC.UploadRelease)
|
||
adminWrite.POST("/releases/:id/publish", adminC.PublishRelease)
|
||
|
||
files := root.Group("/files", func(c *gin.Context) {
|
||
c.Header("Cache-Control", "public, max-age=31536000, immutable")
|
||
})
|
||
files.Static("/", cfg.StorageDir)
|
||
return r
|
||
}
|