remove deprecated mock admin endpoints
This commit is contained in:
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
41
backend/internal/server/routes/admin_routes_test.go
Normal file
41
backend/internal/server/routes/admin_routes_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user