// Package router 负责路由注册。 // // 所有 URL 的唯一定义处 —— 想知道服务暴露了哪些接口,只看这个文件即可。 package router import ( "fashionapi/internal/config" "fashionapi/internal/handler" "fashionapi/internal/middleware" "fashionapi/internal/pkg/jwt" "github.com/gin-gonic/gin" ) // Options 路由注册所需的全部依赖,由 main 装配后传入。 type Options struct { Config *config.Config JWT *jwt.Manager Article *handler.ArticleHandler Brand *handler.BrandHandler Auth *handler.AuthHandler Health *handler.HealthHandler SSG *handler.SSGHandler } // New 构建 Gin 引擎并注册全部对外路由。 // // 注意:SSG 内部接口(/api/ssg/*)不在这里注册,而是由 NewSSG 挂在独立的内部端口上, // 二者物理隔离,确保构建期全量数据不会从对外公开端口泄露。 func New(opt Options) *gin.Engine { r := gin.Default() r.Use(middleware.CORS(opt.Config.CORS)) // 上传图片的静态服务。 // 接口返回的 cover / image 是相对路径(如 /uploads/2026/08/xx.jpg), // 前端用 toAbs() 拼上 base 后直接访问,因此这里必须对外提供静态文件。 r.Static(opt.Config.Upload.URLPrefix, opt.Config.Upload.Dir) api := r.Group("/api/v1") { // 健康检查,供容器 / 反向代理探活 api.GET("/health", opt.Health.Check) // 对外公开接口:只读查询,不暴露任何后台管理字段 public := api.Group("/public") { public.GET("/articles", opt.Article.List) public.GET("/articles/:id", opt.Article.Detail) public.GET("/brands", opt.Brand.List) } // 账号体系:注册 / 登录 / 登出 / 当前用户(JWT 无状态) auth := api.Group("/auth") { auth.POST("/register", opt.Auth.Register) auth.POST("/login", opt.Auth.Login) auth.GET("/me", middleware.Auth(opt.JWT), opt.Auth.Me) } } return r }