remove deprecated mock admin endpoints

This commit is contained in:
2026-04-20 13:05:44 +08:00
parent ed642e8769
commit b3f112005e
13 changed files with 68 additions and 198 deletions

View File

@@ -29,7 +29,6 @@ func setupAdminRouter() (*gin.Engine, *stubAdminService) {
router.DELETE("/api/v1/admin/users/:id", userHandler.Delete)
router.POST("/api/v1/admin/users/:id/balance", userHandler.UpdateBalance)
router.GET("/api/v1/admin/users/:id/api-keys", userHandler.GetUserAPIKeys)
router.GET("/api/v1/admin/users/:id/usage", userHandler.GetUserUsage)
router.GET("/api/v1/admin/groups", groupHandler.List)
router.GET("/api/v1/admin/groups/all", groupHandler.GetAll)
@@ -49,7 +48,6 @@ func setupAdminRouter() (*gin.Engine, *stubAdminService) {
router.POST("/api/v1/admin/proxies/batch-delete", proxyHandler.BatchDelete)
router.POST("/api/v1/admin/proxies/:id/test", proxyHandler.Test)
router.POST("/api/v1/admin/proxies/:id/quality-check", proxyHandler.CheckQuality)
router.GET("/api/v1/admin/proxies/:id/stats", proxyHandler.GetStats)
router.GET("/api/v1/admin/proxies/:id/accounts", proxyHandler.GetProxyAccounts)
router.GET("/api/v1/admin/redeem-codes", redeemHandler.List)
@@ -58,7 +56,6 @@ func setupAdminRouter() (*gin.Engine, *stubAdminService) {
router.DELETE("/api/v1/admin/redeem-codes/:id", redeemHandler.Delete)
router.POST("/api/v1/admin/redeem-codes/batch-delete", redeemHandler.BatchDelete)
router.POST("/api/v1/admin/redeem-codes/:id/expire", redeemHandler.Expire)
router.GET("/api/v1/admin/redeem-codes/:id/stats", redeemHandler.GetStats)
return router, adminSvc
}
@@ -108,10 +105,6 @@ func TestUserHandlerEndpoints(t *testing.T) {
router.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodGet, "/api/v1/admin/users/1/usage?period=today", nil)
router.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
}
func TestGroupHandlerEndpoints(t *testing.T) {
@@ -215,11 +208,6 @@ func TestProxyHandlerEndpoints(t *testing.T) {
router.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodGet, "/api/v1/admin/proxies/4/stats", nil)
router.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodGet, "/api/v1/admin/proxies/4/accounts", nil)
router.ServeHTTP(rec, req)
@@ -282,8 +270,31 @@ func TestRedeemHandlerEndpoints(t *testing.T) {
router.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
rec = httptest.NewRecorder()
req = httptest.NewRequest(http.MethodGet, "/api/v1/admin/redeem-codes/5/stats", nil)
router.ServeHTTP(rec, req)
require.Equal(t, http.StatusOK, rec.Code)
}
func TestDeprecatedMockAdminEndpointsNotRegistered(t *testing.T) {
router, _ := setupAdminRouter()
testCases := []struct {
name string
url string
}{
{
name: "user usage",
url: "/api/v1/admin/users/1/usage?period=today",
},
{
name: "proxy stats",
url: "/api/v1/admin/proxies/4/stats",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
rec := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, tc.url, nil)
router.ServeHTTP(rec, req)
require.Equal(t, http.StatusNotFound, rec.Code)
})
}
}

View File

@@ -164,10 +164,6 @@ func (s *stubAdminService) GetUserAPIKeys(ctx context.Context, userID int64, pag
return s.apiKeys, int64(len(s.apiKeys)), nil
}
func (s *stubAdminService) GetUserUsageStats(ctx context.Context, userID int64, period string) (any, error) {
return map[string]any{"user_id": userID}, nil
}
func (s *stubAdminService) ListGroups(ctx context.Context, page, pageSize int, platform, status, search string, isExclusive *bool, sortBy, sortOrder string) ([]service.Group, int64, error) {
return s.groups, int64(len(s.groups)), nil
}

View File

@@ -175,18 +175,6 @@ func (h *DashboardHandler) BackfillAggregation(c *gin.Context) {
})
}
// GetRealtimeMetrics handles getting real-time system metrics
// GET /api/v1/admin/dashboard/realtime
func (h *DashboardHandler) GetRealtimeMetrics(c *gin.Context) {
// Return mock data for now
response.Success(c, gin.H{
"active_requests": 0,
"requests_per_minute": 0,
"average_response_time": 0,
"error_rate": 0.0,
})
}
// GetUsageTrend handles getting usage trend data
// GET /api/v1/admin/dashboard/trend
// Query params: start_date, end_date (YYYY-MM-DD), granularity (day/hour), user_id, api_key_id, model, account_id, group_id, request_type, stream, billing_type

View File

@@ -257,26 +257,6 @@ func (h *ProxyHandler) CheckQuality(c *gin.Context) {
response.Success(c, result)
}
// GetStats handles getting proxy statistics
// GET /api/v1/admin/proxies/:id/stats
func (h *ProxyHandler) GetStats(c *gin.Context) {
proxyID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequest(c, "Invalid proxy ID")
return
}
// Return mock data for now
_ = proxyID
response.Success(c, gin.H{
"total_accounts": 0,
"active_accounts": 0,
"total_requests": 0,
"success_rate": 100.0,
"average_latency": 0,
})
}
// GetProxyAccounts handles getting accounts using a proxy
// GET /api/v1/admin/proxies/:id/accounts
func (h *ProxyHandler) GetProxyAccounts(c *gin.Context) {

View File

@@ -276,24 +276,6 @@ func (h *RedeemHandler) Expire(c *gin.Context) {
response.Success(c, dto.RedeemCodeFromServiceAdmin(code))
}
// GetStats handles getting redeem code statistics
// GET /api/v1/admin/redeem-codes/stats
func (h *RedeemHandler) GetStats(c *gin.Context) {
// Return mock data for now
response.Success(c, gin.H{
"total_codes": 0,
"active_codes": 0,
"used_codes": 0,
"expired_codes": 0,
"total_value_distributed": 0.0,
"by_type": gin.H{
"balance": 0,
"concurrency": 0,
"trial": 0,
},
})
}
// Export handles exporting redeem codes to CSV
// GET /api/v1/admin/redeem-codes/export
func (h *RedeemHandler) Export(c *gin.Context) {

View File

@@ -308,26 +308,6 @@ func (h *UserHandler) GetUserAPIKeys(c *gin.Context) {
response.Paginated(c, out, total, page, pageSize)
}
// GetUserUsage handles getting user's usage statistics
// GET /api/v1/admin/users/:id/usage
func (h *UserHandler) GetUserUsage(c *gin.Context) {
userID, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.BadRequest(c, "Invalid user ID")
return
}
period := c.DefaultQuery("period", "month")
stats, err := h.adminService.GetUserUsageStats(c.Request.Context(), userID, period)
if err != nil {
response.ErrorFrom(c, err)
return
}
response.Success(c, stats)
}
// GetBalanceHistory handles getting user's balance/concurrency change history
// GET /api/v1/admin/users/:id/balance-history
// Query params:

View File

@@ -213,7 +213,6 @@ func registerDashboardRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
{
dashboard.GET("/snapshot-v2", h.Admin.Dashboard.GetSnapshotV2)
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("/groups", h.Admin.Dashboard.GetGroupStats)
@@ -237,7 +236,6 @@ func registerUserManagementRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
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)
users.POST("/:id/replace-group", h.Admin.User.ReplaceGroup)
@@ -370,7 +368,6 @@ func registerProxyRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
proxies.DELETE("/:id", h.Admin.Proxy.Delete)
proxies.POST("/:id/test", h.Admin.Proxy.Test)
proxies.POST("/:id/quality-check", h.Admin.Proxy.CheckQuality)
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)
@@ -381,7 +378,6 @@ 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("/create-and-redeem", h.Admin.Redeem.CreateAndRedeem)

View File

@@ -0,0 +1,41 @@
package routes
import (
"testing"
"github.com/Wei-Shaw/sub2api/internal/handler"
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
)
func TestRegisterAdminRoutes_OmitsDeprecatedMockEndpoints(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := gin.New()
v1 := engine.Group("/api/v1")
adminAuth := middleware.AdminAuthMiddleware(func(c *gin.Context) {
c.Next()
})
RegisterAdminRoutes(v1, &handler.Handlers{
Admin: &handler.AdminHandlers{},
}, adminAuth)
routesByMethodAndPath := make(map[string]struct{})
for _, route := range engine.Routes() {
routesByMethodAndPath[route.Method+" "+route.Path] = struct{}{}
}
deprecatedRoutes := []string{
"GET /api/v1/admin/dashboard/realtime",
"GET /api/v1/admin/users/:id/usage",
"GET /api/v1/admin/proxies/:id/stats",
"GET /api/v1/admin/redeem-codes/stats",
}
for _, route := range deprecatedRoutes {
_, exists := routesByMethodAndPath[route]
require.Falsef(t, exists, "deprecated mock route should not be registered: %s", route)
}
}

View File

@@ -28,7 +28,6 @@ type AdminService interface {
DeleteUser(ctx context.Context, id int64) error
UpdateUserBalance(ctx context.Context, userID int64, balance float64, operation string, notes string) (*User, error)
GetUserAPIKeys(ctx context.Context, userID int64, page, pageSize int, sortBy, sortOrder string) ([]APIKey, int64, error)
GetUserUsageStats(ctx context.Context, userID int64, period string) (any, error)
// GetUserBalanceHistory returns paginated balance/concurrency change records for a user.
// codeType is optional - pass empty string to return all types.
// Also returns totalRecharged (sum of all positive balance top-ups).
@@ -772,17 +771,6 @@ func (s *adminServiceImpl) GetUserAPIKeys(ctx context.Context, userID int64, pag
return keys, result.Total, nil
}
func (s *adminServiceImpl) GetUserUsageStats(ctx context.Context, userID int64, period string) (any, error) {
// Return mock data for now
return map[string]any{
"period": period,
"total_requests": 0,
"total_cost": 0.0,
"total_tokens": 0,
"avg_duration_ms": 0,
}, nil
}
// GetUserBalanceHistory returns paginated balance/concurrency change records for a user.
func (s *adminServiceImpl) GetUserBalanceHistory(ctx context.Context, userID int64, page, pageSize int, codeType string) ([]RedeemCode, int64, float64, error) {
params := pagination.PaginationParams{Page: page, PageSize: pageSize}

View File

@@ -25,25 +25,6 @@ export async function getStats(): Promise<DashboardStats> {
return data
}
/**
* Get real-time metrics
* @returns Real-time system metrics
*/
export async function getRealtimeMetrics(): Promise<{
active_requests: number
requests_per_minute: number
average_response_time: number
error_rate: number
}> {
const { data } = await apiClient.get<{
active_requests: number
requests_per_minute: number
average_response_time: number
error_rate: number
}>('/admin/dashboard/realtime')
return data
}
export interface TrendParams {
start_date?: string
end_date?: string
@@ -317,7 +298,6 @@ export async function getBatchApiKeysUsage(
export const dashboardAPI = {
getStats,
getRealtimeMetrics,
getUsageTrend,
getModelStats,
getGroupStats,

View File

@@ -156,28 +156,6 @@ export async function checkProxyQuality(id: number): Promise<ProxyQualityCheckRe
return data
}
/**
* Get proxy usage statistics
* @param id - Proxy ID
* @returns Proxy usage statistics
*/
export async function getStats(id: number): Promise<{
total_accounts: number
active_accounts: number
total_requests: number
success_rate: number
average_latency: number
}> {
const { data } = await apiClient.get<{
total_accounts: number
active_accounts: number
total_requests: number
success_rate: number
average_latency: number
}>(`/admin/proxies/${id}/stats`)
return data
}
/**
* Get accounts using a proxy
* @param id - Proxy ID
@@ -266,7 +244,6 @@ export const proxiesAPI = {
toggleStatus,
testProxy,
checkProxyQuality,
getStats,
getProxyAccounts,
batchCreate,
batchDelete,

View File

@@ -127,29 +127,6 @@ export async function expire(id: number): Promise<RedeemCode> {
return data
}
/**
* Get redeem code statistics
* @returns Statistics about redeem codes
*/
export async function getStats(): Promise<{
total_codes: number
active_codes: number
used_codes: number
expired_codes: number
total_value_distributed: number
by_type: Record<RedeemCodeType, number>
}> {
const { data } = await apiClient.get<{
total_codes: number
active_codes: number
used_codes: number
expired_codes: number
total_value_distributed: number
by_type: Record<RedeemCodeType, number>
}>('/admin/redeem-codes/stats')
return data
}
/**
* Export redeem codes to CSV
* @param filters - Optional filters
@@ -176,7 +153,6 @@ export const redeemAPI = {
delete: deleteCode,
batchDelete,
expire,
getStats,
exportCodes
}

View File

@@ -158,30 +158,6 @@ export async function getUserApiKeys(id: number): Promise<PaginatedResponse<ApiK
return data
}
/**
* Get user's usage statistics
* @param id - User ID
* @param period - Time period
* @returns User usage statistics
*/
export async function getUserUsageStats(
id: number,
period: string = 'month'
): Promise<{
total_requests: number
total_cost: number
total_tokens: number
}> {
const { data } = await apiClient.get<{
total_requests: number
total_cost: number
total_tokens: number
}>(`/admin/users/${id}/usage`, {
params: { period }
})
return data
}
/**
* Balance history item returned from the API
*/
@@ -258,7 +234,6 @@ export const usersAPI = {
updateConcurrency,
toggleStatus,
getUserApiKeys,
getUserUsageStats,
getUserBalanceHistory,
replaceGroup
}