// Package routes provides HTTP route registration and handlers. package routes import ( "github.com/Wei-Shaw/sub2api/internal/handler" "github.com/Wei-Shaw/sub2api/internal/server/middleware" "github.com/gin-gonic/gin" ) // RegisterAdminRoutes 注册管理员路由 func RegisterAdminRoutes( v1 *gin.RouterGroup, h *handler.Handlers, adminAuth middleware.AdminAuthMiddleware, ) { admin := v1.Group("/admin") admin.Use(gin.HandlerFunc(adminAuth)) { // 仪表盘 registerDashboardRoutes(admin, h) // 用户管理 registerUserManagementRoutes(admin, h) // 分组管理 registerGroupRoutes(admin, h) // 账号管理 registerAccountRoutes(admin, h) // 公告管理 registerAnnouncementRoutes(admin, h) // OpenAI OAuth registerOpenAIOAuthRoutes(admin, h) // Gemini OAuth registerGeminiOAuthRoutes(admin, h) // Antigravity OAuth registerAntigravityOAuthRoutes(admin, h) // 代理管理 registerProxyRoutes(admin, h) // 卡密管理 registerRedeemCodeRoutes(admin, h) // 优惠码管理 registerPromoCodeRoutes(admin, h) // 系统设置 registerSettingsRoutes(admin, h) // 运维监控(Ops) registerOpsRoutes(admin, h) // 系统管理 registerSystemRoutes(admin, h) // 订阅管理 registerSubscriptionRoutes(admin, h) // 使用记录管理 registerUsageRoutes(admin, h) // 用户属性管理 registerUserAttributeRoutes(admin, h) } } func registerOpsRoutes(admin *gin.RouterGroup, h *handler.Handlers) { ops := admin.Group("/ops") { // Realtime ops signals ops.GET("/concurrency", h.Admin.Ops.GetConcurrencyStats) ops.GET("/account-availability", h.Admin.Ops.GetAccountAvailability) ops.GET("/realtime-traffic", h.Admin.Ops.GetRealtimeTrafficSummary) // Alerts (rules + events) ops.GET("/alert-rules", h.Admin.Ops.ListAlertRules) ops.POST("/alert-rules", h.Admin.Ops.CreateAlertRule) ops.PUT("/alert-rules/:id", h.Admin.Ops.UpdateAlertRule) ops.DELETE("/alert-rules/:id", h.Admin.Ops.DeleteAlertRule) ops.GET("/alert-events", h.Admin.Ops.ListAlertEvents) ops.GET("/alert-events/:id", h.Admin.Ops.GetAlertEvent) ops.PUT("/alert-events/:id/status", h.Admin.Ops.UpdateAlertEventStatus) ops.POST("/alert-silences", h.Admin.Ops.CreateAlertSilence) // Email notification config (DB-backed) ops.GET("/email-notification/config", h.Admin.Ops.GetEmailNotificationConfig) ops.PUT("/email-notification/config", h.Admin.Ops.UpdateEmailNotificationConfig) // Runtime settings (DB-backed) runtime := ops.Group("/runtime") { runtime.GET("/alert", h.Admin.Ops.GetAlertRuntimeSettings) runtime.PUT("/alert", h.Admin.Ops.UpdateAlertRuntimeSettings) } // Advanced settings (DB-backed) ops.GET("/advanced-settings", h.Admin.Ops.GetAdvancedSettings) ops.PUT("/advanced-settings", h.Admin.Ops.UpdateAdvancedSettings) // Settings group (DB-backed) settings := ops.Group("/settings") { settings.GET("/metric-thresholds", h.Admin.Ops.GetMetricThresholds) settings.PUT("/metric-thresholds", h.Admin.Ops.UpdateMetricThresholds) } // WebSocket realtime (QPS/TPS) ws := ops.Group("/ws") { ws.GET("/qps", h.Admin.Ops.QPSWSHandler) } // Error logs (legacy) ops.GET("/errors", h.Admin.Ops.GetErrorLogs) ops.GET("/errors/:id", h.Admin.Ops.GetErrorLogByID) ops.GET("/errors/:id/retries", h.Admin.Ops.ListRetryAttempts) ops.POST("/errors/:id/retry", h.Admin.Ops.RetryErrorRequest) ops.PUT("/errors/:id/resolve", h.Admin.Ops.UpdateErrorResolution) // Request errors (client-visible failures) ops.GET("/request-errors", h.Admin.Ops.ListRequestErrors) ops.GET("/request-errors/:id", h.Admin.Ops.GetRequestError) ops.GET("/request-errors/:id/upstream-errors", h.Admin.Ops.ListRequestErrorUpstreamErrors) ops.POST("/request-errors/:id/retry-client", h.Admin.Ops.RetryRequestErrorClient) ops.POST("/request-errors/:id/upstream-errors/:idx/retry", h.Admin.Ops.RetryRequestErrorUpstreamEvent) ops.PUT("/request-errors/:id/resolve", h.Admin.Ops.ResolveRequestError) // Upstream errors (independent upstream failures) ops.GET("/upstream-errors", h.Admin.Ops.ListUpstreamErrors) ops.GET("/upstream-errors/:id", h.Admin.Ops.GetUpstreamError) ops.POST("/upstream-errors/:id/retry", h.Admin.Ops.RetryUpstreamError) ops.PUT("/upstream-errors/:id/resolve", h.Admin.Ops.ResolveUpstreamError) // Request drilldown (success + error) ops.GET("/requests", h.Admin.Ops.ListRequestDetails) // Dashboard (vNext - raw path for MVP) ops.GET("/dashboard/overview", h.Admin.Ops.GetDashboardOverview) ops.GET("/dashboard/throughput-trend", h.Admin.Ops.GetDashboardThroughputTrend) ops.GET("/dashboard/latency-histogram", h.Admin.Ops.GetDashboardLatencyHistogram) ops.GET("/dashboard/error-trend", h.Admin.Ops.GetDashboardErrorTrend) ops.GET("/dashboard/error-distribution", h.Admin.Ops.GetDashboardErrorDistribution) } } func registerDashboardRoutes(admin *gin.RouterGroup, h *handler.Handlers) { dashboard := admin.Group("/dashboard") { dashboard.GET("/stats", h.Admin.Dashboard.GetStats) dashboard.GET("/realtime", h.Admin.Dashboard.GetRealtimeMetrics) dashboard.GET("/trend", h.Admin.Dashboard.GetUsageTrend) dashboard.GET("/models", h.Admin.Dashboard.GetModelStats) dashboard.GET("/api-keys-trend", h.Admin.Dashboard.GetAPIKeyUsageTrend) dashboard.GET("/users-trend", h.Admin.Dashboard.GetUserUsageTrend) dashboard.POST("/users-usage", h.Admin.Dashboard.GetBatchUsersUsage) dashboard.POST("/api-keys-usage", h.Admin.Dashboard.GetBatchAPIKeysUsage) dashboard.POST("/aggregation/backfill", h.Admin.Dashboard.BackfillAggregation) } } func registerUserManagementRoutes(admin *gin.RouterGroup, h *handler.Handlers) { users := admin.Group("/users") { users.GET("", h.Admin.User.List) users.GET("/:id", h.Admin.User.GetByID) users.POST("", h.Admin.User.Create) users.PUT("/:id", h.Admin.User.Update) users.DELETE("/:id", h.Admin.User.Delete) users.POST("/:id/balance", h.Admin.User.UpdateBalance) users.GET("/:id/api-keys", h.Admin.User.GetUserAPIKeys) users.GET("/:id/usage", h.Admin.User.GetUserUsage) users.GET("/:id/balance-history", h.Admin.User.GetBalanceHistory) // User attribute values users.GET("/:id/attributes", h.Admin.UserAttribute.GetUserAttributes) users.PUT("/:id/attributes", h.Admin.UserAttribute.UpdateUserAttributes) } } func registerGroupRoutes(admin *gin.RouterGroup, h *handler.Handlers) { groups := admin.Group("/groups") { groups.GET("", h.Admin.Group.List) groups.GET("/all", h.Admin.Group.GetAll) groups.GET("/:id", h.Admin.Group.GetByID) groups.POST("", h.Admin.Group.Create) groups.PUT("/:id", h.Admin.Group.Update) groups.DELETE("/:id", h.Admin.Group.Delete) groups.GET("/:id/stats", h.Admin.Group.GetStats) groups.GET("/:id/api-keys", h.Admin.Group.GetGroupAPIKeys) } } func registerAccountRoutes(admin *gin.RouterGroup, h *handler.Handlers) { accounts := admin.Group("/accounts") { accounts.GET("", h.Admin.Account.List) accounts.GET("/:id", h.Admin.Account.GetByID) accounts.POST("", h.Admin.Account.Create) accounts.POST("/sync/crs", h.Admin.Account.SyncFromCRS) accounts.PUT("/:id", h.Admin.Account.Update) accounts.DELETE("/:id", h.Admin.Account.Delete) accounts.POST("/:id/test", h.Admin.Account.Test) accounts.POST("/:id/refresh", h.Admin.Account.Refresh) accounts.POST("/:id/refresh-tier", h.Admin.Account.RefreshTier) accounts.GET("/:id/stats", h.Admin.Account.GetStats) accounts.POST("/:id/clear-error", h.Admin.Account.ClearError) accounts.GET("/:id/usage", h.Admin.Account.GetUsage) accounts.GET("/:id/today-stats", h.Admin.Account.GetTodayStats) accounts.POST("/:id/clear-rate-limit", h.Admin.Account.ClearRateLimit) accounts.GET("/:id/temp-unschedulable", h.Admin.Account.GetTempUnschedulable) accounts.DELETE("/:id/temp-unschedulable", h.Admin.Account.ClearTempUnschedulable) accounts.POST("/:id/schedulable", h.Admin.Account.SetSchedulable) accounts.GET("/:id/models", h.Admin.Account.GetAvailableModels) accounts.POST("/batch", h.Admin.Account.BatchCreate) accounts.POST("/batch-update-credentials", h.Admin.Account.BatchUpdateCredentials) accounts.POST("/batch-refresh-tier", h.Admin.Account.BatchRefreshTier) accounts.POST("/bulk-update", h.Admin.Account.BulkUpdate) // Claude OAuth routes accounts.POST("/generate-auth-url", h.Admin.OAuth.GenerateAuthURL) accounts.POST("/generate-setup-token-url", h.Admin.OAuth.GenerateSetupTokenURL) accounts.POST("/exchange-code", h.Admin.OAuth.ExchangeCode) accounts.POST("/exchange-setup-token-code", h.Admin.OAuth.ExchangeSetupTokenCode) accounts.POST("/cookie-auth", h.Admin.OAuth.CookieAuth) accounts.POST("/setup-token-cookie-auth", h.Admin.OAuth.SetupTokenCookieAuth) } } func registerAnnouncementRoutes(admin *gin.RouterGroup, h *handler.Handlers) { announcements := admin.Group("/announcements") { announcements.GET("", h.Admin.Announcement.List) announcements.POST("", h.Admin.Announcement.Create) announcements.GET("/:id", h.Admin.Announcement.GetByID) announcements.PUT("/:id", h.Admin.Announcement.Update) announcements.DELETE("/:id", h.Admin.Announcement.Delete) announcements.GET("/:id/read-status", h.Admin.Announcement.ListReadStatus) } } func registerOpenAIOAuthRoutes(admin *gin.RouterGroup, h *handler.Handlers) { openai := admin.Group("/openai") { openai.POST("/generate-auth-url", h.Admin.OpenAIOAuth.GenerateAuthURL) openai.POST("/exchange-code", h.Admin.OpenAIOAuth.ExchangeCode) openai.POST("/refresh-token", h.Admin.OpenAIOAuth.RefreshToken) openai.POST("/accounts/:id/refresh", h.Admin.OpenAIOAuth.RefreshAccountToken) openai.POST("/create-from-oauth", h.Admin.OpenAIOAuth.CreateAccountFromOAuth) } } func registerGeminiOAuthRoutes(admin *gin.RouterGroup, h *handler.Handlers) { gemini := admin.Group("/gemini") { gemini.POST("/oauth/auth-url", h.Admin.GeminiOAuth.GenerateAuthURL) gemini.POST("/oauth/exchange-code", h.Admin.GeminiOAuth.ExchangeCode) gemini.GET("/oauth/capabilities", h.Admin.GeminiOAuth.GetCapabilities) } } func registerAntigravityOAuthRoutes(admin *gin.RouterGroup, h *handler.Handlers) { antigravity := admin.Group("/antigravity") { antigravity.POST("/oauth/auth-url", h.Admin.AntigravityOAuth.GenerateAuthURL) antigravity.POST("/oauth/exchange-code", h.Admin.AntigravityOAuth.ExchangeCode) } } func registerProxyRoutes(admin *gin.RouterGroup, h *handler.Handlers) { proxies := admin.Group("/proxies") { proxies.GET("", h.Admin.Proxy.List) proxies.GET("/all", h.Admin.Proxy.GetAll) proxies.GET("/:id", h.Admin.Proxy.GetByID) proxies.POST("", h.Admin.Proxy.Create) proxies.PUT("/:id", h.Admin.Proxy.Update) proxies.DELETE("/:id", h.Admin.Proxy.Delete) proxies.POST("/:id/test", h.Admin.Proxy.Test) proxies.GET("/:id/stats", h.Admin.Proxy.GetStats) proxies.GET("/:id/accounts", h.Admin.Proxy.GetProxyAccounts) proxies.POST("/batch-delete", h.Admin.Proxy.BatchDelete) proxies.POST("/batch", h.Admin.Proxy.BatchCreate) } } func registerRedeemCodeRoutes(admin *gin.RouterGroup, h *handler.Handlers) { codes := admin.Group("/redeem-codes") { codes.GET("", h.Admin.Redeem.List) codes.GET("/stats", h.Admin.Redeem.GetStats) codes.GET("/export", h.Admin.Redeem.Export) codes.GET("/:id", h.Admin.Redeem.GetByID) codes.POST("/generate", h.Admin.Redeem.Generate) codes.DELETE("/:id", h.Admin.Redeem.Delete) codes.POST("/batch-delete", h.Admin.Redeem.BatchDelete) codes.POST("/:id/expire", h.Admin.Redeem.Expire) } } func registerPromoCodeRoutes(admin *gin.RouterGroup, h *handler.Handlers) { promoCodes := admin.Group("/promo-codes") { promoCodes.GET("", h.Admin.Promo.List) promoCodes.GET("/:id", h.Admin.Promo.GetByID) promoCodes.POST("", h.Admin.Promo.Create) promoCodes.PUT("/:id", h.Admin.Promo.Update) promoCodes.DELETE("/:id", h.Admin.Promo.Delete) promoCodes.GET("/:id/usages", h.Admin.Promo.GetUsages) } } func registerSettingsRoutes(admin *gin.RouterGroup, h *handler.Handlers) { adminSettings := admin.Group("/settings") { adminSettings.GET("", h.Admin.Setting.GetSettings) adminSettings.PUT("", h.Admin.Setting.UpdateSettings) adminSettings.POST("/test-smtp", h.Admin.Setting.TestSMTPConnection) adminSettings.POST("/send-test-email", h.Admin.Setting.SendTestEmail) // Admin API Key 管理 adminSettings.GET("/admin-api-key", h.Admin.Setting.GetAdminAPIKey) adminSettings.POST("/admin-api-key/regenerate", h.Admin.Setting.RegenerateAdminAPIKey) adminSettings.DELETE("/admin-api-key", h.Admin.Setting.DeleteAdminAPIKey) // 流超时处理配置 adminSettings.GET("/stream-timeout", h.Admin.Setting.GetStreamTimeoutSettings) adminSettings.PUT("/stream-timeout", h.Admin.Setting.UpdateStreamTimeoutSettings) } } func registerSystemRoutes(admin *gin.RouterGroup, h *handler.Handlers) { system := admin.Group("/system") { system.GET("/version", h.Admin.System.GetVersion) system.GET("/check-updates", h.Admin.System.CheckUpdates) system.POST("/update", h.Admin.System.PerformUpdate) system.POST("/rollback", h.Admin.System.Rollback) system.POST("/restart", h.Admin.System.RestartService) } } func registerSubscriptionRoutes(admin *gin.RouterGroup, h *handler.Handlers) { subscriptions := admin.Group("/subscriptions") { subscriptions.GET("", h.Admin.Subscription.List) subscriptions.GET("/:id", h.Admin.Subscription.GetByID) subscriptions.GET("/:id/progress", h.Admin.Subscription.GetProgress) subscriptions.POST("/assign", h.Admin.Subscription.Assign) subscriptions.POST("/bulk-assign", h.Admin.Subscription.BulkAssign) subscriptions.POST("/:id/extend", h.Admin.Subscription.Extend) subscriptions.DELETE("/:id", h.Admin.Subscription.Revoke) } // 分组下的订阅列表 admin.GET("/groups/:id/subscriptions", h.Admin.Subscription.ListByGroup) // 用户下的订阅列表 admin.GET("/users/:id/subscriptions", h.Admin.Subscription.ListByUser) } func registerUsageRoutes(admin *gin.RouterGroup, h *handler.Handlers) { usage := admin.Group("/usage") { usage.GET("", h.Admin.Usage.List) usage.GET("/stats", h.Admin.Usage.Stats) usage.GET("/search-users", h.Admin.Usage.SearchUsers) usage.GET("/search-api-keys", h.Admin.Usage.SearchAPIKeys) usage.GET("/cleanup-tasks", h.Admin.Usage.ListCleanupTasks) usage.POST("/cleanup-tasks", h.Admin.Usage.CreateCleanupTask) usage.POST("/cleanup-tasks/:id/cancel", h.Admin.Usage.CancelCleanupTask) } } func registerUserAttributeRoutes(admin *gin.RouterGroup, h *handler.Handlers) { attrs := admin.Group("/user-attributes") { attrs.GET("", h.Admin.UserAttribute.ListDefinitions) attrs.POST("", h.Admin.UserAttribute.CreateDefinition) attrs.POST("/batch", h.Admin.UserAttribute.GetBatchUserAttributes) attrs.PUT("/reorder", h.Admin.UserAttribute.ReorderDefinitions) attrs.PUT("/:id", h.Admin.UserAttribute.UpdateDefinition) attrs.DELETE("/:id", h.Admin.UserAttribute.DeleteDefinition) } }