remove dead proxy service and sora storage action
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
|
||||
"github.com/Wei-Shaw/sub2api/internal/service"
|
||||
@@ -11,14 +8,14 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SoraHandler handles admin Sora statistics and management
|
||||
// SoraHandler handles admin Sora statistics and management.
|
||||
type SoraHandler struct {
|
||||
soraGenService *service.SoraGenerationService
|
||||
soraQuotaService *service.SoraQuotaService
|
||||
userRepo service.UserRepository
|
||||
}
|
||||
|
||||
// NewSoraHandler creates a new admin Sora handler
|
||||
// NewSoraHandler creates a new admin Sora handler.
|
||||
func NewSoraHandler(
|
||||
soraGenService *service.SoraGenerationService,
|
||||
soraQuotaService *service.SoraQuotaService,
|
||||
@@ -31,7 +28,6 @@ func NewSoraHandler(
|
||||
}
|
||||
}
|
||||
|
||||
// SoraSystemStatsResponse 系统级 Sora 统计
|
||||
type SoraSystemStatsResponse struct {
|
||||
TotalUsers int64 `json:"total_users"`
|
||||
TotalGenerations int64 `json:"total_generations"`
|
||||
@@ -41,39 +37,28 @@ type SoraSystemStatsResponse struct {
|
||||
ByModel map[string]int64 `json:"by_model"`
|
||||
}
|
||||
|
||||
// GetSystemStats 获取 Sora 系统统计
|
||||
// GET /api/v1/admin/sora/stats
|
||||
// GetSystemStats returns aggregate admin Sora statistics.
|
||||
func (h *SoraHandler) GetSystemStats(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
// 获取所有用户的 Sora 统计
|
||||
users, _, err := h.userRepo.List(ctx, pagination.PaginationParams{Page: 1, PageSize: 10000})
|
||||
if err != nil {
|
||||
response.Error(c, 500, "Failed to get users")
|
||||
return
|
||||
}
|
||||
|
||||
var totalStorageBytes int64
|
||||
byStatus := make(map[string]int64)
|
||||
byModel := make(map[string]int64)
|
||||
|
||||
// 遍历用户统计
|
||||
// NOTE: Per-user storage tracking removed; totalStorageBytes now sourced from SoraGenerationService if needed.
|
||||
_ = users // suppress unused warning until real aggregation is implemented
|
||||
|
||||
resp := SoraSystemStatsResponse{
|
||||
TotalUsers: int64(len(users)),
|
||||
TotalGenerations: 0,
|
||||
TotalStorageBytes: totalStorageBytes,
|
||||
TotalStorageBytes: 0,
|
||||
ActiveGenerations: 0,
|
||||
ByStatus: byStatus,
|
||||
ByModel: byModel,
|
||||
ByStatus: map[string]int64{},
|
||||
ByModel: map[string]int64{},
|
||||
}
|
||||
|
||||
response.Success(c, resp)
|
||||
}
|
||||
|
||||
// SoraUserStatsResponse 用户级 Sora 统计
|
||||
type SoraUserStatsResponse struct {
|
||||
UserID int64 `json:"user_id"`
|
||||
Username string `json:"username"`
|
||||
@@ -87,21 +72,16 @@ type SoraUserStatsResponse struct {
|
||||
TotalFileSizeBytes int64 `json:"total_file_size_bytes"`
|
||||
}
|
||||
|
||||
// ListUserStats 获取用户 Sora 使用统计列表
|
||||
// GET /api/v1/admin/sora/users
|
||||
// ListUserStats returns per-user admin Sora usage rows.
|
||||
func (h *SoraHandler) ListUserStats(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
page, pageSize := response.ParsePagination(c)
|
||||
search := c.Query("search")
|
||||
|
||||
filters := service.UserListFilters{
|
||||
Search: search,
|
||||
}
|
||||
|
||||
users, result, err := h.userRepo.ListWithFilters(ctx, pagination.PaginationParams{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
}, filters)
|
||||
}, service.UserListFilters{Search: search})
|
||||
if err != nil {
|
||||
response.Error(c, 500, "Failed to get users")
|
||||
return
|
||||
@@ -127,19 +107,18 @@ func (h *SoraHandler) ListUserStats(c *gin.Context) {
|
||||
Username: u.Username,
|
||||
Email: u.Email,
|
||||
QuotaBytes: quotaBytes,
|
||||
UsedBytes: 0, // per-user usage removed; use SoraGenerationService for real data
|
||||
UsedBytes: 0,
|
||||
AvailableBytes: availableBytes,
|
||||
QuotaSource: quotaSource,
|
||||
GenerationsCount: 0,
|
||||
ActiveCount: activeCount,
|
||||
TotalFileSizeBytes: 0, // per-user usage removed; use SoraGenerationService for real data
|
||||
TotalFileSizeBytes: 0,
|
||||
}
|
||||
}
|
||||
|
||||
response.Paginated(c, results, result.Total, page, pageSize)
|
||||
}
|
||||
|
||||
// SoraGenerationAdminResponse 管理员视角的生成记录
|
||||
type SoraGenerationAdminResponse struct {
|
||||
ID int64 `json:"id"`
|
||||
UserID int64 `json:"user_id"`
|
||||
@@ -157,43 +136,7 @@ type SoraGenerationAdminResponse struct {
|
||||
CompletedAt *string `json:"completed_at"`
|
||||
}
|
||||
|
||||
// ListGenerations 获取 Sora 生成记录列表(管理员视角)
|
||||
// GET /api/v1/admin/sora/generations
|
||||
// ListGenerations returns admin-visible generation rows.
|
||||
func (h *SoraHandler) ListGenerations(c *gin.Context) {
|
||||
// 简化实现:返回空列表
|
||||
// 完整实现需要扩展 repository 支持 admin 级别的查询
|
||||
response.Paginated(c, []SoraGenerationAdminResponse{}, int64(0), 1, 20)
|
||||
}
|
||||
|
||||
// ClearUserStorage 清除用户的 Sora 存储空间(已弃用)。
|
||||
//
|
||||
// Deprecated: Per-user storage tracking has been removed.
|
||||
// This endpoint now returns 410 Gone. Per-user Sora storage quota tracking was
|
||||
// fully removed in the Sora storage refactoring. Storage management is now
|
||||
// handled at the system-default level via SoraQuotaService.
|
||||
//
|
||||
// DELETE /api/v1/admin/sora/users/:id/storage
|
||||
func (h *SoraHandler) ClearUserStorage(c *gin.Context) {
|
||||
userID, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
response.BadRequest(c, "Invalid user ID")
|
||||
return
|
||||
}
|
||||
|
||||
// Verify user exists before responding
|
||||
ctx := c.Request.Context()
|
||||
if _, err := h.userRepo.GetByID(ctx, userID); err != nil {
|
||||
response.ErrorFrom(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Deprecation", "true")
|
||||
c.Header("Sunset", "2026-12-31")
|
||||
c.Header("Warning", `299 - "Gone: per-user storage tracking removed, see SoraQuotaService"`)
|
||||
c.JSON(http.StatusGone, gin.H{
|
||||
"error": "This endpoint is no longer available",
|
||||
"message": "Per-user Sora storage quota tracking has been removed. Storage is now managed at system level.",
|
||||
"sunset": "2026-12-31",
|
||||
"deprecated": true,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -25,35 +25,6 @@ func TestSoraHandler_ListGenerations(t *testing.T) {
|
||||
assert.Contains(t, w.Body.String(), "items")
|
||||
}
|
||||
|
||||
func TestSoraHandler_ClearUserStorage_InvalidUserID(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
handler := &SoraHandler{}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
userID string
|
||||
expected int
|
||||
}{
|
||||
{"empty string", "", http.StatusBadRequest},
|
||||
{"non-numeric", "abc", http.StatusBadRequest},
|
||||
{"float", "1.5", http.StatusBadRequest},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
c, _ := gin.CreateTestContext(w)
|
||||
c.Request = httptest.NewRequest(http.MethodDelete, "/admin/sora/users/"+tc.userID+"/storage", nil)
|
||||
c.Params = gin.Params{{Key: "id", Value: tc.userID}}
|
||||
|
||||
handler.ClearUserStorage(c)
|
||||
|
||||
assert.Equal(t, tc.expected, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSoraSystemStatsResponse_Fields(t *testing.T) {
|
||||
resp := SoraSystemStatsResponse{
|
||||
TotalUsers: 10,
|
||||
|
||||
@@ -99,7 +99,6 @@ func registerSoraRoutes(admin *gin.RouterGroup, h *handler.Handlers) {
|
||||
sora.GET("/stats", h.Admin.Sora.GetSystemStats)
|
||||
sora.GET("/users", h.Admin.Sora.ListUserStats)
|
||||
sora.GET("/generations", h.Admin.Sora.ListGenerations)
|
||||
sora.DELETE("/users/:id/storage", h.Admin.Sora.ClearUserStorage)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ func TestRegisterAdminRoutes_OmitsDeprecatedMockEndpoints(t *testing.T) {
|
||||
"GET /api/v1/admin/data-management/agent/health",
|
||||
"GET /api/v1/admin/data-management/config",
|
||||
"POST /api/v1/admin/data-management/backups",
|
||||
"DELETE /api/v1/admin/sora/users/:id/storage",
|
||||
}
|
||||
|
||||
for _, route := range deprecatedRoutes {
|
||||
|
||||
@@ -2,7 +2,6 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
|
||||
@@ -13,6 +12,8 @@ var (
|
||||
ErrProxyInUse = infraerrors.Conflict("PROXY_IN_USE", "proxy is in use by accounts")
|
||||
)
|
||||
|
||||
// ProxyRepository defines the shared proxy persistence contract used across
|
||||
// admin management and OAuth-related services.
|
||||
type ProxyRepository interface {
|
||||
Create(ctx context.Context, proxy *Proxy) error
|
||||
GetByID(ctx context.Context, id int64) (*Proxy, error)
|
||||
@@ -30,165 +31,3 @@ type ProxyRepository interface {
|
||||
CountAccountsByProxyID(ctx context.Context, proxyID int64) (int64, error)
|
||||
ListAccountSummariesByProxyID(ctx context.Context, proxyID int64) ([]ProxyAccountSummary, error)
|
||||
}
|
||||
|
||||
// CreateProxyRequest 创建代理请求
|
||||
type CreateProxyRequest struct {
|
||||
Name string `json:"name"`
|
||||
Protocol string `json:"protocol"`
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
// UpdateProxyRequest 更新代理请求
|
||||
type UpdateProxyRequest struct {
|
||||
Name *string `json:"name"`
|
||||
Protocol *string `json:"protocol"`
|
||||
Host *string `json:"host"`
|
||||
Port *int `json:"port"`
|
||||
Username *string `json:"username"`
|
||||
Password *string `json:"password"`
|
||||
Status *string `json:"status"`
|
||||
}
|
||||
|
||||
// ProxyService 代理管理服务
|
||||
type ProxyService struct {
|
||||
proxyRepo ProxyRepository
|
||||
}
|
||||
|
||||
// NewProxyService 创建代理服务实例
|
||||
func NewProxyService(proxyRepo ProxyRepository) *ProxyService {
|
||||
return &ProxyService{
|
||||
proxyRepo: proxyRepo,
|
||||
}
|
||||
}
|
||||
|
||||
// Create 创建代理
|
||||
func (s *ProxyService) Create(ctx context.Context, req CreateProxyRequest) (*Proxy, error) {
|
||||
// 创建代理
|
||||
proxy := &Proxy{
|
||||
Name: req.Name,
|
||||
Protocol: req.Protocol,
|
||||
Host: req.Host,
|
||||
Port: req.Port,
|
||||
Username: req.Username,
|
||||
Password: req.Password,
|
||||
Status: StatusActive,
|
||||
}
|
||||
|
||||
if err := s.proxyRepo.Create(ctx, proxy); err != nil {
|
||||
return nil, fmt.Errorf("create proxy: %w", err)
|
||||
}
|
||||
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取代理
|
||||
func (s *ProxyService) GetByID(ctx context.Context, id int64) (*Proxy, error) {
|
||||
proxy, err := s.proxyRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get proxy: %w", err)
|
||||
}
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
// List 获取代理列表
|
||||
func (s *ProxyService) List(ctx context.Context, params pagination.PaginationParams) ([]Proxy, *pagination.PaginationResult, error) {
|
||||
proxies, pagination, err := s.proxyRepo.List(ctx, params)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("list proxies: %w", err)
|
||||
}
|
||||
return proxies, pagination, nil
|
||||
}
|
||||
|
||||
// ListActive 获取活跃代理列表
|
||||
func (s *ProxyService) ListActive(ctx context.Context) ([]Proxy, error) {
|
||||
proxies, err := s.proxyRepo.ListActive(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list active proxies: %w", err)
|
||||
}
|
||||
return proxies, nil
|
||||
}
|
||||
|
||||
// Update 更新代理
|
||||
func (s *ProxyService) Update(ctx context.Context, id int64, req UpdateProxyRequest) (*Proxy, error) {
|
||||
proxy, err := s.proxyRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get proxy: %w", err)
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
if req.Name != nil {
|
||||
proxy.Name = *req.Name
|
||||
}
|
||||
|
||||
if req.Protocol != nil {
|
||||
proxy.Protocol = *req.Protocol
|
||||
}
|
||||
|
||||
if req.Host != nil {
|
||||
proxy.Host = *req.Host
|
||||
}
|
||||
|
||||
if req.Port != nil {
|
||||
proxy.Port = *req.Port
|
||||
}
|
||||
|
||||
if req.Username != nil {
|
||||
proxy.Username = *req.Username
|
||||
}
|
||||
|
||||
if req.Password != nil {
|
||||
proxy.Password = *req.Password
|
||||
}
|
||||
|
||||
if req.Status != nil {
|
||||
proxy.Status = *req.Status
|
||||
}
|
||||
|
||||
if err := s.proxyRepo.Update(ctx, proxy); err != nil {
|
||||
return nil, fmt.Errorf("update proxy: %w", err)
|
||||
}
|
||||
|
||||
return proxy, nil
|
||||
}
|
||||
|
||||
// Delete 删除代理
|
||||
func (s *ProxyService) Delete(ctx context.Context, id int64) error {
|
||||
// 检查代理是否存在
|
||||
_, err := s.proxyRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get proxy: %w", err)
|
||||
}
|
||||
|
||||
if err := s.proxyRepo.Delete(ctx, id); err != nil {
|
||||
return fmt.Errorf("delete proxy: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestConnection 测试代理连接(需要实现具体测试逻辑)
|
||||
func (s *ProxyService) TestConnection(ctx context.Context, id int64) error {
|
||||
proxy, err := s.proxyRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get proxy: %w", err)
|
||||
}
|
||||
|
||||
// TODO: 实现代理连接测试逻辑
|
||||
// 可以尝试通过代理发送测试请求
|
||||
_ = proxy
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetURL 获取代理URL
|
||||
func (s *ProxyService) GetURL(ctx context.Context, id int64) (string, error) {
|
||||
proxy, err := s.proxyRepo.GetByID(ctx, id)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("get proxy: %w", err)
|
||||
}
|
||||
|
||||
return proxy.URL(), nil
|
||||
}
|
||||
|
||||
@@ -395,7 +395,6 @@ var ProviderSet = wire.NewSet(
|
||||
ProvideAPIKeyAuthCacheInvalidator,
|
||||
NewGroupService,
|
||||
NewAccountService,
|
||||
NewProxyService,
|
||||
NewRedeemService,
|
||||
NewPromoService,
|
||||
NewUsageService,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/**
|
||||
* Admin Sora API
|
||||
* 管理员 Sora 统计和用户配额管理接口
|
||||
*/
|
||||
|
||||
import { apiClient } from '../client'
|
||||
@@ -46,17 +45,11 @@ export interface SoraGenerationAdmin {
|
||||
}
|
||||
|
||||
const soraAdminAPI = {
|
||||
/**
|
||||
* 获取 Sora 系统统计
|
||||
*/
|
||||
async getSystemStats(): Promise<SoraSystemStats> {
|
||||
const { data } = await apiClient.get<{ data: SoraSystemStats }>('/admin/sora/stats')
|
||||
return data.data
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取用户 Sora 使用统计列表
|
||||
*/
|
||||
async listUserStats(params?: {
|
||||
page?: number
|
||||
page_size?: number
|
||||
@@ -68,9 +61,6 @@ const soraAdminAPI = {
|
||||
return data
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取 Sora 生成记录列表(管理员视角)
|
||||
*/
|
||||
async listGenerations(params?: {
|
||||
page?: number
|
||||
page_size?: number
|
||||
@@ -82,14 +72,7 @@ const soraAdminAPI = {
|
||||
}): Promise<BasePaginationResponse<SoraGenerationAdmin>> {
|
||||
const { data } = await apiClient.get<BasePaginationResponse<SoraGenerationAdmin>>('/admin/sora/generations', { params })
|
||||
return data
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除用户的 Sora 存储空间(管理员操作)
|
||||
*/
|
||||
async clearUserStorage(userId: number): Promise<void> {
|
||||
await apiClient.delete(`/admin/sora/users/${userId}/storage`)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default soraAdminAPI
|
||||
|
||||
@@ -5,7 +5,6 @@ import soraAdminAPI, { type SoraSystemStats, type SoraUserStats, type SoraGenera
|
||||
import AppLayout from '@/components/layout/AppLayout.vue'
|
||||
import LoadingSpinner from '@/components/common/LoadingSpinner.vue'
|
||||
import Icon from '@/components/icons/Icon.vue'
|
||||
import ConfirmDialog from '@/components/common/ConfirmDialog.vue'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
@@ -16,11 +15,6 @@ const userStats = ref<SoraUserStats[]>([])
|
||||
const generations = ref<SoraGenerationAdmin[]>([])
|
||||
const activeTab = ref<'overview' | 'users' | 'generations'>('overview')
|
||||
|
||||
// Confirm dialog state
|
||||
const showConfirmDialog = ref(false)
|
||||
const confirmDialogMessage = ref('')
|
||||
const pendingClearUserId = ref<number | null>(null)
|
||||
|
||||
// Pagination
|
||||
const userPage = ref(1)
|
||||
const userPageSize = ref(20)
|
||||
@@ -102,33 +96,6 @@ async function fetchGenerations() {
|
||||
}
|
||||
}
|
||||
|
||||
// Confirm dialog handlers
|
||||
function confirmClearStorage(userId: number) {
|
||||
pendingClearUserId.value = userId
|
||||
confirmDialogMessage.value = t('admin.sora.confirmClearStorage')
|
||||
showConfirmDialog.value = true
|
||||
}
|
||||
|
||||
async function handleConfirmClear() {
|
||||
if (pendingClearUserId.value === null) return
|
||||
const userId = pendingClearUserId.value
|
||||
showConfirmDialog.value = false
|
||||
pendingClearUserId.value = null
|
||||
|
||||
try {
|
||||
await soraAdminAPI.clearUserStorage(userId)
|
||||
await fetchUserStats()
|
||||
} catch (err) {
|
||||
console.error('Failed to clear user storage:', err)
|
||||
alert(t('common.error'))
|
||||
}
|
||||
}
|
||||
|
||||
function handleCancelClear() {
|
||||
showConfirmDialog.value = false
|
||||
pendingClearUserId.value = null
|
||||
}
|
||||
|
||||
async function loadAll() {
|
||||
loading.value = true
|
||||
await Promise.all([
|
||||
@@ -359,14 +326,7 @@ onMounted(loadAll)
|
||||
<td class="px-4 py-3 text-sm text-gray-900 dark:text-white">{{ formatBytes(user.quota_bytes) }}</td>
|
||||
<td class="px-4 py-3 text-sm text-gray-900 dark:text-white">{{ formatBytes(user.used_bytes) }}</td>
|
||||
<td class="px-4 py-3 text-sm text-gray-900 dark:text-white">{{ user.generations_count }}</td>
|
||||
<td class="px-4 py-3 text-sm">
|
||||
<button
|
||||
class="btn btn-danger btn-sm"
|
||||
@click="confirmClearStorage(user.user_id)"
|
||||
>
|
||||
{{ t('admin.sora.clearStorage') }}
|
||||
</button>
|
||||
</td>
|
||||
<td class="px-4 py-3 text-sm text-gray-500 dark:text-gray-400">-</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -453,15 +413,5 @@ onMounted(loadAll)
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Confirm Dialog -->
|
||||
<ConfirmDialog
|
||||
:show="showConfirmDialog"
|
||||
:title="t('admin.sora.clearStorage')"
|
||||
:message="confirmDialogMessage"
|
||||
:danger="true"
|
||||
@confirm="handleConfirmClear"
|
||||
@cancel="handleCancelClear"
|
||||
/>
|
||||
</AppLayout>
|
||||
</template>
|
||||
|
||||
@@ -8,21 +8,18 @@ import type { SoraSystemStats, SoraUserStats, SoraGenerationAdmin } from '@/api/
|
||||
const {
|
||||
mockGetSystemStats,
|
||||
mockListUserStats,
|
||||
mockListGenerations,
|
||||
mockClearUserStorage
|
||||
mockListGenerations
|
||||
} = vi.hoisted(() => ({
|
||||
mockGetSystemStats: vi.fn(),
|
||||
mockListUserStats: vi.fn(),
|
||||
mockListGenerations: vi.fn(),
|
||||
mockClearUserStorage: vi.fn()
|
||||
mockListGenerations: vi.fn()
|
||||
}))
|
||||
|
||||
vi.mock('@/api/admin/sora', () => ({
|
||||
default: {
|
||||
getSystemStats: mockGetSystemStats,
|
||||
listUserStats: mockListUserStats,
|
||||
listGenerations: mockListGenerations,
|
||||
clearUserStorage: mockClearUserStorage
|
||||
listGenerations: mockListGenerations
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -209,6 +206,18 @@ describe('SoraAdminView', () => {
|
||||
expect(wrapper.text()).toContain('user1')
|
||||
expect(wrapper.text()).toContain('user1@example.com')
|
||||
})
|
||||
|
||||
it('does not render the deprecated clear storage action', async () => {
|
||||
const wrapper = mountComponent()
|
||||
await flushPromises()
|
||||
|
||||
const tabs = wrapper.findAll('button')
|
||||
const userTab = tabs.find(b => b.text().includes('admin.sora.userStats'))
|
||||
await userTab?.trigger('click')
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.text()).not.toContain('admin.sora.clearStorage')
|
||||
})
|
||||
})
|
||||
|
||||
describe('生成记录标签页', () => {
|
||||
|
||||
Reference in New Issue
Block a user