// Package router 负责路由注册与中间件装配。 package router import ( "log/slog" "time" "github.com/gin-gonic/gin" "ws_usernode/internal/api" "ws_usernode/internal/auth" "ws_usernode/internal/config" "ws_usernode/internal/webui" ) // New 构建根 router:API v1 + 前端静态资源(go:embed)。 // production 模式启用 gin.ReleaseMode;否则启用调试模式与开发日志。 func New(cfg *config.Config, h *api.Handler, sessions auth.SessionStore, log *slog.Logger) *gin.Engine { if cfg.App.Env == "production" { gin.SetMode(gin.ReleaseMode) } r := gin.New() r.Use(gin.Recovery(), requestLogger(log)) // 健康检查(不进 /api/v1 前缀,便于负载均衡/探针) r.GET("/healthz", h.Health.Healthz) // RESTful API v1 v1 := r.Group("/api/v1") { authGrp := v1.Group("/auth") { authGrp.GET("/captcha", h.Auth.Captcha) authGrp.POST("/otp/send", h.Auth.OTPSend) authGrp.POST("/otp/login", h.Auth.OTPLogin) authGrp.POST("/admin/login", h.Auth.AdminLogin) authGrp.POST("/admin/forgot", h.Auth.AdminForgot) authGrp.POST("/admin/reset", h.Auth.AdminReset) // 需要会话(管理员或外部用户) authed := authGrp.Group("", sessionMiddleware(sessions)) authed.POST("/logout", h.Auth.Logout) authed.GET("/me", h.Auth.Me) } // 用户管理(admin) users := v1.Group("/users", sessionMiddleware(sessions), requireUserType(auth.SessionUserAdmin)) { users.GET("", h.User.List) users.POST("", h.User.Create) users.GET("/:id", h.User.Get) users.PATCH("/:id", h.User.Update) users.POST("/:id/disable", h.User.Disable) users.POST("/:id/enable", h.User.Enable) users.POST("/:id/extend", h.User.Extend) users.DELETE("/:id", h.User.Delete) users.GET("/:id/keys", h.Key.ListForUser) } // 我的密钥(外部用户,自助管理;仅用户上传,管理员不代签) me := v1.Group("/me", sessionMiddleware(sessions), requireUserType(auth.SessionUserUser)) { me.GET("/keys", h.Key.ListMine) me.POST("/keys", h.Key.Create) me.PATCH("/keys/:id", h.Key.Rename) me.DELETE("/keys/:id", h.Key.Revoke) } // 申请审批(M3):提交公开;列表与审批需管理员会话 v1.POST("/approvals", h.Approval.Submit) approvals := v1.Group("/approvals", sessionMiddleware(sessions), requireUserType(auth.SessionUserAdmin)) { approvals.GET("", h.Approval.List) approvals.POST("/:id/review", h.Approval.Review) } // 审计(M4):查询 + 手动 CSV 导出(admin) auditGrp := v1.Group("/audit", sessionMiddleware(sessions), requireUserType(auth.SessionUserAdmin)) { auditGrp.GET("", h.Audit.List) auditGrp.GET("/export", h.Audit.Export) } // 系统设置(M4):读写(admin) settings := v1.Group("/settings", sessionMiddleware(sessions), requireUserType(auth.SessionUserAdmin)) { settings.GET("", h.Settings.List) settings.PUT("", h.Settings.Update) } } // 前端静态资源(go:embed;dev 阶段由 Vite dev server 代理,见 Makefile dev) r.NoRoute(gin.WrapH(webui.NewHandler())) return r } // requestLogger 以 slog 输出结构化请求日志。 func requestLogger(log *slog.Logger) gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() c.Next() log.Info("http", "method", c.Request.Method, "path", c.Request.URL.Path, "status", c.Writer.Status(), "ip", c.ClientIP(), "latency_ms", time.Since(start).Milliseconds(), ) } }