2026-01-04 19:27:53 +08:00
|
|
|
|
// Package routes provides HTTP route registration and handlers.
|
2025-12-26 10:42:08 +08:00
|
|
|
|
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)
|
|
|
|
|
|
|
2026-01-30 16:45:04 +08:00
|
|
|
|
// 公告管理
|
|
|
|
|
|
registerAnnouncementRoutes(admin, h)
|
|
|
|
|
|
|
2025-12-26 10:42:08 +08:00
|
|
|
|
// OpenAI OAuth
|
|
|
|
|
|
registerOpenAIOAuthRoutes(admin, h)
|
2026-02-19 08:02:56 +08:00
|
|
|
|
// Sora OAuth(实现复用 OpenAI OAuth 服务,入口独立)
|
|
|
|
|
|
registerSoraOAuthRoutes(admin, h)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
|
2025-12-26 00:17:55 -08:00
|
|
|
|
// Gemini OAuth
|
|
|
|
|
|
registerGeminiOAuthRoutes(admin, h)
|
|
|
|
|
|
|
2025-12-29 00:44:07 +08:00
|
|
|
|
// Antigravity OAuth
|
|
|
|
|
|
registerAntigravityOAuthRoutes(admin, h)
|
|
|
|
|
|
|
2025-12-26 10:42:08 +08:00
|
|
|
|
// 代理管理
|
|
|
|
|
|
registerProxyRoutes(admin, h)
|
|
|
|
|
|
|
|
|
|
|
|
// 卡密管理
|
|
|
|
|
|
registerRedeemCodeRoutes(admin, h)
|
|
|
|
|
|
|
2026-01-10 13:14:35 +08:00
|
|
|
|
// 优惠码管理
|
|
|
|
|
|
registerPromoCodeRoutes(admin, h)
|
|
|
|
|
|
|
2025-12-26 10:42:08 +08:00
|
|
|
|
// 系统设置
|
|
|
|
|
|
registerSettingsRoutes(admin, h)
|
|
|
|
|
|
|
2026-02-28 15:01:20 +08:00
|
|
|
|
// 数据管理
|
|
|
|
|
|
registerDataManagementRoutes(admin, h)
|
|
|
|
|
|
|
2026-03-13 10:38:19 +08:00
|
|
|
|
// 数据库备份恢复
|
|
|
|
|
|
registerBackupRoutes(admin, h)
|
|
|
|
|
|
|
2026-01-09 20:55:12 +08:00
|
|
|
|
// 运维监控(Ops)
|
|
|
|
|
|
registerOpsRoutes(admin, h)
|
|
|
|
|
|
|
2025-12-26 10:42:08 +08:00
|
|
|
|
// 系统管理
|
|
|
|
|
|
registerSystemRoutes(admin, h)
|
|
|
|
|
|
|
|
|
|
|
|
// 订阅管理
|
|
|
|
|
|
registerSubscriptionRoutes(admin, h)
|
|
|
|
|
|
|
|
|
|
|
|
// 使用记录管理
|
|
|
|
|
|
registerUsageRoutes(admin, h)
|
2026-01-01 18:58:34 +08:00
|
|
|
|
|
|
|
|
|
|
// 用户属性管理
|
|
|
|
|
|
registerUserAttributeRoutes(admin, h)
|
2026-02-05 21:52:54 +08:00
|
|
|
|
|
|
|
|
|
|
// 错误透传规则管理
|
|
|
|
|
|
registerErrorPassthroughRoutes(admin, h)
|
2026-02-28 00:07:44 +08:00
|
|
|
|
|
|
|
|
|
|
// API Key 管理
|
|
|
|
|
|
registerAdminAPIKeyRoutes(admin, h)
|
2026-03-05 16:06:05 +08:00
|
|
|
|
|
|
|
|
|
|
// 定时测试计划
|
|
|
|
|
|
registerScheduledTestRoutes(admin, h)
|
2026-02-28 00:07:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func registerAdminAPIKeyRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
|
|
|
|
|
|
apiKeys := admin.Group("/api-keys")
|
|
|
|
|
|
{
|
|
|
|
|
|
apiKeys.PUT("/:id", h.Admin.APIKey.UpdateGroup)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 20:55:12 +08:00
|
|
|
|
func registerOpsRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
|
|
|
|
|
|
ops := admin.Group("/ops")
|
|
|
|
|
|
{
|
|
|
|
|
|
// Realtime ops signals
|
|
|
|
|
|
ops.GET("/concurrency", h.Admin.Ops.GetConcurrencyStats)
|
2026-02-07 12:31:10 +08:00
|
|
|
|
ops.GET("/user-concurrency", h.Admin.Ops.GetUserConcurrencyStats)
|
2026-01-09 20:55:12 +08:00
|
|
|
|
ops.GET("/account-availability", h.Admin.Ops.GetAccountAvailability)
|
2026-01-12 14:17:58 +08:00
|
|
|
|
ops.GET("/realtime-traffic", h.Admin.Ops.GetRealtimeTrafficSummary)
|
2026-01-09 20:55:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 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)
|
2026-01-14 09:03:35 +08:00
|
|
|
|
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)
|
2026-01-09 20:55:12 +08:00
|
|
|
|
|
|
|
|
|
|
// 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)
|
2026-02-12 16:27:29 +08:00
|
|
|
|
runtime.GET("/logging", h.Admin.Ops.GetRuntimeLogConfig)
|
|
|
|
|
|
runtime.PUT("/logging", h.Admin.Ops.UpdateRuntimeLogConfig)
|
|
|
|
|
|
runtime.POST("/logging/reset", h.Admin.Ops.ResetRuntimeLogConfig)
|
2026-01-09 20:55:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-11 19:51:18 +08:00
|
|
|
|
// Advanced settings (DB-backed)
|
|
|
|
|
|
ops.GET("/advanced-settings", h.Admin.Ops.GetAdvancedSettings)
|
|
|
|
|
|
ops.PUT("/advanced-settings", h.Admin.Ops.UpdateAdvancedSettings)
|
|
|
|
|
|
|
2026-01-12 11:42:56 +08:00
|
|
|
|
// Settings group (DB-backed)
|
|
|
|
|
|
settings := ops.Group("/settings")
|
|
|
|
|
|
{
|
|
|
|
|
|
settings.GET("/metric-thresholds", h.Admin.Ops.GetMetricThresholds)
|
|
|
|
|
|
settings.PUT("/metric-thresholds", h.Admin.Ops.UpdateMetricThresholds)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-09 20:55:12 +08:00
|
|
|
|
// WebSocket realtime (QPS/TPS)
|
|
|
|
|
|
ws := ops.Group("/ws")
|
|
|
|
|
|
{
|
|
|
|
|
|
ws.GET("/qps", h.Admin.Ops.QPSWSHandler)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-14 14:29:01 +08:00
|
|
|
|
// Error logs (legacy)
|
2026-01-09 20:55:12 +08:00
|
|
|
|
ops.GET("/errors", h.Admin.Ops.GetErrorLogs)
|
|
|
|
|
|
ops.GET("/errors/:id", h.Admin.Ops.GetErrorLogByID)
|
2026-01-14 09:03:35 +08:00
|
|
|
|
ops.GET("/errors/:id/retries", h.Admin.Ops.ListRetryAttempts)
|
2026-01-09 20:55:12 +08:00
|
|
|
|
ops.POST("/errors/:id/retry", h.Admin.Ops.RetryErrorRequest)
|
2026-01-14 09:03:35 +08:00
|
|
|
|
ops.PUT("/errors/:id/resolve", h.Admin.Ops.UpdateErrorResolution)
|
2026-01-09 20:55:12 +08:00
|
|
|
|
|
2026-01-14 14:29:01 +08:00
|
|
|
|
// Request errors (client-visible failures)
|
|
|
|
|
|
ops.GET("/request-errors", h.Admin.Ops.ListRequestErrors)
|
|
|
|
|
|
ops.GET("/request-errors/:id", h.Admin.Ops.GetRequestError)
|
2026-01-14 23:16:01 +08:00
|
|
|
|
ops.GET("/request-errors/:id/upstream-errors", h.Admin.Ops.ListRequestErrorUpstreamErrors)
|
2026-01-14 14:29:01 +08:00
|
|
|
|
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)
|
2026-01-09 20:55:12 +08:00
|
|
|
|
|
|
|
|
|
|
// Request drilldown (success + error)
|
|
|
|
|
|
ops.GET("/requests", h.Admin.Ops.ListRequestDetails)
|
|
|
|
|
|
|
2026-02-12 16:27:29 +08:00
|
|
|
|
// Indexed system logs
|
|
|
|
|
|
ops.GET("/system-logs", h.Admin.Ops.ListSystemLogs)
|
|
|
|
|
|
ops.POST("/system-logs/cleanup", h.Admin.Ops.CleanupSystemLogs)
|
|
|
|
|
|
ops.GET("/system-logs/health", h.Admin.Ops.GetSystemLogIngestionHealth)
|
|
|
|
|
|
|
2026-01-09 20:55:12 +08:00
|
|
|
|
// Dashboard (vNext - raw path for MVP)
|
2026-03-04 13:45:49 +08:00
|
|
|
|
ops.GET("/dashboard/snapshot-v2", h.Admin.Ops.GetDashboardSnapshotV2)
|
2026-01-09 20:55:12 +08:00
|
|
|
|
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)
|
2026-02-12 14:20:14 +08:00
|
|
|
|
ops.GET("/dashboard/openai-token-stats", h.Admin.Ops.GetDashboardOpenAITokenStats)
|
2026-01-09 20:55:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 10:42:08 +08:00
|
|
|
|
func registerDashboardRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
|
|
|
|
|
|
dashboard := admin.Group("/dashboard")
|
|
|
|
|
|
{
|
2026-03-04 13:45:49 +08:00
|
|
|
|
dashboard.GET("/snapshot-v2", h.Admin.Dashboard.GetSnapshotV2)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
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)
|
2026-03-01 20:10:51 +08:00
|
|
|
|
dashboard.GET("/groups", h.Admin.Dashboard.GetGroupStats)
|
2026-01-04 19:27:53 +08:00
|
|
|
|
dashboard.GET("/api-keys-trend", h.Admin.Dashboard.GetAPIKeyUsageTrend)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
dashboard.GET("/users-trend", h.Admin.Dashboard.GetUserUsageTrend)
|
2026-03-13 03:41:29 +08:00
|
|
|
|
dashboard.GET("/users-ranking", h.Admin.Dashboard.GetUserSpendingRanking)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
dashboard.POST("/users-usage", h.Admin.Dashboard.GetBatchUsersUsage)
|
2026-01-04 19:27:53 +08:00
|
|
|
|
dashboard.POST("/api-keys-usage", h.Admin.Dashboard.GetBatchAPIKeysUsage)
|
feat(dashboard): add per-user drill-down for group, model, and endpoint distributions
Click on a group name, model name, or endpoint name in the distribution
tables to expand and show per-user usage breakdown (requests, tokens,
actual cost, standard cost).
Backend: new GET /admin/dashboard/user-breakdown API with group_id,
model, endpoint, endpoint_type filters.
Frontend: clickable rows with expand/collapse sub-table in all three
distribution charts.
2026-03-16 21:31:52 +08:00
|
|
|
|
dashboard.GET("/user-breakdown", h.Admin.Dashboard.GetUserBreakdown)
|
2026-01-11 16:01:35 +08:00
|
|
|
|
dashboard.POST("/aggregation/backfill", h.Admin.Dashboard.BackfillAggregation)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
2026-02-03 00:16:10 +08:00
|
|
|
|
users.GET("/:id/balance-history", h.Admin.User.GetBalanceHistory)
|
2026-01-01 18:58:34 +08:00
|
|
|
|
|
|
|
|
|
|
// User attribute values
|
|
|
|
|
|
users.GET("/:id/attributes", h.Admin.UserAttribute.GetUserAttributes)
|
|
|
|
|
|
users.PUT("/:id/attributes", h.Admin.UserAttribute.UpdateUserAttributes)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
2026-02-08 16:53:45 +08:00
|
|
|
|
groups.PUT("/sort-order", h.Admin.Group.UpdateSortOrder)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
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)
|
2026-03-12 23:37:36 +08:00
|
|
|
|
groups.GET("/:id/rate-multipliers", h.Admin.Group.GetGroupRateMultipliers)
|
|
|
|
|
|
groups.PUT("/:id/rate-multipliers", h.Admin.Group.BatchSetGroupRateMultipliers)
|
|
|
|
|
|
groups.DELETE("/:id/rate-multipliers", h.Admin.Group.ClearGroupRateMultipliers)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
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)
|
2026-02-24 17:11:14 +08:00
|
|
|
|
accounts.POST("/check-mixed-channel", h.Admin.Account.CheckMixedChannel)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
accounts.POST("/sync/crs", h.Admin.Account.SyncFromCRS)
|
2026-02-09 10:38:26 +08:00
|
|
|
|
accounts.POST("/sync/crs/preview", h.Admin.Account.PreviewFromCRS)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
accounts.PUT("/:id", h.Admin.Account.Update)
|
|
|
|
|
|
accounts.DELETE("/:id", h.Admin.Account.Delete)
|
|
|
|
|
|
accounts.POST("/:id/test", h.Admin.Account.Test)
|
2026-03-08 06:59:53 +08:00
|
|
|
|
accounts.POST("/:id/recover-state", h.Admin.Account.RecoverState)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
accounts.POST("/:id/refresh", h.Admin.Account.Refresh)
|
feat(gemini): 添加 Google One 存储空间推断 Tier 功能
## 功能概述
通过 Google Drive API 获取存储空间配额来推断 Google One 订阅等级,并优化统一的配额显示系统。
## 后端改动
- 新增 Drive API 客户端 (drive_client.go)
- 支持代理和指数退避重试
- 处理 403/429 错误
- 添加 Tier 推断逻辑 (inferGoogleOneTier)
- 支持 6 种 tier 类型:AI_PREMIUM, GOOGLE_ONE_STANDARD, GOOGLE_ONE_BASIC, FREE, GOOGLE_ONE_UNKNOWN, GOOGLE_ONE_UNLIMITED
- 集成到 OAuth 流程
- ExchangeCode: 授权时自动获取 tier
- RefreshAccountToken: Token 刷新时更新 tier (24小时缓存)
- 新增管理 API 端点
- POST /api/v1/admin/accounts/:id/refresh-tier (单个账号刷新)
- POST /api/v1/admin/accounts/batch-refresh-tier (批量刷新)
## 前端改动
- 更新 AccountQuotaInfo.vue
- 添加 Google One tier 标签映射
- 添加 tier 颜色样式 (紫色/蓝色/绿色/灰色/琥珀色)
- 更新 AccountUsageCell.vue
- 添加 Google One tier 显示逻辑
- 根据 oauth_type 区分显示方式
- 添加国际化翻译 (en.ts, zh.ts)
- aiPremium, standard, basic, free, personal, unlimited
## Tier 推断规则
- >= 2TB: AI Premium
- >= 200GB: Google One Standard
- >= 100GB: Google One Basic
- >= 15GB: Free
- > 100TB: Unlimited (G Suite legacy)
- 其他/失败: Unknown (显示为 Personal)
## 优雅降级
- Drive API 失败时使用 GOOGLE_ONE_UNKNOWN
- 不阻断 OAuth 流程
- 24小时缓存避免频繁调用
## 测试
- ✅ 后端编译成功
- ✅ 前端构建成功
- ✅ 所有代码符合现有规范
2025-12-31 21:45:24 -08:00
|
|
|
|
accounts.POST("/:id/refresh-tier", h.Admin.Account.RefreshTier)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
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)
|
2026-02-28 15:01:20 +08:00
|
|
|
|
accounts.POST("/today-stats/batch", h.Admin.Account.GetBatchTodayStats)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
accounts.POST("/:id/clear-rate-limit", h.Admin.Account.ClearRateLimit)
|
2026-03-05 20:54:37 +08:00
|
|
|
|
accounts.POST("/:id/reset-quota", h.Admin.Account.ResetQuota)
|
2026-01-03 06:34:00 -08:00
|
|
|
|
accounts.GET("/:id/temp-unschedulable", h.Admin.Account.GetTempUnschedulable)
|
|
|
|
|
|
accounts.DELETE("/:id/temp-unschedulable", h.Admin.Account.ClearTempUnschedulable)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
accounts.POST("/:id/schedulable", h.Admin.Account.SetSchedulable)
|
|
|
|
|
|
accounts.GET("/:id/models", h.Admin.Account.GetAvailableModels)
|
|
|
|
|
|
accounts.POST("/batch", h.Admin.Account.BatchCreate)
|
2026-02-05 17:46:08 +08:00
|
|
|
|
accounts.GET("/data", h.Admin.Account.ExportData)
|
|
|
|
|
|
accounts.POST("/data", h.Admin.Account.ImportData)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
accounts.POST("/batch-update-credentials", h.Admin.Account.BatchUpdateCredentials)
|
feat(gemini): 添加 Google One 存储空间推断 Tier 功能
## 功能概述
通过 Google Drive API 获取存储空间配额来推断 Google One 订阅等级,并优化统一的配额显示系统。
## 后端改动
- 新增 Drive API 客户端 (drive_client.go)
- 支持代理和指数退避重试
- 处理 403/429 错误
- 添加 Tier 推断逻辑 (inferGoogleOneTier)
- 支持 6 种 tier 类型:AI_PREMIUM, GOOGLE_ONE_STANDARD, GOOGLE_ONE_BASIC, FREE, GOOGLE_ONE_UNKNOWN, GOOGLE_ONE_UNLIMITED
- 集成到 OAuth 流程
- ExchangeCode: 授权时自动获取 tier
- RefreshAccountToken: Token 刷新时更新 tier (24小时缓存)
- 新增管理 API 端点
- POST /api/v1/admin/accounts/:id/refresh-tier (单个账号刷新)
- POST /api/v1/admin/accounts/batch-refresh-tier (批量刷新)
## 前端改动
- 更新 AccountQuotaInfo.vue
- 添加 Google One tier 标签映射
- 添加 tier 颜色样式 (紫色/蓝色/绿色/灰色/琥珀色)
- 更新 AccountUsageCell.vue
- 添加 Google One tier 显示逻辑
- 根据 oauth_type 区分显示方式
- 添加国际化翻译 (en.ts, zh.ts)
- aiPremium, standard, basic, free, personal, unlimited
## Tier 推断规则
- >= 2TB: AI Premium
- >= 200GB: Google One Standard
- >= 100GB: Google One Basic
- >= 15GB: Free
- > 100TB: Unlimited (G Suite legacy)
- 其他/失败: Unknown (显示为 Personal)
## 优雅降级
- Drive API 失败时使用 GOOGLE_ONE_UNKNOWN
- 不阻断 OAuth 流程
- 24小时缓存避免频繁调用
## 测试
- ✅ 后端编译成功
- ✅ 前端构建成功
- ✅ 所有代码符合现有规范
2025-12-31 21:45:24 -08:00
|
|
|
|
accounts.POST("/batch-refresh-tier", h.Admin.Account.BatchRefreshTier)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
accounts.POST("/bulk-update", h.Admin.Account.BulkUpdate)
|
2026-03-09 17:47:30 +08:00
|
|
|
|
accounts.POST("/batch-clear-error", h.Admin.Account.BatchClearError)
|
|
|
|
|
|
accounts.POST("/batch-refresh", h.Admin.Account.BatchRefresh)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
|
2026-02-07 12:31:10 +08:00
|
|
|
|
// Antigravity 默认模型映射
|
|
|
|
|
|
accounts.GET("/antigravity/default-model-mapping", h.Admin.Account.GetAntigravityDefaultModelMapping)
|
|
|
|
|
|
|
2025-12-26 10:42:08 +08:00
|
|
|
|
// 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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-30 16:45:04 +08:00
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 10:42:08 +08:00
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-19 08:02:56 +08:00
|
|
|
|
func registerSoraOAuthRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
|
|
|
|
|
|
sora := admin.Group("/sora")
|
|
|
|
|
|
{
|
|
|
|
|
|
sora.POST("/generate-auth-url", h.Admin.OpenAIOAuth.GenerateAuthURL)
|
|
|
|
|
|
sora.POST("/exchange-code", h.Admin.OpenAIOAuth.ExchangeCode)
|
|
|
|
|
|
sora.POST("/refresh-token", h.Admin.OpenAIOAuth.RefreshToken)
|
|
|
|
|
|
sora.POST("/st2at", h.Admin.OpenAIOAuth.ExchangeSoraSessionToken)
|
|
|
|
|
|
sora.POST("/rt2at", h.Admin.OpenAIOAuth.RefreshToken)
|
|
|
|
|
|
sora.POST("/accounts/:id/refresh", h.Admin.OpenAIOAuth.RefreshAccountToken)
|
|
|
|
|
|
sora.POST("/create-from-oauth", h.Admin.OpenAIOAuth.CreateAccountFromOAuth)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 00:17:55 -08:00
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-28 15:54:42 +08:00
|
|
|
|
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)
|
2026-02-10 23:57:18 +08:00
|
|
|
|
antigravity.POST("/oauth/refresh-token", h.Admin.AntigravityOAuth.RefreshToken)
|
2025-12-28 15:54:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 10:42:08 +08:00
|
|
|
|
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)
|
2026-02-05 17:46:08 +08:00
|
|
|
|
proxies.GET("/data", h.Admin.Proxy.ExportData)
|
2026-02-05 18:23:49 +08:00
|
|
|
|
proxies.POST("/data", h.Admin.Proxy.ImportData)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
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)
|
2026-02-19 21:18:35 +08:00
|
|
|
|
proxies.POST("/:id/quality-check", h.Admin.Proxy.CheckQuality)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
proxies.GET("/:id/stats", h.Admin.Proxy.GetStats)
|
|
|
|
|
|
proxies.GET("/:id/accounts", h.Admin.Proxy.GetProxyAccounts)
|
2026-01-14 19:45:29 +08:00
|
|
|
|
proxies.POST("/batch-delete", h.Admin.Proxy.BatchDelete)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
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)
|
2026-03-01 00:41:38 +08:00
|
|
|
|
codes.POST("/create-and-redeem", h.Admin.Redeem.CreateAndRedeem)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-10 13:14:35 +08:00
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 10:42:08 +08:00
|
|
|
|
func registerSettingsRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
|
|
|
|
|
|
adminSettings := admin.Group("/settings")
|
|
|
|
|
|
{
|
|
|
|
|
|
adminSettings.GET("", h.Admin.Setting.GetSettings)
|
|
|
|
|
|
adminSettings.PUT("", h.Admin.Setting.UpdateSettings)
|
2026-01-04 19:27:53 +08:00
|
|
|
|
adminSettings.POST("/test-smtp", h.Admin.Setting.TestSMTPConnection)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
adminSettings.POST("/send-test-email", h.Admin.Setting.SendTestEmail)
|
|
|
|
|
|
// Admin API Key 管理
|
2026-01-04 19:27:53 +08:00
|
|
|
|
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)
|
2026-01-11 21:54:52 -08:00
|
|
|
|
// 流超时处理配置
|
|
|
|
|
|
adminSettings.GET("/stream-timeout", h.Admin.Setting.GetStreamTimeoutSettings)
|
|
|
|
|
|
adminSettings.PUT("/stream-timeout", h.Admin.Setting.UpdateStreamTimeoutSettings)
|
2026-03-07 21:45:18 +08:00
|
|
|
|
// 请求整流器配置
|
|
|
|
|
|
adminSettings.GET("/rectifier", h.Admin.Setting.GetRectifierSettings)
|
|
|
|
|
|
adminSettings.PUT("/rectifier", h.Admin.Setting.UpdateRectifierSettings)
|
2026-03-10 11:14:17 +08:00
|
|
|
|
// Beta 策略配置
|
|
|
|
|
|
adminSettings.GET("/beta-policy", h.Admin.Setting.GetBetaPolicySettings)
|
|
|
|
|
|
adminSettings.PUT("/beta-policy", h.Admin.Setting.UpdateBetaPolicySettings)
|
2026-02-28 15:01:20 +08:00
|
|
|
|
// Sora S3 存储配置
|
|
|
|
|
|
adminSettings.GET("/sora-s3", h.Admin.Setting.GetSoraS3Settings)
|
|
|
|
|
|
adminSettings.PUT("/sora-s3", h.Admin.Setting.UpdateSoraS3Settings)
|
|
|
|
|
|
adminSettings.POST("/sora-s3/test", h.Admin.Setting.TestSoraS3Connection)
|
|
|
|
|
|
adminSettings.GET("/sora-s3/profiles", h.Admin.Setting.ListSoraS3Profiles)
|
|
|
|
|
|
adminSettings.POST("/sora-s3/profiles", h.Admin.Setting.CreateSoraS3Profile)
|
|
|
|
|
|
adminSettings.PUT("/sora-s3/profiles/:profile_id", h.Admin.Setting.UpdateSoraS3Profile)
|
|
|
|
|
|
adminSettings.DELETE("/sora-s3/profiles/:profile_id", h.Admin.Setting.DeleteSoraS3Profile)
|
|
|
|
|
|
adminSettings.POST("/sora-s3/profiles/:profile_id/activate", h.Admin.Setting.SetActiveSoraS3Profile)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func registerDataManagementRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
|
|
|
|
|
|
dataManagement := admin.Group("/data-management")
|
|
|
|
|
|
{
|
|
|
|
|
|
dataManagement.GET("/agent/health", h.Admin.DataManagement.GetAgentHealth)
|
|
|
|
|
|
dataManagement.GET("/config", h.Admin.DataManagement.GetConfig)
|
|
|
|
|
|
dataManagement.PUT("/config", h.Admin.DataManagement.UpdateConfig)
|
|
|
|
|
|
dataManagement.GET("/sources/:source_type/profiles", h.Admin.DataManagement.ListSourceProfiles)
|
|
|
|
|
|
dataManagement.POST("/sources/:source_type/profiles", h.Admin.DataManagement.CreateSourceProfile)
|
|
|
|
|
|
dataManagement.PUT("/sources/:source_type/profiles/:profile_id", h.Admin.DataManagement.UpdateSourceProfile)
|
|
|
|
|
|
dataManagement.DELETE("/sources/:source_type/profiles/:profile_id", h.Admin.DataManagement.DeleteSourceProfile)
|
|
|
|
|
|
dataManagement.POST("/sources/:source_type/profiles/:profile_id/activate", h.Admin.DataManagement.SetActiveSourceProfile)
|
|
|
|
|
|
dataManagement.POST("/s3/test", h.Admin.DataManagement.TestS3)
|
|
|
|
|
|
dataManagement.GET("/s3/profiles", h.Admin.DataManagement.ListS3Profiles)
|
|
|
|
|
|
dataManagement.POST("/s3/profiles", h.Admin.DataManagement.CreateS3Profile)
|
|
|
|
|
|
dataManagement.PUT("/s3/profiles/:profile_id", h.Admin.DataManagement.UpdateS3Profile)
|
|
|
|
|
|
dataManagement.DELETE("/s3/profiles/:profile_id", h.Admin.DataManagement.DeleteS3Profile)
|
|
|
|
|
|
dataManagement.POST("/s3/profiles/:profile_id/activate", h.Admin.DataManagement.SetActiveS3Profile)
|
|
|
|
|
|
dataManagement.POST("/backups", h.Admin.DataManagement.CreateBackupJob)
|
|
|
|
|
|
dataManagement.GET("/backups", h.Admin.DataManagement.ListBackupJobs)
|
|
|
|
|
|
dataManagement.GET("/backups/:job_id", h.Admin.DataManagement.GetBackupJob)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-13 10:38:19 +08:00
|
|
|
|
func registerBackupRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
|
|
|
|
|
|
backup := admin.Group("/backups")
|
|
|
|
|
|
{
|
|
|
|
|
|
// S3 存储配置
|
|
|
|
|
|
backup.GET("/s3-config", h.Admin.Backup.GetS3Config)
|
|
|
|
|
|
backup.PUT("/s3-config", h.Admin.Backup.UpdateS3Config)
|
|
|
|
|
|
backup.POST("/s3-config/test", h.Admin.Backup.TestS3Connection)
|
|
|
|
|
|
|
|
|
|
|
|
// 定时备份配置
|
|
|
|
|
|
backup.GET("/schedule", h.Admin.Backup.GetSchedule)
|
|
|
|
|
|
backup.PUT("/schedule", h.Admin.Backup.UpdateSchedule)
|
|
|
|
|
|
|
|
|
|
|
|
// 备份操作
|
|
|
|
|
|
backup.POST("", h.Admin.Backup.CreateBackup)
|
|
|
|
|
|
backup.GET("", h.Admin.Backup.ListBackups)
|
|
|
|
|
|
backup.GET("/:id", h.Admin.Backup.GetBackup)
|
|
|
|
|
|
backup.DELETE("/:id", h.Admin.Backup.DeleteBackup)
|
|
|
|
|
|
backup.GET("/:id/download-url", h.Admin.Backup.GetDownloadURL)
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复操作
|
|
|
|
|
|
backup.POST("/:id/restore", h.Admin.Backup.RestoreBackup)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-26 10:42:08 +08:00
|
|
|
|
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)
|
2026-03-10 11:21:11 +08:00
|
|
|
|
subscriptions.POST("/:id/reset-quota", h.Admin.Subscription.ResetQuota)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
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)
|
2026-01-04 19:27:53 +08:00
|
|
|
|
usage.GET("/search-api-keys", h.Admin.Usage.SearchAPIKeys)
|
2026-01-18 10:52:18 +08:00
|
|
|
|
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)
|
2025-12-26 10:42:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-01 18:58:34 +08:00
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-02-05 21:52:54 +08:00
|
|
|
|
|
2026-03-05 16:06:05 +08:00
|
|
|
|
func registerScheduledTestRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
|
|
|
|
|
|
plans := admin.Group("/scheduled-test-plans")
|
|
|
|
|
|
{
|
|
|
|
|
|
plans.POST("", h.Admin.ScheduledTest.Create)
|
|
|
|
|
|
plans.PUT("/:id", h.Admin.ScheduledTest.Update)
|
|
|
|
|
|
plans.DELETE("/:id", h.Admin.ScheduledTest.Delete)
|
|
|
|
|
|
plans.GET("/:id/results", h.Admin.ScheduledTest.ListResults)
|
|
|
|
|
|
}
|
|
|
|
|
|
// Nested under accounts
|
|
|
|
|
|
admin.GET("/accounts/:id/scheduled-test-plans", h.Admin.ScheduledTest.ListByAccount)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-05 21:52:54 +08:00
|
|
|
|
func registerErrorPassthroughRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
|
|
|
|
|
|
rules := admin.Group("/error-passthrough-rules")
|
|
|
|
|
|
{
|
|
|
|
|
|
rules.GET("", h.Admin.ErrorPassthrough.List)
|
|
|
|
|
|
rules.GET("/:id", h.Admin.ErrorPassthrough.GetByID)
|
|
|
|
|
|
rules.POST("", h.Admin.ErrorPassthrough.Create)
|
|
|
|
|
|
rules.PUT("/:id", h.Admin.ErrorPassthrough.Update)
|
|
|
|
|
|
rules.DELETE("/:id", h.Admin.ErrorPassthrough.Delete)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|