router.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. package server
  2. import (
  3. "context"
  4. "log"
  5. "sync/atomic"
  6. "time"
  7. "github.com/Wei-Shaw/sub2api/internal/config"
  8. "github.com/Wei-Shaw/sub2api/internal/handler"
  9. middleware2 "github.com/Wei-Shaw/sub2api/internal/server/middleware"
  10. "github.com/Wei-Shaw/sub2api/internal/server/routes"
  11. "github.com/Wei-Shaw/sub2api/internal/service"
  12. "github.com/Wei-Shaw/sub2api/internal/web"
  13. "github.com/gin-gonic/gin"
  14. "github.com/redis/go-redis/v9"
  15. )
  16. const frameSrcRefreshTimeout = 5 * time.Second
  17. // SetupRouter 配置路由器中间件和路由
  18. func SetupRouter(
  19. r *gin.Engine,
  20. handlers *handler.Handlers,
  21. jwtAuth middleware2.JWTAuthMiddleware,
  22. optionalJWTAuth middleware2.OptionalJWTAuthMiddleware,
  23. adminAuth middleware2.AdminAuthMiddleware,
  24. apiKeyAuth middleware2.APIKeyAuthMiddleware,
  25. auditLog middleware2.AuditLogMiddleware,
  26. stepUpAuth middleware2.StepUpAuthMiddleware,
  27. apiKeyService *service.APIKeyService,
  28. subscriptionService *service.SubscriptionService,
  29. opsService *service.OpsService,
  30. settingService *service.SettingService,
  31. userService *service.UserService,
  32. compositeResolver *service.CompositeRouteResolver,
  33. cfg *config.Config,
  34. redisClient *redis.Client,
  35. ) *gin.Engine {
  36. middleware2.SetIngressRejectRecorder(opsService)
  37. // 缓存 iframe 页面的 origin 列表,用于动态注入 CSP frame-src
  38. var cachedFrameOrigins atomic.Pointer[[]string]
  39. emptyOrigins := []string{}
  40. cachedFrameOrigins.Store(&emptyOrigins)
  41. refreshFrameOrigins := func() {
  42. ctx, cancel := context.WithTimeout(context.Background(), frameSrcRefreshTimeout)
  43. defer cancel()
  44. origins, err := settingService.GetFrameSrcOrigins(ctx)
  45. if err != nil {
  46. // 获取失败时保留已有缓存,避免 frame-src 被意外清空
  47. return
  48. }
  49. cachedFrameOrigins.Store(&origins)
  50. }
  51. refreshFrameOrigins() // 启动时初始化
  52. // 应用中间件
  53. r.Use(middleware2.RequestLogger())
  54. // 将客户端 IP + UA 注入 request context,供 token 签发/会话绑定/审计日志统一读取。
  55. // 解析模式按请求快照:兼容开关开启时信任原始转发头,关闭时使用 server.trusted_proxies。
  56. r.Use(middleware2.SessionBindingContext(cfg))
  57. r.Use(middleware2.Logger())
  58. r.Use(middleware2.CORS(cfg.CORS))
  59. r.Use(middleware2.SecurityHeaders(cfg.Security.CSP, func() []string {
  60. if p := cachedFrameOrigins.Load(); p != nil {
  61. return *p
  62. }
  63. return nil
  64. }))
  65. r.Use(middleware2.ServerTiming(cfg.Server.EnableServerTiming))
  66. // Serve embedded frontend with settings injection if available
  67. if web.HasEmbeddedFrontend() {
  68. frontendServer, err := web.NewFrontendServer(settingService) //nolint:staticcheck // SA4023: the !embed stub always errors; embed builds can return nil
  69. if err != nil { //nolint:staticcheck // SA4023: see above
  70. log.Printf("Warning: Failed to create frontend server with settings injection: %v, using legacy mode", err)
  71. r.Use(web.ServeEmbeddedFrontend())
  72. settingService.SetOnUpdateCallback(refreshFrameOrigins)
  73. } else {
  74. // Register combined callback: invalidate HTML cache + refresh frame origins
  75. settingService.SetOnUpdateCallback(func() {
  76. frontendServer.InvalidateCache()
  77. refreshFrameOrigins()
  78. })
  79. r.Use(frontendServer.Middleware())
  80. }
  81. } else {
  82. settingService.SetOnUpdateCallback(refreshFrameOrigins)
  83. }
  84. // 注册路由
  85. registerRoutes(r, handlers, jwtAuth, optionalJWTAuth, adminAuth, apiKeyAuth, auditLog, stepUpAuth, apiKeyService, subscriptionService, opsService, settingService, userService, compositeResolver, cfg, redisClient)
  86. return r
  87. }
  88. // registerRoutes 注册所有 HTTP 路由
  89. func registerRoutes(
  90. r *gin.Engine,
  91. h *handler.Handlers,
  92. jwtAuth middleware2.JWTAuthMiddleware,
  93. optionalJWTAuth middleware2.OptionalJWTAuthMiddleware,
  94. adminAuth middleware2.AdminAuthMiddleware,
  95. apiKeyAuth middleware2.APIKeyAuthMiddleware,
  96. auditLog middleware2.AuditLogMiddleware,
  97. stepUpAuth middleware2.StepUpAuthMiddleware,
  98. apiKeyService *service.APIKeyService,
  99. subscriptionService *service.SubscriptionService,
  100. opsService *service.OpsService,
  101. settingService *service.SettingService,
  102. userService *service.UserService,
  103. compositeResolver *service.CompositeRouteResolver,
  104. cfg *config.Config,
  105. redisClient *redis.Client,
  106. ) {
  107. // 通用路由(健康检查、状态等)
  108. routes.RegisterCommonRoutes(r)
  109. // API v1
  110. v1 := r.Group("/api/v1")
  111. // 面板 API 限流器:认证接口按用户 ID、公开接口按安全客户端 IP,
  112. // 防止高频刷管理面接口打爆数据库(阈值可在系统设置中调整)。
  113. panelRateLimiter := middleware2.NewPanelRateLimiter(redisClient, settingService)
  114. // 注册各模块路由
  115. routes.RegisterAuthRoutes(v1, h, jwtAuth, auditLog, redisClient, settingService, panelRateLimiter)
  116. routes.RegisterUserRoutes(v1, h, jwtAuth, auditLog, settingService, panelRateLimiter)
  117. routes.RegisterModelPlazaRoutes(v1, h, optionalJWTAuth, settingService, panelRateLimiter)
  118. routes.RegisterAdminRoutes(v1, h, adminAuth, auditLog, stepUpAuth, settingService, panelRateLimiter, userService)
  119. routes.RegisterGatewayRoutes(r, h, apiKeyAuth, apiKeyService, subscriptionService, opsService, settingService, compositeResolver, cfg)
  120. routes.RegisterPaymentRoutes(v1, h.Payment, h.PaymentWebhook, h.Admin.Payment, jwtAuth, adminAuth, auditLog, settingService, panelRateLimiter)
  121. handler.RegisterPageRoutes(v1, cfg.Pricing.DataDir, gin.HandlerFunc(jwtAuth), gin.HandlerFunc(adminAuth), settingService)
  122. }