remove dead sora quota and dashboard wrappers
This commit is contained in:
@@ -8,32 +8,27 @@ import (
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/logger"
|
||||
)
|
||||
|
||||
// SoraQuotaService 管理 Sora 用户存储配额。
|
||||
// 仅支持系统默认配额(用户级和分组级配额已移除)。
|
||||
// SoraQuotaService manages Sora storage quota using only the system default quota.
|
||||
type SoraQuotaService struct {
|
||||
settingService *SettingService
|
||||
}
|
||||
|
||||
// NewSoraQuotaService 创建配额服务实例。
|
||||
func NewSoraQuotaService(settingService *SettingService) *SoraQuotaService {
|
||||
return &SoraQuotaService{
|
||||
settingService: settingService,
|
||||
}
|
||||
}
|
||||
|
||||
// QuotaInfo 返回给客户端的配额信息。
|
||||
type QuotaInfo struct {
|
||||
QuotaBytes int64 `json:"quota_bytes"` // 总配额(0 表示无限制)
|
||||
UsedBytes int64 `json:"used_bytes"` // 已使用(已移除追踪功能,始终为0)
|
||||
AvailableBytes int64 `json:"available_bytes"` // 剩余可用(无限制时为 0)
|
||||
QuotaSource string `json:"quota_source"` // 配额来源:system / unlimited
|
||||
Source string `json:"source,omitempty"` // 兼容旧字段
|
||||
QuotaBytes int64 `json:"quota_bytes"`
|
||||
UsedBytes int64 `json:"used_bytes"`
|
||||
AvailableBytes int64 `json:"available_bytes"`
|
||||
QuotaSource string `json:"quota_source"`
|
||||
Source string `json:"source,omitempty"`
|
||||
}
|
||||
|
||||
// ErrSoraStorageQuotaExceeded 表示配额不足。
|
||||
var ErrSoraStorageQuotaExceeded = errors.New("sora storage quota exceeded")
|
||||
|
||||
// QuotaExceededError 包含配额不足的上下文信息。
|
||||
type QuotaExceededError struct {
|
||||
QuotaBytes int64
|
||||
UsedBytes int64
|
||||
@@ -41,47 +36,39 @@ type QuotaExceededError struct {
|
||||
|
||||
func (e *QuotaExceededError) Error() string {
|
||||
if e == nil {
|
||||
return "存储配额不足"
|
||||
return "storage quota exceeded"
|
||||
}
|
||||
return fmt.Sprintf("存储配额不足(已用 %d / 配额 %d 字节)", e.UsedBytes, e.QuotaBytes)
|
||||
return fmt.Sprintf("storage quota exceeded (used %d / quota %d bytes)", e.UsedBytes, e.QuotaBytes)
|
||||
}
|
||||
|
||||
// GetQuota 获取用户的存储配额信息。
|
||||
// 仅使用系统默认值(用户级和分组级配额已移除)。
|
||||
func (s *SoraQuotaService) GetQuota(ctx context.Context, userID int64) (*QuotaInfo, error) {
|
||||
info := &QuotaInfo{
|
||||
UsedBytes: 0, // 已移除用量追踪
|
||||
UsedBytes: 0,
|
||||
}
|
||||
|
||||
// 系统默认值
|
||||
defaultQuota := s.getSystemDefaultQuota(ctx)
|
||||
if defaultQuota > 0 {
|
||||
info.QuotaBytes = defaultQuota
|
||||
info.QuotaSource = "system"
|
||||
info.Source = info.QuotaSource
|
||||
info.AvailableBytes = defaultQuota // 无用量追踪,可用等于配额
|
||||
info.AvailableBytes = defaultQuota
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// 无配额限制
|
||||
info.QuotaSource = "unlimited"
|
||||
info.Source = info.QuotaSource
|
||||
info.AvailableBytes = 0
|
||||
return info, nil
|
||||
}
|
||||
|
||||
// CheckQuota 检查用户是否有足够的存储配额。
|
||||
// 返回 nil 表示配额充足或无限制。
|
||||
func (s *SoraQuotaService) CheckQuota(ctx context.Context, userID int64, additionalBytes int64) error {
|
||||
quota, err := s.GetQuota(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// 0 表示无限制
|
||||
if quota.QuotaBytes == 0 {
|
||||
return nil
|
||||
}
|
||||
// 无用量追踪,只要请求大小不超过配额即可
|
||||
if additionalBytes > quota.QuotaBytes {
|
||||
return &QuotaExceededError{
|
||||
QuotaBytes: quota.QuotaBytes,
|
||||
@@ -91,7 +78,6 @@ func (s *SoraQuotaService) CheckQuota(ctx context.Context, userID int64, additio
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddUsage 累加用量(已移除追踪功能,仅记录日志)。
|
||||
func (s *SoraQuotaService) AddUsage(ctx context.Context, userID int64, bytes int64) error {
|
||||
if bytes <= 0 {
|
||||
return nil
|
||||
@@ -100,7 +86,6 @@ func (s *SoraQuotaService) AddUsage(ctx context.Context, userID int64, bytes int
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReleaseUsage 释放用量(已移除追踪功能,仅记录日志)。
|
||||
func (s *SoraQuotaService) ReleaseUsage(ctx context.Context, userID int64, bytes int64) error {
|
||||
if bytes <= 0 {
|
||||
return nil
|
||||
@@ -120,12 +105,6 @@ func (s *SoraQuotaService) getSystemDefaultQuota(ctx context.Context) int64 {
|
||||
return settings.DefaultStorageQuotaBytes
|
||||
}
|
||||
|
||||
// GetQuotaFromSettings 从系统设置获取默认配额(供外部使用)。
|
||||
func (s *SoraQuotaService) GetQuotaFromSettings(ctx context.Context) int64 {
|
||||
return s.getSystemDefaultQuota(ctx)
|
||||
}
|
||||
|
||||
// SetUserSoraQuota 设置用户级配额(已移除此功能)。
|
||||
func SetUserSoraQuota(ctx context.Context, userRepo UserRepository, userID int64, quotaBytes int64) error {
|
||||
return errors.New("user-level sora quota setting is no longer supported")
|
||||
}
|
||||
@@ -110,7 +110,10 @@ func TestCheckQuota_Exceeded(t *testing.T) {
|
||||
|
||||
err := svc.CheckQuota(context.Background(), 1, 2048)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "配额不足")
|
||||
var qe *QuotaExceededError
|
||||
require.ErrorAs(t, err, &qe)
|
||||
require.Equal(t, int64(1024), qe.QuotaBytes)
|
||||
require.Equal(t, int64(0), qe.UsedBytes)
|
||||
}
|
||||
|
||||
func TestCheckQuota_NoLimit(t *testing.T) {
|
||||
@@ -171,11 +174,6 @@ func TestGetQuotaFromSettings_WithSettings(t *testing.T) {
|
||||
// ==================== SetUserSoraQuota ====================
|
||||
// 用户级配额设置已移除
|
||||
|
||||
func TestSetUserSoraQuota_NotSupported(t *testing.T) {
|
||||
err := SetUserSoraQuota(context.Background(), nil, 1, 1024)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "no longer supported")
|
||||
}
|
||||
|
||||
// ==================== GetQuota — 字段完整性 ====================
|
||||
|
||||
@@ -265,7 +263,7 @@ func TestReleaseUsage_NegativeBytes(t *testing.T) {
|
||||
func TestQuotaExceededError_NilSafe(t *testing.T) {
|
||||
var e *QuotaExceededError
|
||||
msg := e.Error()
|
||||
require.Contains(t, msg, "配额不足")
|
||||
require.Contains(t, msg, "storage quota exceeded")
|
||||
}
|
||||
|
||||
func TestQuotaExceededError_Format(t *testing.T) {
|
||||
@@ -273,4 +271,4 @@ func TestQuotaExceededError_Format(t *testing.T) {
|
||||
msg := e.Error()
|
||||
require.Contains(t, msg, "512")
|
||||
require.Contains(t, msg, "1024")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import type {
|
||||
TrendDataPoint,
|
||||
ModelStat,
|
||||
GroupStat,
|
||||
ApiKeyUsageTrendPoint,
|
||||
UserUsageTrendPoint,
|
||||
UserSpendingRankingResponse,
|
||||
UserBreakdownItem,
|
||||
@@ -30,23 +29,6 @@ export interface TrendParams {
|
||||
billing_type?: number | null
|
||||
}
|
||||
|
||||
export interface TrendResponse {
|
||||
trend: TrendDataPoint[]
|
||||
start_date: string
|
||||
end_date: string
|
||||
granularity: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Get usage trend data
|
||||
* @param params - Query parameters for filtering
|
||||
* @returns Usage trend data
|
||||
*/
|
||||
export async function getUsageTrend(params?: TrendParams): Promise<TrendResponse> {
|
||||
const { data } = await apiClient.get<TrendResponse>('/admin/dashboard/trend', { params })
|
||||
return data
|
||||
}
|
||||
|
||||
export interface ModelStatsParams {
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
@@ -77,24 +59,6 @@ export async function getModelStats(params?: ModelStatsParams): Promise<ModelSta
|
||||
return data
|
||||
}
|
||||
|
||||
export interface GroupStatsParams {
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
user_id?: number
|
||||
api_key_id?: number
|
||||
account_id?: number
|
||||
group_id?: number
|
||||
request_type?: UsageRequestType
|
||||
stream?: boolean
|
||||
billing_type?: number | null
|
||||
}
|
||||
|
||||
export interface GroupStatsResponse {
|
||||
groups: GroupStat[]
|
||||
start_date: string
|
||||
end_date: string
|
||||
}
|
||||
|
||||
export interface DashboardSnapshotV2Params extends TrendParams {
|
||||
include_stats?: boolean
|
||||
include_trend?: boolean
|
||||
@@ -120,16 +84,6 @@ export interface DashboardSnapshotV2Response {
|
||||
users_trend?: UserUsageTrendPoint[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Get group usage statistics
|
||||
* @param params - Query parameters for filtering
|
||||
* @returns Group usage statistics
|
||||
*/
|
||||
export async function getGroupStats(params?: GroupStatsParams): Promise<GroupStatsResponse> {
|
||||
const { data } = await apiClient.get<GroupStatsResponse>('/admin/dashboard/groups', { params })
|
||||
return data
|
||||
}
|
||||
|
||||
export interface UserBreakdownParams {
|
||||
start_date?: string
|
||||
end_date?: string
|
||||
@@ -171,31 +125,6 @@ export async function getSnapshotV2(params?: DashboardSnapshotV2Params): Promise
|
||||
return data
|
||||
}
|
||||
|
||||
export interface ApiKeyTrendParams extends TrendParams {
|
||||
limit?: number
|
||||
}
|
||||
|
||||
export interface ApiKeyTrendResponse {
|
||||
trend: ApiKeyUsageTrendPoint[]
|
||||
start_date: string
|
||||
end_date: string
|
||||
granularity: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Get API key usage trend data
|
||||
* @param params - Query parameters for filtering
|
||||
* @returns API key usage trend data
|
||||
*/
|
||||
export async function getApiKeyUsageTrend(
|
||||
params?: ApiKeyTrendParams
|
||||
): Promise<ApiKeyTrendResponse> {
|
||||
const { data } = await apiClient.get<ApiKeyTrendResponse>('/admin/dashboard/api-keys-trend', {
|
||||
params
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
export interface UserTrendParams extends TrendParams {
|
||||
limit?: number
|
||||
}
|
||||
@@ -260,43 +189,13 @@ export async function getBatchUsersUsage(userIds: number[]): Promise<BatchUsersU
|
||||
return data
|
||||
}
|
||||
|
||||
export interface BatchApiKeyUsageStats {
|
||||
api_key_id: number
|
||||
today_actual_cost: number
|
||||
total_actual_cost: number
|
||||
}
|
||||
|
||||
export interface BatchApiKeysUsageResponse {
|
||||
stats: Record<string, BatchApiKeyUsageStats>
|
||||
}
|
||||
|
||||
/**
|
||||
* Get batch usage stats for multiple API keys
|
||||
* @param apiKeyIds - Array of API key IDs
|
||||
* @returns Usage stats map keyed by API key ID
|
||||
*/
|
||||
export async function getBatchApiKeysUsage(
|
||||
apiKeyIds: number[]
|
||||
): Promise<BatchApiKeysUsageResponse> {
|
||||
const { data } = await apiClient.post<BatchApiKeysUsageResponse>(
|
||||
'/admin/dashboard/api-keys-usage',
|
||||
{
|
||||
api_key_ids: apiKeyIds
|
||||
}
|
||||
)
|
||||
return data
|
||||
}
|
||||
|
||||
export const dashboardAPI = {
|
||||
getUsageTrend,
|
||||
getModelStats,
|
||||
getGroupStats,
|
||||
getUserBreakdown,
|
||||
getSnapshotV2,
|
||||
getApiKeyUsageTrend,
|
||||
getUserUsageTrend,
|
||||
getUserSpendingRanking,
|
||||
getBatchUsersUsage,
|
||||
getBatchApiKeysUsage
|
||||
getBatchUsersUsage
|
||||
}
|
||||
|
||||
export default dashboardAPI
|
||||
|
||||
@@ -8,6 +8,8 @@ const routerPath = resolve(dirname(fileURLToPath(import.meta.url)), '../index.ts
|
||||
const routerSource = readFileSync(routerPath, 'utf8')
|
||||
const adminApiIndexPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../api/admin/index.ts')
|
||||
const adminApiIndexSource = readFileSync(adminApiIndexPath, 'utf8')
|
||||
const adminDashboardApiPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../api/admin/dashboard.ts')
|
||||
const adminDashboardApiSource = readFileSync(adminDashboardApiPath, 'utf8')
|
||||
|
||||
describe('deprecated admin features', () => {
|
||||
it('does not expose the deprecated data-management admin route', () => {
|
||||
@@ -21,4 +23,11 @@ describe('deprecated admin features', () => {
|
||||
expect(adminApiIndexSource).not.toContain('dataManagementAPI,')
|
||||
expect(adminApiIndexSource).not.toContain("from './dataManagement'")
|
||||
})
|
||||
|
||||
it('does not expose dead dashboard admin wrappers', () => {
|
||||
expect(adminDashboardApiSource).not.toContain('getUsageTrend,')
|
||||
expect(adminDashboardApiSource).not.toContain('getGroupStats,')
|
||||
expect(adminDashboardApiSource).not.toContain('getApiKeyUsageTrend,')
|
||||
expect(adminDashboardApiSource).not.toContain('getBatchApiKeysUsage')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user