mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-07 01:47:47 +08:00
fix: restore security hardening compatibility and validation
Restore authorized rule reveal, explicit full HTTP capture and retention, video task business fields, and valid payment URLs. Add opt-in credential preservation for trusted recovery, fix frontend type contracts and async races, and eliminate PostgreSQL test fixture resource leaks. Document audit coverage and successful fmt and CI-scoped Clippy checks.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { AxiosAdapter, AxiosInstance, InternalAxiosRequestConfig } from 'axios'
|
||||
import type { AxiosAdapter, InternalAxiosRequestConfig } from 'axios'
|
||||
|
||||
import apiClient, {
|
||||
AUTH_SESSION_SIGNAL_KEY,
|
||||
@@ -8,10 +8,6 @@ import apiClient, {
|
||||
} from '@/api/client'
|
||||
import { cache, cachedRequest } from '@/utils/cache'
|
||||
|
||||
type TestableApiClient = typeof apiClient & {
|
||||
client: AxiosInstance
|
||||
}
|
||||
|
||||
describe('apiClient auth state change event', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear()
|
||||
@@ -55,10 +51,10 @@ describe('apiClient auth state change event', () => {
|
||||
})
|
||||
|
||||
it('restores a session through the refresh cookie and stores the result in memory only', async () => {
|
||||
const rawClient = apiClient as TestableApiClient
|
||||
const previousAdapter = rawClient.client.defaults.adapter
|
||||
const rawClient = apiClient['client']
|
||||
const previousAdapter = rawClient.defaults.adapter
|
||||
|
||||
rawClient.client.defaults.adapter = (async (config: InternalAxiosRequestConfig) => ({
|
||||
rawClient.defaults.adapter = (async (config: InternalAxiosRequestConfig) => ({
|
||||
data: { access_token: 'restored-access-token' },
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
@@ -72,16 +68,16 @@ describe('apiClient auth state change event', () => {
|
||||
expect(localStorage.getItem('access_token')).toBeNull()
|
||||
expect(sessionStorage.getItem('access_token')).toBeNull()
|
||||
} finally {
|
||||
rawClient.client.defaults.adapter = previousAdapter
|
||||
rawClient.defaults.adapter = previousAdapter
|
||||
}
|
||||
})
|
||||
|
||||
it('does not resurrect a session when logout wins an in-flight restore', async () => {
|
||||
const rawClient = apiClient as TestableApiClient
|
||||
const previousAdapter = rawClient.client.defaults.adapter
|
||||
const rawClient = apiClient['client']
|
||||
const previousAdapter = rawClient.defaults.adapter
|
||||
let resolveRefresh!: (response: Awaited<ReturnType<AxiosAdapter>>) => void
|
||||
|
||||
rawClient.client.defaults.adapter = (() => new Promise((resolve) => {
|
||||
rawClient.defaults.adapter = (() => new Promise((resolve) => {
|
||||
resolveRefresh = resolve
|
||||
})) as AxiosAdapter
|
||||
|
||||
@@ -100,7 +96,7 @@ describe('apiClient auth state change event', () => {
|
||||
await expect(restore).rejects.toThrow('Auth state changed')
|
||||
expect(apiClient.getToken()).toBeNull()
|
||||
} finally {
|
||||
rawClient.client.defaults.adapter = previousAdapter
|
||||
rawClient.defaults.adapter = previousAdapter
|
||||
}
|
||||
})
|
||||
|
||||
@@ -141,11 +137,11 @@ describe('apiClient auth state change event', () => {
|
||||
})
|
||||
|
||||
it('sends auth refresh without a request body', async () => {
|
||||
const rawClient = apiClient as TestableApiClient
|
||||
const previousAdapter = rawClient.client.defaults.adapter
|
||||
const rawClient = apiClient['client']
|
||||
const previousAdapter = rawClient.defaults.adapter
|
||||
const requests: InternalAxiosRequestConfig[] = []
|
||||
|
||||
rawClient.client.defaults.adapter = (async (config: InternalAxiosRequestConfig) => {
|
||||
rawClient.defaults.adapter = (async (config: InternalAxiosRequestConfig) => {
|
||||
requests.push(config)
|
||||
return {
|
||||
data: { access_token: 'new-access-token' },
|
||||
@@ -165,16 +161,16 @@ describe('apiClient auth state change event', () => {
|
||||
expect(requests[0].method).toBe('post')
|
||||
expect(requests[0].data).toBeUndefined()
|
||||
} finally {
|
||||
rawClient.client.defaults.adapter = previousAdapter
|
||||
rawClient.defaults.adapter = previousAdapter
|
||||
}
|
||||
})
|
||||
|
||||
it('authenticates protected gateway operational requests', async () => {
|
||||
const rawClient = apiClient as TestableApiClient
|
||||
const previousAdapter = rawClient.client.defaults.adapter
|
||||
const rawClient = apiClient['client']
|
||||
const previousAdapter = rawClient.defaults.adapter
|
||||
const requests: InternalAxiosRequestConfig[] = []
|
||||
|
||||
rawClient.client.defaults.adapter = (async (config: InternalAxiosRequestConfig) => {
|
||||
rawClient.defaults.adapter = (async (config: InternalAxiosRequestConfig) => {
|
||||
requests.push(config)
|
||||
return {
|
||||
data: '',
|
||||
@@ -193,7 +189,7 @@ describe('apiClient auth state change event', () => {
|
||||
expect(requests[0].headers.Authorization).toBe('Bearer operational-access-token')
|
||||
expect(requests[0].headers['X-Client-Device-Id']).toBeTruthy()
|
||||
} finally {
|
||||
rawClient.client.defaults.adapter = previousAdapter
|
||||
rawClient.defaults.adapter = previousAdapter
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { revealEndpointRules } from '../endpoints/endpoints'
|
||||
|
||||
const client = vi.hoisted(() => ({ get: vi.fn() }))
|
||||
vi.mock('../client', () => ({ default: client }))
|
||||
|
||||
beforeEach(() => {
|
||||
client.get.mockReset().mockResolvedValue({ data: { header_rules: [], body_rules: [], response_header_rules: [] } })
|
||||
})
|
||||
|
||||
describe('endpoint rule reveal API', () => {
|
||||
it('uses the scoped route and forwards cancellation', async () => {
|
||||
const controller = new AbortController()
|
||||
await revealEndpointRules('endpoint/with?reserved', controller.signal)
|
||||
expect(client.get).toHaveBeenCalledWith(
|
||||
'/api/admin/endpoints/endpoint%2Fwith%3Freserved/rules/reveal',
|
||||
{ signal: controller.signal },
|
||||
)
|
||||
})
|
||||
|
||||
it('fetches each reveal without retaining a cached response', async () => {
|
||||
await revealEndpointRules('endpoint-1')
|
||||
await revealEndpointRules('endpoint-1')
|
||||
expect(client.get).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
@@ -215,7 +215,18 @@ export const adminWalletApi = {
|
||||
credited_at: string | null
|
||||
}
|
||||
}> {
|
||||
const response = await apiClient.post(`/api/admin/wallets/${walletId}/recharge`, payload)
|
||||
const response = await apiClient.post<{
|
||||
wallet: AdminWallet
|
||||
payment_order: {
|
||||
id: string
|
||||
order_no: string
|
||||
amount_usd: number
|
||||
payment_method: string
|
||||
status: string
|
||||
created_at: string
|
||||
credited_at: string | null
|
||||
}
|
||||
}>(`/api/admin/wallets/${walletId}/recharge`, payload)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -223,7 +234,10 @@ export const adminWalletApi = {
|
||||
wallet: AdminWallet
|
||||
transaction: WalletTransaction
|
||||
}> {
|
||||
const response = await apiClient.post(`/api/admin/wallets/${walletId}/adjust`, payload)
|
||||
const response = await apiClient.post<{
|
||||
wallet: AdminWallet
|
||||
transaction: WalletTransaction
|
||||
}>(`/api/admin/wallets/${walletId}/adjust`, payload)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -232,7 +246,11 @@ export const adminWalletApi = {
|
||||
refund: RefundRequest
|
||||
transaction: WalletTransaction
|
||||
}> {
|
||||
const response = await apiClient.post(
|
||||
const response = await apiClient.post<{
|
||||
wallet: AdminWallet
|
||||
refund: RefundRequest
|
||||
transaction: WalletTransaction
|
||||
}>(
|
||||
`/api/admin/wallets/${walletId}/refunds/${refundId}/process`,
|
||||
{}
|
||||
)
|
||||
@@ -244,7 +262,11 @@ export const adminWalletApi = {
|
||||
refund: RefundRequest
|
||||
transaction: WalletTransaction | null
|
||||
}> {
|
||||
const response = await apiClient.post(
|
||||
const response = await apiClient.post<{
|
||||
wallet: AdminWallet
|
||||
refund: RefundRequest
|
||||
transaction: WalletTransaction | null
|
||||
}>(
|
||||
`/api/admin/wallets/${walletId}/refunds/${refundId}/fail`,
|
||||
payload
|
||||
)
|
||||
@@ -256,7 +278,7 @@ export const adminWalletApi = {
|
||||
refundId: string,
|
||||
payload: RefundCompleteRequest
|
||||
): Promise<{ refund: RefundRequest }> {
|
||||
const response = await apiClient.post(
|
||||
const response = await apiClient.post<{ refund: RefundRequest }>(
|
||||
`/api/admin/wallets/${walletId}/refunds/${refundId}/complete`,
|
||||
payload
|
||||
)
|
||||
|
||||
@@ -8,6 +8,11 @@ import type { ApiKeyInstallSession, InstallSessionTargetSystem, InstallTargetCli
|
||||
const SYSTEM_DATA_IMPORT_TIMEOUT_MS = 10 * 60 * 1000
|
||||
const ALL_SYSTEM_CONFIGS_CACHE_KEY = 'admin:system:configs'
|
||||
|
||||
export interface AdminTimeSeriesPoint extends Record<string, unknown> {
|
||||
date: string
|
||||
total_cost: number
|
||||
}
|
||||
|
||||
export interface AdminSystemConfigItem {
|
||||
key: string
|
||||
value: unknown
|
||||
@@ -1594,12 +1599,12 @@ export const adminApi = {
|
||||
provider_name?: string
|
||||
},
|
||||
options?: AdminAnalyticsRequestOptions
|
||||
): Promise<Array<Record<string, unknown>>> {
|
||||
): Promise<AdminTimeSeriesPoint[]> {
|
||||
const cacheKey = buildCacheKey('admin:stats:time-series', params)
|
||||
return cachedRequest(
|
||||
cacheKey,
|
||||
async () => {
|
||||
const response = await apiClient.get<Array<Record<string, unknown>>>('/api/admin/stats/time-series', { params })
|
||||
const response = await apiClient.get<AdminTimeSeriesPoint[]>('/api/admin/stats/time-series', { params })
|
||||
return response.data
|
||||
},
|
||||
options?.skipCache ? 0 : 20 * 1000
|
||||
|
||||
@@ -57,61 +57,61 @@ export const announcementApi = {
|
||||
limit?: number
|
||||
offset?: number
|
||||
}): Promise<AnnouncementListResponse> {
|
||||
const response = await apiClient.get('/api/announcements', { params })
|
||||
const response = await apiClient.get<AnnouncementListResponse>('/api/announcements', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 获取当前有效的公告
|
||||
async getActiveAnnouncements(): Promise<AnnouncementListResponse> {
|
||||
const response = await apiClient.get('/api/announcements/active')
|
||||
const response = await apiClient.get<AnnouncementListResponse>('/api/announcements/active')
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 获取单个公告
|
||||
async getAnnouncement(id: string): Promise<Announcement> {
|
||||
const response = await apiClient.get(`/api/announcements/${id}`)
|
||||
const response = await apiClient.get<Announcement>(`/api/announcements/${id}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 标记公告为已读
|
||||
async markAsRead(id: string): Promise<{ message: string }> {
|
||||
const response = await apiClient.patch(`/api/announcements/${id}/read-status`)
|
||||
const response = await apiClient.patch<{ message: string }>(`/api/announcements/${id}/read-status`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 标记所有公告为已读
|
||||
async markAllAsRead(): Promise<{ message: string }> {
|
||||
const response = await apiClient.post('/api/announcements/read-all')
|
||||
const response = await apiClient.post<{ message: string }>('/api/announcements/read-all')
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 获取未读公告数量
|
||||
async getUnreadCount(): Promise<{ unread_count: number }> {
|
||||
const response = await apiClient.get('/api/announcements/users/me/unread-count')
|
||||
const response = await apiClient.get<{ unread_count: number }>('/api/announcements/users/me/unread-count')
|
||||
return response.data
|
||||
},
|
||||
|
||||
async getRequiredUnreadAnnouncements(): Promise<AnnouncementListResponse> {
|
||||
const response = await apiClient.get('/api/announcements/users/me/required-unread')
|
||||
const response = await apiClient.get<AnnouncementListResponse>('/api/announcements/users/me/required-unread')
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 管理员方法
|
||||
// 创建公告
|
||||
async createAnnouncement(data: CreateAnnouncementRequest): Promise<{ id: string; title: string; message: string }> {
|
||||
const response = await apiClient.post('/api/announcements', data)
|
||||
const response = await apiClient.post<{ id: string; title: string; message: string }>('/api/announcements', data)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 更新公告
|
||||
async updateAnnouncement(id: string, data: UpdateAnnouncementRequest): Promise<{ message: string }> {
|
||||
const response = await apiClient.put(`/api/announcements/${id}`, data)
|
||||
const response = await apiClient.put<{ message: string }>(`/api/announcements/${id}`, data)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 删除公告
|
||||
async deleteAnnouncement(id: string): Promise<{ message: string }> {
|
||||
const response = await apiClient.delete(`/api/announcements/${id}`)
|
||||
const response = await apiClient.delete<{ message: string }>(`/api/announcements/${id}`)
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,27 +180,27 @@ export const asyncTasksApi = {
|
||||
|
||||
const query = searchParams.toString()
|
||||
const url = query ? `/api/admin/tasks?${query}` : '/api/admin/tasks'
|
||||
const response = await apiClient.get(url)
|
||||
const response = await apiClient.get<AsyncTaskListResponse>(url)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async getStats(): Promise<AsyncTaskStatsResponse> {
|
||||
const response = await apiClient.get('/api/admin/tasks/stats')
|
||||
const response = await apiClient.get<AsyncTaskStatsResponse>('/api/admin/tasks/stats')
|
||||
return response.data
|
||||
},
|
||||
|
||||
async getDetail(taskId: string): Promise<AsyncTaskDetail> {
|
||||
const response = await apiClient.get(`/api/admin/tasks/${taskId}`)
|
||||
const response = await apiClient.get<AsyncTaskDetail>(`/api/admin/tasks/${taskId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async getEvents(taskId: string): Promise<{ items: AsyncTaskEvent[] }> {
|
||||
const response = await apiClient.get(`/api/admin/tasks/${taskId}/events`)
|
||||
const response = await apiClient.get<{ items: AsyncTaskEvent[] }>(`/api/admin/tasks/${taskId}/events`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async cancel(taskId: string): Promise<{ id: string; status: string; message: string }> {
|
||||
const response = await apiClient.post(`/api/admin/tasks/${taskId}/cancel`)
|
||||
const response = await apiClient.post<{ id: string; status: string; message: string }>(`/api/admin/tasks/${taskId}/cancel`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -213,7 +213,7 @@ export const asyncTasksApi = {
|
||||
},
|
||||
|
||||
async trigger(taskKey: string, payload: Record<string, unknown> = {}): Promise<{ run_id: string; status: string }> {
|
||||
const response = await apiClient.post(`/api/admin/tasks/${taskKey}/trigger`, payload)
|
||||
const response = await apiClient.post<{ run_id: string; status: string }>(`/api/admin/tasks/${taskKey}/trigger`, payload)
|
||||
return response.data
|
||||
},
|
||||
}
|
||||
|
||||
@@ -57,13 +57,13 @@ export const auditApi = {
|
||||
limit?: number
|
||||
offset?: number
|
||||
}): Promise<AuditLogsResponse> {
|
||||
const response = await apiClient.get('/api/monitoring/my-audit-logs', { params: filters })
|
||||
const response = await apiClient.get<Record<string, unknown>>('/api/monitoring/my-audit-logs', { params: filters })
|
||||
return normalizeAuditResponse(response.data)
|
||||
},
|
||||
|
||||
// 获取所有审计日志 (管理员)
|
||||
async getAuditLogs(filters?: AuditFilters): Promise<AuditLogsResponse> {
|
||||
const response = await apiClient.get('/api/admin/monitoring/audit-logs', { params: filters })
|
||||
const response = await apiClient.get<Record<string, unknown>>('/api/admin/monitoring/audit-logs', { params: filters })
|
||||
return normalizeAuditResponse(response.data)
|
||||
},
|
||||
|
||||
@@ -72,7 +72,10 @@ export const auditApi = {
|
||||
activities: AuditLog[]
|
||||
count: number
|
||||
}> {
|
||||
const response = await apiClient.get('/api/admin/monitoring/suspicious-activities', {
|
||||
const response = await apiClient.get<{
|
||||
activities: AuditLog[]
|
||||
count: number
|
||||
}>('/api/admin/monitoring/suspicious-activities', {
|
||||
params: { hours, limit }
|
||||
})
|
||||
return response.data
|
||||
@@ -83,7 +86,10 @@ export const auditApi = {
|
||||
analysis: Record<string, unknown>
|
||||
recommendations: string[]
|
||||
}> {
|
||||
const response = await apiClient.get(`/api/admin/monitoring/user-behavior/${userId}`, {
|
||||
const response = await apiClient.get<{
|
||||
analysis: Record<string, unknown>
|
||||
recommendations: string[]
|
||||
}>(`/api/admin/monitoring/user-behavior/${userId}`, {
|
||||
params: { days }
|
||||
})
|
||||
return response.data
|
||||
|
||||
+21
-15
@@ -89,7 +89,7 @@ export const cacheApi = {
|
||||
* 获取缓存统计信息
|
||||
*/
|
||||
async getStats(): Promise<CacheStats> {
|
||||
const response = await api.get('/api/admin/monitoring/cache/stats')
|
||||
const response = await api.get<{ data: CacheStats }>('/api/admin/monitoring/cache/stats')
|
||||
return response.data.data
|
||||
},
|
||||
|
||||
@@ -97,7 +97,7 @@ export const cacheApi = {
|
||||
* 获取缓存配置
|
||||
*/
|
||||
async getConfig(): Promise<CacheConfig> {
|
||||
const response = await api.get('/api/admin/monitoring/cache/config')
|
||||
const response = await api.get<{ data: CacheConfig }>('/api/admin/monitoring/cache/config')
|
||||
return response.data.data
|
||||
},
|
||||
|
||||
@@ -107,7 +107,7 @@ export const cacheApi = {
|
||||
* @param userIdentifier 用户标识符,支持:用户名、邮箱、User UUID、API Key ID
|
||||
*/
|
||||
async getUserAffinity(userIdentifier: string): Promise<UserAffinity[] | null> {
|
||||
const response = await api.get(`/api/admin/monitoring/cache/affinity/${userIdentifier}`)
|
||||
const response = await api.get<{ status: string; affinities: UserAffinity[] }>(`/api/admin/monitoring/cache/affinity/${userIdentifier}`)
|
||||
if (response.data.status === 'not_found') {
|
||||
return null
|
||||
}
|
||||
@@ -151,7 +151,7 @@ export const cacheApi = {
|
||||
* 清除所有缓存
|
||||
*/
|
||||
async clearAllCache(): Promise<{ count: number }> {
|
||||
const response = await api.delete('/api/admin/monitoring/cache')
|
||||
const response = await api.delete<{ count: number }>('/api/admin/monitoring/cache')
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -159,7 +159,7 @@ export const cacheApi = {
|
||||
* 清除指定Provider的所有缓存
|
||||
*/
|
||||
async clearProviderCache(providerId: string): Promise<{ count: number; provider_id: string }> {
|
||||
const response = await api.delete(`/api/admin/monitoring/cache/providers/${providerId}`)
|
||||
const response = await api.delete<{ count: number; provider_id: string }>(`/api/admin/monitoring/cache/providers/${providerId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -167,7 +167,13 @@ export const cacheApi = {
|
||||
* 获取缓存亲和性列表
|
||||
*/
|
||||
async listAffinities(keyword?: string): Promise<AffinityListResponse> {
|
||||
const response = await api.get('/api/admin/monitoring/cache/affinities', {
|
||||
const response = await api.get<{
|
||||
data?: {
|
||||
items?: UserAffinity[]
|
||||
meta?: { total?: number }
|
||||
matched_user_id?: string | null
|
||||
}
|
||||
}>('/api/admin/monitoring/cache/affinities', {
|
||||
params: keyword ? { keyword } : undefined
|
||||
})
|
||||
const data = response.data.data ?? {}
|
||||
@@ -212,7 +218,7 @@ export const redisCacheApi = {
|
||||
* 获取 Redis 缓存分类概览
|
||||
*/
|
||||
async getCategories(): Promise<RedisCacheCategoriesResponse> {
|
||||
const response = await api.get('/api/admin/monitoring/cache/redis-keys')
|
||||
const response = await api.get<{ data: RedisCacheCategoriesResponse }>('/api/admin/monitoring/cache/redis-keys')
|
||||
return response.data.data
|
||||
},
|
||||
|
||||
@@ -220,7 +226,7 @@ export const redisCacheApi = {
|
||||
* 清除指定分类的 Redis 缓存
|
||||
*/
|
||||
async clearCategory(category: string): Promise<{ status: string; message: string; category: string; deleted_count: number }> {
|
||||
const response = await api.delete(`/api/admin/monitoring/cache/redis-keys/${category}`)
|
||||
const response = await api.delete<{ status: string; message: string; category: string; deleted_count: number }>(`/api/admin/monitoring/cache/redis-keys/${category}`)
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
@@ -308,7 +314,7 @@ export const cacheAnalysisApi = {
|
||||
api_key_id?: string
|
||||
hours?: number
|
||||
}): Promise<TTLAnalysisResponse> {
|
||||
const response = await api.get('/api/admin/usage/cache-affinity/ttl-analysis', { params })
|
||||
const response = await api.get<TTLAnalysisResponse>('/api/admin/usage/cache-affinity/ttl-analysis', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -320,7 +326,7 @@ export const cacheAnalysisApi = {
|
||||
api_key_id?: string
|
||||
hours?: number
|
||||
}): Promise<CacheHitAnalysisResponse> {
|
||||
const response = await api.get('/api/admin/usage/cache-affinity/hit-analysis', { params })
|
||||
const response = await api.get<CacheHitAnalysisResponse>('/api/admin/usage/cache-affinity/hit-analysis', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -339,7 +345,7 @@ export const cacheAnalysisApi = {
|
||||
return cachedRequest(
|
||||
cacheKey,
|
||||
async () => {
|
||||
const response = await api.get('/api/admin/usage/cache-affinity/interval-timeline', { params })
|
||||
const response = await api.get<IntervalTimelineResponse>('/api/admin/usage/cache-affinity/interval-timeline', { params })
|
||||
return response.data
|
||||
},
|
||||
30000
|
||||
@@ -408,7 +414,7 @@ export const modelMappingCacheApi = {
|
||||
* 获取模型映射缓存统计
|
||||
*/
|
||||
async getStats(): Promise<ModelMappingCacheStats> {
|
||||
const response = await api.get('/api/admin/monitoring/cache/model-mapping/stats')
|
||||
const response = await api.get<{ data: ModelMappingCacheStats }>('/api/admin/monitoring/cache/model-mapping/stats')
|
||||
return response.data.data
|
||||
},
|
||||
|
||||
@@ -416,7 +422,7 @@ export const modelMappingCacheApi = {
|
||||
* 清除所有模型映射缓存
|
||||
*/
|
||||
async clearAll(): Promise<ClearModelMappingCacheResponse> {
|
||||
const response = await api.delete('/api/admin/monitoring/cache/model-mapping')
|
||||
const response = await api.delete<ClearModelMappingCacheResponse>('/api/admin/monitoring/cache/model-mapping')
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -424,7 +430,7 @@ export const modelMappingCacheApi = {
|
||||
* 清除指定模型名称的映射缓存
|
||||
*/
|
||||
async clearByName(modelName: string): Promise<ClearModelMappingCacheResponse> {
|
||||
const response = await api.delete(`/api/admin/monitoring/cache/model-mapping/${encodeURIComponent(modelName)}`)
|
||||
const response = await api.delete<ClearModelMappingCacheResponse>(`/api/admin/monitoring/cache/model-mapping/${encodeURIComponent(modelName)}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -432,7 +438,7 @@ export const modelMappingCacheApi = {
|
||||
* 清除指定 Provider 和 GlobalModel 的映射缓存
|
||||
*/
|
||||
async clearProviderModel(providerId: string, globalModelId: string): Promise<ClearModelMappingCacheResponse> {
|
||||
const response = await api.delete(`/api/admin/monitoring/cache/model-mapping/provider/${providerId}/${globalModelId}`)
|
||||
const response = await api.delete<ClearModelMappingCacheResponse>(`/api/admin/monitoring/cache/model-mapping/provider/${providerId}/${globalModelId}`)
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ class ApiClient {
|
||||
/**
|
||||
* 处理响应错误
|
||||
*/
|
||||
private async handleResponseError(error: unknown): Promise<never> {
|
||||
private async handleResponseError(error: unknown): Promise<AxiosResponse> {
|
||||
// 请求被取消
|
||||
if (axios.isCancel(error)) {
|
||||
return Promise.reject(error)
|
||||
|
||||
@@ -278,14 +278,14 @@ export interface RequestDetail {
|
||||
end_to_end_first_byte_time_ms?: number | null
|
||||
created_at: string
|
||||
updated_at?: string | null
|
||||
request_headers?: Record<string, unknown>
|
||||
request_body?: Record<string, unknown>
|
||||
provider_request_headers?: Record<string, unknown>
|
||||
provider_request_body?: Record<string, unknown>
|
||||
response_headers?: Record<string, unknown>
|
||||
client_response_headers?: Record<string, unknown>
|
||||
response_body?: Record<string, unknown>
|
||||
client_response_body?: Record<string, unknown>
|
||||
request_headers?: Record<string, unknown> | null
|
||||
request_body?: Record<string, unknown> | null
|
||||
provider_request_headers?: Record<string, unknown> | null
|
||||
provider_request_body?: Record<string, unknown> | null
|
||||
response_headers?: Record<string, unknown> | null
|
||||
client_response_headers?: Record<string, unknown> | null
|
||||
response_body?: Record<string, unknown> | null
|
||||
client_response_body?: Record<string, unknown> | null
|
||||
has_request_body?: boolean
|
||||
has_provider_request_body?: boolean
|
||||
has_response_body?: boolean
|
||||
|
||||
@@ -17,7 +17,13 @@ export async function toggleAdaptiveMode(
|
||||
rpm_limit: number | null
|
||||
effective_limit: number | null
|
||||
}> {
|
||||
const response = await client.patch(`/api/admin/adaptive/keys/${keyId}/mode`, data)
|
||||
const response = await client.patch<{
|
||||
message: string
|
||||
key_id: string
|
||||
is_adaptive: boolean
|
||||
rpm_limit: number | null
|
||||
effective_limit: number | null
|
||||
}>(`/api/admin/adaptive/keys/${keyId}/mode`, data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -34,7 +40,13 @@ export async function setRpmLimit(
|
||||
rpm_limit: number
|
||||
previous_mode: string
|
||||
}> {
|
||||
const response = await client.patch(`/api/admin/adaptive/keys/${keyId}/limit`, null, {
|
||||
const response = await client.patch<{
|
||||
message: string
|
||||
key_id: string
|
||||
is_adaptive: boolean
|
||||
rpm_limit: number
|
||||
previous_mode: string
|
||||
}>(`/api/admin/adaptive/keys/${keyId}/limit`, null, {
|
||||
params: { limit }
|
||||
})
|
||||
return response.data
|
||||
@@ -44,7 +56,7 @@ export async function setRpmLimit(
|
||||
* 获取 Key 的自适应统计
|
||||
*/
|
||||
export async function getAdaptiveStats(keyId: string): Promise<AdaptiveStatsResponse> {
|
||||
const response = await client.get(`/api/admin/adaptive/keys/${keyId}/stats`)
|
||||
const response = await client.get<AdaptiveStatsResponse>(`/api/admin/adaptive/keys/${keyId}/stats`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -52,6 +64,6 @@ export async function getAdaptiveStats(keyId: string): Promise<AdaptiveStatsResp
|
||||
* 重置 Key 的学习状态
|
||||
*/
|
||||
export async function resetAdaptiveLearning(keyId: string): Promise<{ message: string; key_id: string }> {
|
||||
const response = await client.delete(`/api/admin/adaptive/keys/${keyId}/learning`)
|
||||
const response = await client.delete<{ message: string; key_id: string }>(`/api/admin/adaptive/keys/${keyId}/learning`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
import client from '../client'
|
||||
import type { ProviderEndpoint, ProxyConfig, HeaderRule, BodyRule, FormatAcceptanceConfig } from './types'
|
||||
|
||||
export interface ProviderEndpointRules {
|
||||
header_rules: HeaderRule[]
|
||||
body_rules: BodyRule[]
|
||||
response_header_rules: HeaderRule[]
|
||||
}
|
||||
|
||||
export async function revealEndpointRules(endpointId: string, signal?: AbortSignal): Promise<ProviderEndpointRules> {
|
||||
const response = await client.get<ProviderEndpointRules>(
|
||||
`/api/admin/endpoints/${encodeURIComponent(endpointId)}/rules/reveal`,
|
||||
{ signal },
|
||||
)
|
||||
return response.data
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定 Provider 的所有 Endpoints
|
||||
*/
|
||||
export async function getProviderEndpoints(providerId: string): Promise<ProviderEndpoint[]> {
|
||||
const response = await client.get(`/api/admin/endpoints/providers/${providerId}/endpoints`)
|
||||
const response = await client.get<ProviderEndpoint[]>(`/api/admin/endpoints/providers/${providerId}/endpoints`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -13,7 +27,7 @@ export async function getProviderEndpoints(providerId: string): Promise<Provider
|
||||
* 获取 Endpoint 详情
|
||||
*/
|
||||
export async function getEndpoint(endpointId: string): Promise<ProviderEndpoint> {
|
||||
const response = await client.get(`/api/admin/endpoints/${endpointId}`)
|
||||
const response = await client.get<ProviderEndpoint>(`/api/admin/endpoints/${endpointId}`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -36,7 +50,7 @@ export async function createEndpoint(
|
||||
format_acceptance_config?: FormatAcceptanceConfig | null
|
||||
}
|
||||
): Promise<ProviderEndpoint> {
|
||||
const response = await client.post(`/api/admin/endpoints/providers/${providerId}/endpoints`, data)
|
||||
const response = await client.post<ProviderEndpoint>(`/api/admin/endpoints/providers/${providerId}/endpoints`, data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -57,7 +71,7 @@ export async function updateEndpoint(
|
||||
format_acceptance_config: FormatAcceptanceConfig | null
|
||||
}>
|
||||
): Promise<ProviderEndpoint> {
|
||||
const response = await client.put(`/api/admin/endpoints/${endpointId}`, data)
|
||||
const response = await client.put<ProviderEndpoint>(`/api/admin/endpoints/${endpointId}`, data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -65,7 +79,7 @@ export async function updateEndpoint(
|
||||
* 删除 Endpoint
|
||||
*/
|
||||
export async function deleteEndpoint(endpointId: string): Promise<{ message: string; affected_keys_count: number }> {
|
||||
const response = await client.delete(`/api/admin/endpoints/${endpointId}`)
|
||||
const response = await client.delete<{ message: string; affected_keys_count: number }>(`/api/admin/endpoints/${endpointId}`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -75,6 +89,6 @@ export async function deleteEndpoint(endpointId: string): Promise<{ message: str
|
||||
export async function getDefaultBodyRules(apiFormat: string, providerType?: string): Promise<{ api_format: string; body_rules: BodyRule[] }> {
|
||||
const params: Record<string, string> = {}
|
||||
if (providerType) params.provider_type = providerType
|
||||
const response = await client.get(`/api/admin/endpoints/defaults/${apiFormat}/body-rules`, { params })
|
||||
const response = await client.get<{ api_format: string; body_rules: BodyRule[] }>(`/api/admin/endpoints/defaults/${apiFormat}/body-rules`, { params })
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import type {
|
||||
|
||||
// 重新导出路由相关类型供外部使用
|
||||
export type {
|
||||
GlobalModelResponse,
|
||||
RoutingKeyInfo,
|
||||
RoutingEndpointInfo,
|
||||
RoutingModelMapping,
|
||||
@@ -37,7 +38,7 @@ export async function getGlobalModels(params?: {
|
||||
return cachedRequest(
|
||||
key,
|
||||
async () => {
|
||||
const response = await client.get('/api/admin/models/global', { params })
|
||||
const response = await client.get<GlobalModelListResponse>('/api/admin/models/global', { params })
|
||||
return response.data
|
||||
},
|
||||
cacheTtlMs,
|
||||
@@ -49,7 +50,7 @@ export async function getGlobalModels(params?: {
|
||||
*/
|
||||
export async function getGlobalModel(id: string): Promise<GlobalModelWithStats> {
|
||||
return dedupedRequest(`global-models:detail:${id}`, async () => {
|
||||
const response = await client.get(`/api/admin/models/global/${id}`)
|
||||
const response = await client.get<GlobalModelWithStats>(`/api/admin/models/global/${id}`)
|
||||
return response.data
|
||||
})
|
||||
}
|
||||
@@ -58,7 +59,7 @@ export async function getGlobalModel(id: string): Promise<GlobalModelWithStats>
|
||||
* 创建 GlobalModel
|
||||
*/
|
||||
export async function createGlobalModel(data: GlobalModelCreate): Promise<GlobalModelResponse> {
|
||||
const response = await client.post('/api/admin/models/global', data)
|
||||
const response = await client.post<GlobalModelResponse>('/api/admin/models/global', data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -69,7 +70,7 @@ export async function updateGlobalModel(
|
||||
id: string,
|
||||
data: GlobalModelUpdate
|
||||
): Promise<GlobalModelResponse> {
|
||||
const response = await client.patch(`/api/admin/models/global/${id}`, data)
|
||||
const response = await client.patch<GlobalModelResponse>(`/api/admin/models/global/${id}`, data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -89,7 +90,7 @@ export async function deleteGlobalModel(
|
||||
export async function batchDeleteGlobalModels(
|
||||
ids: string[]
|
||||
): Promise<{ success_count: number; failed: Array<{ id: string; error: string }> }> {
|
||||
const response = await client.post('/api/admin/models/global/batch-delete', { ids })
|
||||
const response = await client.post<{ success_count: number; failed: Array<{ id: string; error: string }> }>('/api/admin/models/global/batch-delete', { ids })
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -113,7 +114,17 @@ export async function batchAssignToProviders(
|
||||
error: string
|
||||
}>
|
||||
}> {
|
||||
const response = await client.post(
|
||||
const response = await client.post<{
|
||||
success: Array<{
|
||||
provider_id: string
|
||||
provider_name: string
|
||||
model_id?: string
|
||||
}>
|
||||
errors: Array<{
|
||||
provider_id: string
|
||||
error: string
|
||||
}>
|
||||
}>(
|
||||
`/api/admin/models/global/${globalModelId}/assign-to-providers`,
|
||||
data
|
||||
)
|
||||
@@ -128,7 +139,7 @@ export async function getGlobalModelProviders(globalModelId: string): Promise<{
|
||||
total: number
|
||||
}> {
|
||||
return dedupedRequest(`global-models:providers:${globalModelId}`, async () => {
|
||||
const response = await client.get(
|
||||
const response = await client.get<{ providers: ModelCatalogProviderDetail[]; total: number }>(
|
||||
`/api/admin/models/global/${globalModelId}/providers`
|
||||
)
|
||||
return response.data
|
||||
@@ -141,7 +152,7 @@ export async function getGlobalModelProviders(globalModelId: string): Promise<{
|
||||
export async function getGlobalModelRoutingPreview(
|
||||
globalModelId: string
|
||||
): Promise<ModelRoutingPreviewResponse> {
|
||||
const response = await client.get(
|
||||
const response = await client.get<ModelRoutingPreviewResponse>(
|
||||
`/api/admin/models/global/${globalModelId}/routing`
|
||||
)
|
||||
return response.data
|
||||
|
||||
@@ -14,7 +14,7 @@ import type {
|
||||
* 获取健康状态摘要
|
||||
*/
|
||||
export async function getHealthSummary(): Promise<HealthSummary> {
|
||||
const response = await client.get('/api/admin/endpoints/health/summary')
|
||||
const response = await client.get<HealthSummary>('/api/admin/endpoints/health/summary')
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export async function getHealthSummary(): Promise<HealthSummary> {
|
||||
* 获取 Endpoint 健康状态
|
||||
*/
|
||||
export async function getEndpointHealth(endpointId: string): Promise<HealthStatus> {
|
||||
const response = await client.get(`/api/admin/endpoints/health/endpoint/${endpointId}`)
|
||||
const response = await client.get<HealthStatus>(`/api/admin/endpoints/health/endpoint/${endpointId}`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export async function getEndpointHealth(endpointId: string): Promise<HealthStatu
|
||||
* 获取 Key 健康状态
|
||||
*/
|
||||
export async function getKeyHealth(keyId: string): Promise<HealthStatus> {
|
||||
const response = await client.get(`/api/admin/endpoints/health/key/${keyId}`)
|
||||
const response = await client.get<HealthStatus>(`/api/admin/endpoints/health/key/${keyId}`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -48,7 +48,15 @@ export async function recoverKeyHealth(keyId: string, apiFormat?: string): Promi
|
||||
is_active: boolean
|
||||
}
|
||||
}> {
|
||||
const response = await client.patch(`/api/admin/endpoints/health/keys/${keyId}`, null, {
|
||||
const response = await client.patch<{
|
||||
message: string
|
||||
details: {
|
||||
api_format?: string
|
||||
health_score: number
|
||||
circuit_breaker_open: boolean
|
||||
is_active: boolean
|
||||
}
|
||||
}>(`/api/admin/endpoints/health/keys/${keyId}`, null, {
|
||||
params: apiFormat ? { api_format: apiFormat } : undefined
|
||||
})
|
||||
return response.data
|
||||
@@ -66,7 +74,15 @@ export async function recoverAllKeysHealth(): Promise<{
|
||||
endpoint_id: string
|
||||
}>
|
||||
}> {
|
||||
const response = await client.patch('/api/admin/endpoints/health/keys')
|
||||
const response = await client.patch<{
|
||||
message: string
|
||||
recovered_count: number
|
||||
recovered_keys: Array<{
|
||||
key_id: string
|
||||
key_name: string
|
||||
endpoint_id: string
|
||||
}>
|
||||
}>('/api/admin/endpoints/health/keys')
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -77,7 +93,7 @@ export async function getEndpointStatusMonitor(params?: {
|
||||
lookback_hours?: number
|
||||
per_format_limit?: number
|
||||
}): Promise<EndpointStatusMonitorResponse> {
|
||||
const response = await client.get('/api/admin/endpoints/health/api-formats', {
|
||||
const response = await client.get<EndpointStatusMonitorResponse>('/api/admin/endpoints/health/api-formats', {
|
||||
params
|
||||
})
|
||||
return response.data
|
||||
@@ -90,7 +106,7 @@ export async function getPublicEndpointStatusMonitor(params?: {
|
||||
lookback_hours?: number
|
||||
per_format_limit?: number
|
||||
}): Promise<PublicEndpointStatusMonitorResponse> {
|
||||
const response = await client.get('/api/public/health/api-formats', {
|
||||
const response = await client.get<PublicEndpointStatusMonitorResponse>('/api/public/health/api-formats', {
|
||||
params
|
||||
})
|
||||
return response.data
|
||||
@@ -104,7 +120,7 @@ export async function getModelStatusMonitor(params?: {
|
||||
model_limit?: number
|
||||
per_model_limit?: number
|
||||
}): Promise<ModelStatusMonitorResponse> {
|
||||
const response = await client.get('/api/admin/endpoints/health/models', {
|
||||
const response = await client.get<ModelStatusMonitorResponse>('/api/admin/endpoints/health/models', {
|
||||
params
|
||||
})
|
||||
return response.data
|
||||
@@ -118,7 +134,7 @@ export async function getPublicModelStatusMonitor(params?: {
|
||||
model_limit?: number
|
||||
per_model_limit?: number
|
||||
}): Promise<ModelStatusMonitorResponse> {
|
||||
const response = await client.get('/api/public/health/models', {
|
||||
const response = await client.get<ModelStatusMonitorResponse>('/api/public/health/models', {
|
||||
params
|
||||
})
|
||||
return response.data
|
||||
@@ -133,7 +149,7 @@ export async function getProviderStatusMonitor(params?: {
|
||||
per_provider_model_limit?: number
|
||||
per_model_limit?: number
|
||||
}): Promise<ProviderStatusMonitorResponse> {
|
||||
const response = await client.get('/api/admin/endpoints/health/providers', {
|
||||
const response = await client.get<ProviderStatusMonitorResponse>('/api/admin/endpoints/health/providers', {
|
||||
params
|
||||
})
|
||||
return response.data
|
||||
@@ -146,7 +162,7 @@ export async function getHealthRelatedMonitor(params: {
|
||||
related_limit?: number
|
||||
per_item_limit?: number
|
||||
}): Promise<HealthRelatedMonitorResponse> {
|
||||
const response = await client.get('/api/admin/endpoints/health/related', {
|
||||
const response = await client.get<HealthRelatedMonitorResponse>('/api/admin/endpoints/health/related', {
|
||||
params
|
||||
})
|
||||
return response.data
|
||||
@@ -159,7 +175,7 @@ export async function getPublicHealthRelatedMonitor(params: {
|
||||
related_limit?: number
|
||||
per_item_limit?: number
|
||||
}): Promise<HealthRelatedMonitorResponse> {
|
||||
const response = await client.get('/api/public/health/related', {
|
||||
const response = await client.get<HealthRelatedMonitorResponse>('/api/public/health/related', {
|
||||
params
|
||||
})
|
||||
return response.data
|
||||
|
||||
@@ -37,7 +37,7 @@ export interface ModelCapabilitiesResponse {
|
||||
* 获取所有能力定义
|
||||
*/
|
||||
export async function getAllCapabilities(): Promise<CapabilityDefinition[]> {
|
||||
const response = await client.get('/api/capabilities')
|
||||
const response = await client.get<{ capabilities: CapabilityDefinition[] }>('/api/capabilities')
|
||||
return response.data.capabilities
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ export async function getAllCapabilities(): Promise<CapabilityDefinition[]> {
|
||||
* 获取用户可配置的能力列表
|
||||
*/
|
||||
export async function getUserConfigurableCapabilities(): Promise<CapabilityDefinition[]> {
|
||||
const response = await client.get('/api/capabilities/user-configurable')
|
||||
const response = await client.get<{ capabilities: CapabilityDefinition[] }>('/api/capabilities/user-configurable')
|
||||
return response.data.capabilities
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export async function getUserConfigurableCapabilities(): Promise<CapabilityDefin
|
||||
* 获取指定模型支持的能力列表
|
||||
*/
|
||||
export async function getModelCapabilities(modelName: string): Promise<ModelCapabilitiesResponse> {
|
||||
const response = await client.get(`/api/capabilities/model/${encodeURIComponent(modelName)}`)
|
||||
const response = await client.get<ModelCapabilitiesResponse>(`/api/capabilities/model/${encodeURIComponent(modelName)}`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ export interface RevealKeyResult {
|
||||
}
|
||||
|
||||
export async function revealEndpointKey(keyId: string): Promise<RevealKeyResult> {
|
||||
const response = await client.get(`/api/admin/endpoints/keys/${keyId}/reveal`)
|
||||
const response = await client.get<RevealKeyResult>(`/api/admin/endpoints/keys/${keyId}/reveal`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ export async function revealEndpointKey(keyId: string): Promise<RevealKeyResult>
|
||||
* 导出 OAuth Key 凭据(扁平 JSON,用于跨实例迁移)
|
||||
*/
|
||||
export async function exportKey(keyId: string): Promise<Record<string, unknown>> {
|
||||
const response = await client.get(`/api/admin/endpoints/keys/${keyId}/export`)
|
||||
const response = await client.get<Record<string, unknown>>(`/api/admin/endpoints/keys/${keyId}/export`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ export async function exportKey(keyId: string): Promise<Record<string, unknown>>
|
||||
* 删除 Key
|
||||
*/
|
||||
export async function deleteEndpointKey(keyId: string): Promise<{ message: string }> {
|
||||
const response = await client.delete(`/api/admin/endpoints/keys/${keyId}`)
|
||||
const response = await client.delete<{ message: string }>(`/api/admin/endpoints/keys/${keyId}`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ export interface BatchDeleteKeysResult {
|
||||
}
|
||||
|
||||
export async function batchDeleteEndpointKeys(ids: string[]): Promise<BatchDeleteKeysResult> {
|
||||
const response = await client.post('/api/admin/endpoints/keys/batch-delete', { ids })
|
||||
const response = await client.post<BatchDeleteKeysResult>('/api/admin/endpoints/keys/batch-delete', { ids })
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ export async function addProviderKey(
|
||||
model_exclude_patterns?: string[] // 模型排除规则
|
||||
}
|
||||
): Promise<EndpointAPIKey> {
|
||||
const response = await client.post(`/api/admin/endpoints/providers/${providerId}/keys`, data)
|
||||
const response = await client.post<EndpointAPIKey>(`/api/admin/endpoints/providers/${providerId}/keys`, data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ export async function updateProviderKey(
|
||||
}>,
|
||||
requestOptions?: KeyRequestOptions,
|
||||
): Promise<EndpointAPIKey> {
|
||||
const response = await client.put(
|
||||
const response = await client.put<EndpointAPIKey>(
|
||||
`/api/admin/endpoints/keys/${keyId}`,
|
||||
data,
|
||||
requestOptions,
|
||||
@@ -263,7 +263,7 @@ export async function updateProviderKey(
|
||||
* 清除 Key 的 OAuth 失效标记
|
||||
*/
|
||||
export async function clearOAuthInvalid(keyId: string): Promise<{ message: string }> {
|
||||
const response = await client.post(`/api/admin/endpoints/keys/${keyId}/clear-oauth-invalid`)
|
||||
const response = await client.post<{ message: string }>(`/api/admin/endpoints/keys/${keyId}/clear-oauth-invalid`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -275,7 +275,11 @@ export async function resetProviderKeyCycleStats(keyId: string): Promise<{
|
||||
reset_at: number
|
||||
windows: number
|
||||
}> {
|
||||
const response = await client.post(`/api/admin/endpoints/keys/${keyId}/reset-cycle-stats`)
|
||||
const response = await client.post<{
|
||||
message: string
|
||||
reset_at: number
|
||||
windows: number
|
||||
}>(`/api/admin/endpoints/keys/${keyId}/reset-cycle-stats`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -311,7 +315,7 @@ export async function refreshProviderQuota(
|
||||
keyIds?: string[],
|
||||
): Promise<RefreshQuotaResult> {
|
||||
const body = keyIds && keyIds.length > 0 ? { key_ids: keyIds } : undefined
|
||||
const response = await client.post(
|
||||
const response = await client.post<RefreshQuotaResult>(
|
||||
`/api/admin/endpoints/providers/${providerId}/refresh-quota`,
|
||||
body,
|
||||
{ timeout: 5 * 60 * 1000 },
|
||||
@@ -350,7 +354,7 @@ export async function consumeCodexResetCredit(
|
||||
keyId: string,
|
||||
payload: ConsumeCodexResetCreditPayload,
|
||||
): Promise<ConsumeCodexResetCreditResult> {
|
||||
const response = await client.post(
|
||||
const response = await client.post<ConsumeCodexResetCreditResult>(
|
||||
`/api/admin/endpoints/keys/${keyId}/codex-reset-credit/consume`,
|
||||
payload,
|
||||
{ timeout: 5 * 60 * 1000 },
|
||||
@@ -383,7 +387,7 @@ export async function batchImportOAuth(
|
||||
credentials: string,
|
||||
proxyNodeId?: string
|
||||
): Promise<BatchImportResult> {
|
||||
const response = await client.post(`/api/admin/provider-oauth/providers/${providerId}/batch-import`, {
|
||||
const response = await client.post<BatchImportResult>(`/api/admin/provider-oauth/providers/${providerId}/batch-import`, {
|
||||
credentials,
|
||||
proxy_node_id: proxyNodeId || undefined,
|
||||
})
|
||||
|
||||
@@ -19,7 +19,7 @@ export async function getProviderModels(
|
||||
limit?: number
|
||||
}
|
||||
): Promise<Model[]> {
|
||||
const response = await client.get(`/api/admin/providers/${providerId}/models`, { params })
|
||||
const response = await client.get<Model[]>(`/api/admin/providers/${providerId}/models`, { params })
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ export async function createModel(
|
||||
providerId: string,
|
||||
data: ModelCreate
|
||||
): Promise<Model> {
|
||||
const response = await client.post(`/api/admin/providers/${providerId}/models`, data)
|
||||
const response = await client.post<Model>(`/api/admin/providers/${providerId}/models`, data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export async function getModel(
|
||||
providerId: string,
|
||||
modelId: string
|
||||
): Promise<Model> {
|
||||
const response = await client.get(`/api/admin/providers/${providerId}/models/${modelId}`)
|
||||
const response = await client.get<Model>(`/api/admin/providers/${providerId}/models/${modelId}`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export async function updateModel(
|
||||
modelId: string,
|
||||
data: ModelUpdate
|
||||
): Promise<Model> {
|
||||
const response = await client.patch(`/api/admin/providers/${providerId}/models/${modelId}`, data)
|
||||
const response = await client.patch<Model>(`/api/admin/providers/${providerId}/models/${modelId}`, data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export async function deleteModel(
|
||||
providerId: string,
|
||||
modelId: string
|
||||
): Promise<{ message: string }> {
|
||||
const response = await client.delete(`/api/admin/providers/${providerId}/models/${modelId}`)
|
||||
const response = await client.delete<{ message: string }>(`/api/admin/providers/${providerId}/models/${modelId}`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ export async function batchCreateModels(
|
||||
providerId: string,
|
||||
modelsData: ModelCreate[]
|
||||
): Promise<Model[]> {
|
||||
const response = await client.post(`/api/admin/providers/${providerId}/models/batch`, modelsData)
|
||||
const response = await client.post<Model[]>(`/api/admin/providers/${providerId}/models/batch`, modelsData)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export async function batchCreateModels(
|
||||
* 获取统一模型目录
|
||||
*/
|
||||
export async function getModelCatalog(): Promise<ModelCatalogResponse> {
|
||||
const response = await client.get('/api/admin/models/catalog')
|
||||
const response = await client.get<ModelCatalogResponse>('/api/admin/models/catalog')
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export async function getModelCatalog(): Promise<ModelCatalogResponse> {
|
||||
export async function getProviderAvailableSourceModels(
|
||||
providerId: string
|
||||
): Promise<ProviderAvailableSourceModelsResponse> {
|
||||
const response = await client.get(`/api/admin/providers/${providerId}/available-source-models`)
|
||||
const response = await client.get<ProviderAvailableSourceModelsResponse>(`/api/admin/providers/${providerId}/available-source-models`)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -114,7 +114,17 @@ export async function batchAssignModelsToProvider(
|
||||
error: string
|
||||
}>
|
||||
}> {
|
||||
const response = await client.post(
|
||||
const response = await client.post<{
|
||||
success: Array<{
|
||||
global_model_id: string
|
||||
global_model_name: string
|
||||
model_id: string
|
||||
}>
|
||||
errors: Array<{
|
||||
global_model_id: string
|
||||
error: string
|
||||
}>
|
||||
}>(
|
||||
`/api/admin/providers/${providerId}/assign-global-models`,
|
||||
{ global_model_ids: globalModelIds }
|
||||
)
|
||||
@@ -137,7 +147,7 @@ export async function importModelsFromUpstream(
|
||||
price_per_request?: number
|
||||
}
|
||||
): Promise<ImportFromUpstreamResponse> {
|
||||
const response = await client.post(
|
||||
const response = await client.post<ImportFromUpstreamResponse>(
|
||||
`/api/admin/providers/${providerId}/import-from-upstream`,
|
||||
{
|
||||
model_ids: modelIds,
|
||||
|
||||
@@ -2,6 +2,7 @@ import client from '../client'
|
||||
import { buildCacheKey, cachedRequest } from '@/utils/cache'
|
||||
import type {
|
||||
AllowedModels,
|
||||
ProviderType,
|
||||
OAuthOrganizationInfo,
|
||||
ProxyConfig,
|
||||
UpstreamMetadata,
|
||||
@@ -77,7 +78,7 @@ export async function resetPoolCost(
|
||||
export interface PoolOverviewItem {
|
||||
provider_id: string
|
||||
provider_name: string
|
||||
provider_type: string
|
||||
provider_type: ProviderType
|
||||
total_keys: number
|
||||
active_keys: number
|
||||
cooldown_count: number
|
||||
@@ -508,7 +509,7 @@ export async function batchActionPoolKeys(
|
||||
providerId: string,
|
||||
body: PoolBatchAction,
|
||||
): Promise<{ affected: number; message: string; task_id?: string }> {
|
||||
const response = await client.post(
|
||||
const response = await client.post<{ affected: number; message: string; task_id?: string }>(
|
||||
`/api/admin/pool/${providerId}/keys/batch-action`,
|
||||
body,
|
||||
{ timeout: POOL_BATCH_ACTION_TIMEOUT_MS },
|
||||
@@ -561,7 +562,7 @@ export async function getPoolBatchDeleteTask(
|
||||
export async function cleanupBannedPoolKeys(
|
||||
providerId: string,
|
||||
): Promise<{ affected: number; message: string }> {
|
||||
const response = await client.post(
|
||||
const response = await client.post<{ affected: number; message: string }>(
|
||||
`/api/admin/pool/${providerId}/keys/cleanup-banned`,
|
||||
undefined,
|
||||
{ timeout: POOL_BATCH_ACTION_TIMEOUT_MS },
|
||||
|
||||
@@ -230,14 +230,14 @@ export function normalizeBatchImportCredentials(text: string): BatchImportCreden
|
||||
}
|
||||
|
||||
export async function refreshProviderOAuth(keyId: string): Promise<ProviderOAuthCompleteResponse> {
|
||||
const resp = await client.post(`/api/admin/provider-oauth/keys/${keyId}/refresh`)
|
||||
const resp = await client.post<ProviderOAuthCompleteResponse>(`/api/admin/provider-oauth/keys/${keyId}/refresh`)
|
||||
return resp.data
|
||||
}
|
||||
|
||||
// Provider-level OAuth (不需要预先创建 key)
|
||||
|
||||
export async function startProviderLevelOAuth(providerId: string): Promise<ProviderOAuthStartResponse> {
|
||||
const resp = await client.post(`/api/admin/provider-oauth/providers/${providerId}/start`)
|
||||
const resp = await client.post<ProviderOAuthStartResponse>(`/api/admin/provider-oauth/providers/${providerId}/start`)
|
||||
return resp.data
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ export async function completeProviderLevelOAuth(
|
||||
providerId: string,
|
||||
data: ProviderOAuthCompleteRequest
|
||||
): Promise<ProviderOAuthCompleteResponseWithKey> {
|
||||
const resp = await client.post(`/api/admin/provider-oauth/providers/${providerId}/complete`, data)
|
||||
const resp = await client.post<ProviderOAuthCompleteResponseWithKey>(`/api/admin/provider-oauth/providers/${providerId}/complete`, data)
|
||||
return resp.data
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ export async function authorizeProviderWithCookie(
|
||||
providerId: string,
|
||||
data: ProviderCookieAuthorizeRequest
|
||||
): Promise<ProviderOAuthCompleteResponseWithKey> {
|
||||
const resp = await client.post(
|
||||
const resp = await client.post<ProviderOAuthCompleteResponseWithKey>(
|
||||
`/api/admin/provider-oauth/providers/${providerId}/cookie-authorize`,
|
||||
data,
|
||||
{ timeout: CLAUDE_COOKIE_AUTHORIZE_TIMEOUT_MS },
|
||||
@@ -265,7 +265,7 @@ export async function startProviderCookieAuthorizeTask(
|
||||
providerId: string,
|
||||
data: ProviderCookieAuthorizeBatchTaskRequest,
|
||||
): Promise<OAuthBatchImportTaskStartResponse> {
|
||||
const resp = await client.post(
|
||||
const resp = await client.post<OAuthBatchImportTaskStartResponse>(
|
||||
`/api/admin/provider-oauth/providers/${providerId}/cookie-authorize/tasks`,
|
||||
data,
|
||||
)
|
||||
@@ -276,7 +276,7 @@ export async function getProviderCookieAuthorizeTaskStatus(
|
||||
providerId: string,
|
||||
taskId: string,
|
||||
): Promise<OAuthBatchImportTaskStatusResponse> {
|
||||
const resp = await client.get(
|
||||
const resp = await client.get<OAuthBatchImportTaskStatusResponse>(
|
||||
`/api/admin/provider-oauth/providers/${providerId}/cookie-authorize/tasks/${taskId}`,
|
||||
)
|
||||
return resp.data
|
||||
@@ -313,7 +313,7 @@ export async function importProviderRefreshToken(
|
||||
headers?: Record<string, string>
|
||||
}
|
||||
): Promise<ProviderOAuthCompleteResponseWithKey> {
|
||||
const resp = await client.post(`/api/admin/provider-oauth/providers/${providerId}/import-refresh-token`, data)
|
||||
const resp = await client.post<ProviderOAuthCompleteResponseWithKey>(`/api/admin/provider-oauth/providers/${providerId}/import-refresh-token`, data)
|
||||
return resp.data
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ export async function startBatchImportOAuthTask(
|
||||
const route = containsAgentIdentityImport(credentials)
|
||||
? 'agent-identity-import/tasks'
|
||||
: 'batch-import/tasks'
|
||||
const resp = await client.post(`/api/admin/provider-oauth/providers/${providerId}/${route}`, {
|
||||
const resp = await client.post<OAuthBatchImportTaskStartResponse>(`/api/admin/provider-oauth/providers/${providerId}/${route}`, {
|
||||
credentials,
|
||||
proxy_node_id: proxyNodeId || undefined,
|
||||
})
|
||||
@@ -339,7 +339,7 @@ export async function getBatchImportOAuthTaskStatus(
|
||||
const route = taskId.startsWith('agent-identity-')
|
||||
? 'agent-identity-import/tasks'
|
||||
: 'batch-import/tasks'
|
||||
const resp = await client.get(`/api/admin/provider-oauth/providers/${providerId}/${route}/${taskId}`)
|
||||
const resp = await client.get<OAuthBatchImportTaskStatusResponse>(`/api/admin/provider-oauth/providers/${providerId}/${route}/${taskId}`)
|
||||
return resp.data
|
||||
}
|
||||
|
||||
@@ -412,7 +412,7 @@ export async function startDeviceAuthorize(
|
||||
providerId: string,
|
||||
data: DeviceAuthorizeRequest
|
||||
): Promise<DeviceAuthorizeResponse> {
|
||||
const resp = await client.post(`/api/admin/provider-oauth/providers/${providerId}/device-authorize`, data)
|
||||
const resp = await client.post<DeviceAuthorizeResponse>(`/api/admin/provider-oauth/providers/${providerId}/device-authorize`, data)
|
||||
return resp.data
|
||||
}
|
||||
|
||||
@@ -420,6 +420,6 @@ export async function pollDeviceAuthorize(
|
||||
providerId: string,
|
||||
data: DevicePollRequest
|
||||
): Promise<DevicePollResponse> {
|
||||
const resp = await client.post(`/api/admin/provider-oauth/providers/${providerId}/device-poll`, data)
|
||||
const resp = await client.post<DevicePollResponse>(`/api/admin/provider-oauth/providers/${providerId}/device-poll`, data)
|
||||
return resp.data
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
normalizePoolAdvancedConfig as normalizePoolAdvanced,
|
||||
} from './types'
|
||||
|
||||
export type { ProviderWithEndpointsSummary } from './types'
|
||||
interface ProviderRequestOptions {
|
||||
timeout?: number
|
||||
}
|
||||
@@ -140,7 +141,7 @@ export async function updateProvider(
|
||||
}>,
|
||||
requestOptions?: ProviderRequestOptions,
|
||||
): Promise<ProviderWithEndpointsSummary> {
|
||||
const response = await client.patch(`/api/admin/providers/${providerId}`, data, requestOptions)
|
||||
const response = await client.patch<ProviderWithEndpointsSummary>(`/api/admin/providers/${providerId}`, data, requestOptions)
|
||||
return normalizeProviderSummary(response.data)
|
||||
}
|
||||
|
||||
@@ -175,7 +176,7 @@ export async function createProvider(
|
||||
config?: ProviderConfig | null
|
||||
}
|
||||
): Promise<{ id: string; name: string; message?: string }> {
|
||||
const response = await client.post('/api/admin/providers/', data)
|
||||
const response = await client.post<{ id: string; name: string; message?: string }>('/api/admin/providers/', data)
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -261,7 +262,7 @@ export async function testModel(
|
||||
data: TestModelRequest,
|
||||
options: { signal?: AbortSignal } = {},
|
||||
): Promise<TestModelResponse> {
|
||||
const response = await client.post('/api/admin/provider-query/test-model', data, {
|
||||
const response = await client.post<TestModelResponse>('/api/admin/provider-query/test-model', data, {
|
||||
timeout: 10 * 60 * 1000,
|
||||
signal: options.signal,
|
||||
})
|
||||
@@ -350,7 +351,7 @@ export async function testModelFailover(
|
||||
const failoverModels = Array.isArray(data.failover_models) && data.failover_models.length > 0
|
||||
? data.failover_models
|
||||
: (normalizedModelName ? [normalizedModelName] : undefined)
|
||||
const response = await client.post('/api/admin/provider-query/test-model-failover', {
|
||||
const response = await client.post<TestModelFailoverResponse>('/api/admin/provider-query/test-model-failover', {
|
||||
...data,
|
||||
...(failoverModels ? { failover_models: failoverModels } : {}),
|
||||
}, {
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import type { ProviderModelMapping } from './provider'
|
||||
|
||||
export interface ModelProviderReference {
|
||||
id: string
|
||||
model_id?: string | null
|
||||
name: string
|
||||
is_active: boolean
|
||||
}
|
||||
|
||||
// ========== 阶梯计费类型 ==========
|
||||
|
||||
/** 缓存时长定价配置 */
|
||||
@@ -67,13 +74,26 @@ export interface ProviderTieredPricingConfig {
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
export interface ModelConfig extends Record<string, unknown> {
|
||||
description?: string
|
||||
model_mappings?: string[]
|
||||
api_formats?: string[]
|
||||
billing?: {
|
||||
video?: {
|
||||
price_per_second_by_resolution?: Record<string, number>
|
||||
[key: string]: unknown
|
||||
}
|
||||
[key: string]: unknown
|
||||
}
|
||||
}
|
||||
|
||||
export interface Model {
|
||||
id: string
|
||||
provider_id: string
|
||||
global_model_id: string // 关联的 GlobalModel ID
|
||||
provider_model_name: string // Provider 侧的主模型名称
|
||||
provider_model_mappings?: ProviderModelMapping[] | null // 模型名称映射列表(带优先级)
|
||||
config?: Record<string, unknown> | null // 额外配置(如 billing/video 等)
|
||||
config?: ModelConfig | null // 额外配置(如 billing/video 等)
|
||||
// 原始配置值(可能为空,为空时使用 GlobalModel 默认值)
|
||||
price_per_request?: number | null // 按次计费价格
|
||||
tiered_pricing?: ProviderTieredPricingConfig | null // Provider 原始覆盖,可仅包含 processing_tiers
|
||||
@@ -102,7 +122,7 @@ export interface Model {
|
||||
global_model_name?: string
|
||||
global_model_display_name?: string
|
||||
// 有效配置(合并 Model 和 GlobalModel 的 config)
|
||||
effective_config?: Record<string, unknown> | null
|
||||
effective_config?: ModelConfig | null
|
||||
model_test_capabilities?: ModelTestCapabilities | null
|
||||
}
|
||||
|
||||
@@ -120,7 +140,7 @@ export interface ModelCreate {
|
||||
supports_extended_thinking?: boolean
|
||||
supports_image_generation?: boolean
|
||||
is_active?: boolean
|
||||
config?: Record<string, unknown>
|
||||
config?: ModelConfig
|
||||
}
|
||||
|
||||
export interface ModelUpdate {
|
||||
@@ -136,7 +156,7 @@ export interface ModelUpdate {
|
||||
supports_image_generation?: boolean
|
||||
is_active?: boolean
|
||||
is_available?: boolean
|
||||
config?: Record<string, unknown> | null
|
||||
config?: ModelConfig | null
|
||||
}
|
||||
|
||||
export interface ModelCapabilities {
|
||||
@@ -243,7 +263,7 @@ export interface GlobalModelCreate {
|
||||
// Key 能力配置 - 模型支持的能力列表
|
||||
supported_capabilities?: string[]
|
||||
// 模型配置(JSON格式)- 包含能力、规格、元信息等
|
||||
config?: Record<string, unknown>
|
||||
config?: ModelConfig
|
||||
is_active?: boolean
|
||||
}
|
||||
|
||||
@@ -257,7 +277,7 @@ export interface GlobalModelUpdate {
|
||||
// Key 能力配置 - 模型支持的能力列表
|
||||
supported_capabilities?: string[] | null
|
||||
// 模型配置(JSON格式)- 包含能力、规格、元信息等
|
||||
config?: Record<string, unknown> | null
|
||||
config?: ModelConfig | null
|
||||
}
|
||||
|
||||
export interface GlobalModelResponse {
|
||||
@@ -273,7 +293,7 @@ export interface GlobalModelResponse {
|
||||
supported_capabilities?: string[] | null
|
||||
supports_embedding?: boolean | null
|
||||
// 模型配置(JSON格式)
|
||||
config?: Record<string, unknown> | null
|
||||
config?: ModelConfig | null
|
||||
// 统计数据
|
||||
provider_count?: number
|
||||
active_provider_count?: number
|
||||
|
||||
@@ -68,7 +68,7 @@ export const geminiFilesApi = {
|
||||
* 获取文件映射统计
|
||||
*/
|
||||
async getStats(): Promise<FileMappingStatsResponse> {
|
||||
const response = await apiClient.get('/api/admin/gemini-files/stats')
|
||||
const response = await apiClient.get<FileMappingStatsResponse>('/api/admin/gemini-files/stats')
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -76,7 +76,7 @@ export const geminiFilesApi = {
|
||||
* 列出文件映射
|
||||
*/
|
||||
async listMappings(params?: ListMappingsParams): Promise<FileMappingListResponse> {
|
||||
const response = await apiClient.get('/api/admin/gemini-files/mappings', { params })
|
||||
const response = await apiClient.get<FileMappingListResponse>('/api/admin/gemini-files/mappings', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -84,7 +84,7 @@ export const geminiFilesApi = {
|
||||
* 删除指定映射
|
||||
*/
|
||||
async deleteMapping(mappingId: string): Promise<{ message: string; file_name: string }> {
|
||||
const response = await apiClient.delete(`/api/admin/gemini-files/mappings/${mappingId}`)
|
||||
const response = await apiClient.delete<{ message: string; file_name: string }>(`/api/admin/gemini-files/mappings/${mappingId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -92,7 +92,7 @@ export const geminiFilesApi = {
|
||||
* 清理过期映射
|
||||
*/
|
||||
async cleanupExpired(): Promise<{ message: string; deleted_count: number }> {
|
||||
const response = await apiClient.delete('/api/admin/gemini-files/mappings')
|
||||
const response = await apiClient.delete<{ message: string; deleted_count: number }>('/api/admin/gemini-files/mappings')
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -100,7 +100,7 @@ export const geminiFilesApi = {
|
||||
* 获取可用的 Key 列表
|
||||
*/
|
||||
async getCapableKeys(): Promise<CapableKeyResponse[]> {
|
||||
const response = await apiClient.get('/api/admin/gemini-files/capable-keys')
|
||||
const response = await apiClient.get<CapableKeyResponse[]>('/api/admin/gemini-files/capable-keys')
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -110,7 +110,7 @@ export const geminiFilesApi = {
|
||||
async uploadFile(file: File, keyIds: string[]): Promise<UploadResponse> {
|
||||
const formData = new FormData()
|
||||
formData.append('file', file)
|
||||
const response = await apiClient.post(
|
||||
const response = await apiClient.post<UploadResponse>(
|
||||
`/api/admin/gemini-files/upload?key_ids=${keyIds.join(',')}`,
|
||||
formData,
|
||||
{
|
||||
|
||||
+86
-16
@@ -1,4 +1,5 @@
|
||||
import apiClient from './client'
|
||||
import type { ImageProgress } from './requestTrace'
|
||||
import type { ActivityHeatmap } from '@/types/activity'
|
||||
import type { TieredPricingConfig } from './endpoints/types'
|
||||
import { cachedRequest, buildCacheKey } from '@/utils/cache'
|
||||
@@ -238,13 +239,13 @@ export const meApi = {
|
||||
username?: string
|
||||
feature_settings?: FeatureSettingsMap | null
|
||||
}): Promise<{ message: string }> {
|
||||
const response = await apiClient.put('/api/users/me', data)
|
||||
const response = await apiClient.put<{ message: string }>('/api/users/me', data)
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 修改密码
|
||||
async changePassword(data: ChangePasswordRequest): Promise<{ message: string }> {
|
||||
const response = await apiClient.patch('/api/users/me/password', data)
|
||||
const response = await apiClient.patch<{ message: string }>('/api/users/me/password', data)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -261,12 +262,12 @@ export const meApi = {
|
||||
},
|
||||
|
||||
async revokeSession(sessionId: string): Promise<{ message: string }> {
|
||||
const response = await apiClient.delete(`/api/users/me/sessions/${sessionId}`)
|
||||
const response = await apiClient.delete<{ message: string }>(`/api/users/me/sessions/${sessionId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
async revokeOtherSessions(): Promise<{ message: string; revoked_count: number }> {
|
||||
const response = await apiClient.delete('/api/users/me/sessions/others')
|
||||
const response = await apiClient.delete<{ message: string; revoked_count: number }>('/api/users/me/sessions/others')
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -303,7 +304,7 @@ export const meApi = {
|
||||
},
|
||||
|
||||
async deleteApiKey(keyId: string): Promise<{ message: string }> {
|
||||
const response = await apiClient.delete(`/api/users/me/api-keys/${keyId}`)
|
||||
const response = await apiClient.delete<{ message: string }>(`/api/users/me/api-keys/${keyId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -396,16 +397,61 @@ export const meApi = {
|
||||
reasoning_effort?: string | null
|
||||
service_tier?: string | null
|
||||
actual_service_tier?: string | null
|
||||
image_progress?: ImageProgress | null
|
||||
}>
|
||||
}> {
|
||||
const params = ids ? { ids } : {}
|
||||
const response = await apiClient.get('/api/users/me/usage/active', { params })
|
||||
const response = await apiClient.get<{
|
||||
requests: Array<{
|
||||
id: string
|
||||
status: 'pending' | 'streaming' | 'completed' | 'failed' | 'cancelled'
|
||||
input_tokens: number
|
||||
effective_input_tokens?: number | null
|
||||
output_tokens: number
|
||||
cache_creation_input_tokens?: number | null
|
||||
cache_creation_ephemeral_5m_input_tokens?: number | null
|
||||
cache_creation_ephemeral_1h_input_tokens?: number | null
|
||||
cache_read_input_tokens?: number | null
|
||||
cost: number
|
||||
actual_cost?: number | null
|
||||
rate_multiplier?: number | null
|
||||
response_time_ms: number | null
|
||||
first_byte_time_ms: number | null
|
||||
end_to_end_time_ms?: number | null
|
||||
end_to_end_first_byte_time_ms?: number | null
|
||||
updated_at?: string | null
|
||||
response_time_updated_at?: string | null
|
||||
status_code?: number | null
|
||||
error_message?: string | null
|
||||
api_format?: string | null
|
||||
endpoint_api_format?: string | null
|
||||
is_stream?: boolean | null
|
||||
is_websocket?: boolean | null
|
||||
websocket_transport?: string | null
|
||||
usage_available?: boolean | null
|
||||
usage_pricing_available?: boolean | null
|
||||
input_audio_tokens?: number | null
|
||||
output_audio_tokens?: number | null
|
||||
upstream_is_stream?: boolean | null
|
||||
client_requested_stream?: boolean | null
|
||||
client_is_stream?: boolean | null
|
||||
has_format_conversion?: boolean | null
|
||||
has_fallback?: boolean | null
|
||||
target_model?: string | null
|
||||
request_type?: string | null
|
||||
requested_reasoning_effort?: string | null
|
||||
reasoning_effort?: string | null
|
||||
service_tier?: string | null
|
||||
actual_service_tier?: string | null
|
||||
image_progress?: ImageProgress | null
|
||||
}>
|
||||
}>('/api/users/me/usage/active', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 获取可用的提供商
|
||||
async getAvailableProviders(): Promise<Array<Record<string, unknown>>> {
|
||||
const response = await apiClient.get('/api/users/me/providers')
|
||||
const response = await apiClient.get<Array<Record<string, unknown>>>('/api/users/me/providers')
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -429,24 +475,38 @@ export const meApi = {
|
||||
}>
|
||||
total: number
|
||||
}> {
|
||||
const response = await apiClient.get('/api/users/me/available-models', { params })
|
||||
const response = await apiClient.get<{
|
||||
models: Array<{
|
||||
id: string
|
||||
name: string
|
||||
display_name: string | null
|
||||
is_active: boolean
|
||||
default_price_per_request: number | null
|
||||
default_tiered_pricing: TieredPricingConfig | null
|
||||
supported_capabilities: string[] | null
|
||||
supports_embedding?: boolean | null
|
||||
config: Record<string, unknown> | null
|
||||
usage_count: number
|
||||
}>
|
||||
total: number
|
||||
}>('/api/users/me/available-models', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 获取端点状态(不包含敏感信息)
|
||||
async getEndpointStatus(): Promise<Array<Record<string, unknown>>> {
|
||||
const response = await apiClient.get('/api/users/me/endpoint-status')
|
||||
const response = await apiClient.get<Array<Record<string, unknown>>>('/api/users/me/endpoint-status')
|
||||
return response.data
|
||||
},
|
||||
|
||||
// 偏好设置
|
||||
async getPreferences(): Promise<UserPreferences> {
|
||||
const response = await apiClient.get('/api/users/me/preferences')
|
||||
const response = await apiClient.get<UserPreferences>('/api/users/me/preferences')
|
||||
return response.data
|
||||
},
|
||||
|
||||
async updatePreferences(data: Partial<UserPreferences>): Promise<{ message: string }> {
|
||||
const response = await apiClient.put('/api/users/me/preferences', data)
|
||||
const response = await apiClient.put<{ message: string }>('/api/users/me/preferences', data)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -456,7 +516,7 @@ export const meApi = {
|
||||
async updateApiKeyProviders(keyId: string, data: {
|
||||
allowed_providers?: ProviderConfig[]
|
||||
}): Promise<{ message: string }> {
|
||||
const response = await apiClient.put(`/api/users/me/api-keys/${keyId}/providers`, data)
|
||||
const response = await apiClient.put<{ message: string }>(`/api/users/me/api-keys/${keyId}/providers`, data)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -464,7 +524,7 @@ export const meApi = {
|
||||
async updateApiKeyCapabilities(keyId: string, data: {
|
||||
force_capabilities?: Record<string, boolean> | null
|
||||
}): Promise<{ message: string; force_capabilities?: Record<string, boolean> | null }> {
|
||||
const response = await apiClient.put(`/api/users/me/api-keys/${keyId}/capabilities`, data)
|
||||
const response = await apiClient.put<{ message: string; force_capabilities?: Record<string, boolean> | null }>(`/api/users/me/api-keys/${keyId}/capabilities`, data)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -472,7 +532,9 @@ export const meApi = {
|
||||
async getModelCapabilitySettings(): Promise<{
|
||||
model_capability_settings: Record<string, Record<string, boolean>>
|
||||
}> {
|
||||
const response = await apiClient.get('/api/users/me/model-capabilities')
|
||||
const response = await apiClient.get<{
|
||||
model_capability_settings: Record<string, Record<string, boolean>>
|
||||
}>('/api/users/me/model-capabilities')
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -482,7 +544,10 @@ export const meApi = {
|
||||
message: string
|
||||
model_capability_settings: Record<string, Record<string, boolean>> | null
|
||||
}> {
|
||||
const response = await apiClient.put('/api/users/me/model-capabilities', data)
|
||||
const response = await apiClient.put<{
|
||||
message: string
|
||||
model_capability_settings: Record<string, Record<string, boolean>> | null
|
||||
}>('/api/users/me/model-capabilities', data)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -500,7 +565,12 @@ export const meApi = {
|
||||
return cachedRequest(
|
||||
cacheKey,
|
||||
async () => {
|
||||
const response = await apiClient.get('/api/users/me/usage/interval-timeline', { params })
|
||||
const response = await apiClient.get<{
|
||||
analysis_period_hours: number
|
||||
total_points: number
|
||||
points: Array<{ x: string; y: number; model?: string }>
|
||||
models?: string[]
|
||||
}>('/api/users/me/usage/interval-timeline', { params })
|
||||
return response.data
|
||||
},
|
||||
30000
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
export interface CredentialsSchema {
|
||||
type: 'object'
|
||||
properties: Record<string, SchemaProperty>
|
||||
required?: string[]
|
||||
'x-field-groups'?: SchemaFieldGroup[]
|
||||
'x-auth-type'?: string
|
||||
'x-auth-method'?: string
|
||||
'x-validation'?: SchemaValidation[]
|
||||
'x-quota-divisor'?: number | null
|
||||
'x-currency'?: string
|
||||
'x-default-base-url'?: string
|
||||
'x-balance-extra-format'?: BalanceExtraFormat[]
|
||||
'x-field-hooks'?: Record<string, { action: string; target: string }>
|
||||
}
|
||||
|
||||
export interface SchemaProperty {
|
||||
type: string
|
||||
title?: string
|
||||
description?: string
|
||||
'x-sensitive'?: boolean
|
||||
'x-input-type'?: string
|
||||
'x-default-value'?: string
|
||||
'x-help'?: string
|
||||
}
|
||||
|
||||
export interface SchemaFieldGroup {
|
||||
fields: string[]
|
||||
layout?: 'inline' | 'vertical'
|
||||
'x-flex'?: Record<string, number>
|
||||
'x-help'?: string
|
||||
}
|
||||
|
||||
interface SchemaValidation {
|
||||
type: 'required' | 'any_required' | 'conditional_required'
|
||||
fields?: string[]
|
||||
message: string
|
||||
/** conditional_required: 当此字段有值时 */
|
||||
if?: string
|
||||
/** conditional_required: 除非此字段有值 */
|
||||
unless?: string
|
||||
/** conditional_required: 则这些字段必填 */
|
||||
then?: string[]
|
||||
}
|
||||
|
||||
export interface BalanceExtraFormat {
|
||||
label: string
|
||||
type: 'window_limit' | 'daily_quota' | 'weekly_spent' | 'monthly_expiry'
|
||||
/** window_limit: extra 中的字段名 */
|
||||
source?: string
|
||||
/** window_limit: 单位除数 */
|
||||
unit_divisor?: number
|
||||
/** daily_quota / weekly_spent: limit 字段名 */
|
||||
source_limit?: string
|
||||
/** daily_quota: remaining 字段名 */
|
||||
source_remaining?: string
|
||||
/** daily_quota: 每日重置基准时间字段名(计算下次重置时间) */
|
||||
source_start_date?: string
|
||||
/** weekly_spent: spent 字段名 */
|
||||
source_spent?: string
|
||||
/** weekly_spent: resets_at 字段名 */
|
||||
source_resets_at?: string
|
||||
/** monthly_expiry: 到期日期字段名 */
|
||||
source_end_date?: string
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import client from './client'
|
||||
import type { CredentialsSchema } from './providerCredentials'
|
||||
|
||||
// ==================== Types ====================
|
||||
|
||||
@@ -46,11 +47,11 @@ export interface ArchitectureInfo {
|
||||
architecture_id: string
|
||||
display_name: string
|
||||
description: string
|
||||
credentials_schema: Record<string, unknown>
|
||||
credentials_schema: CredentialsSchema
|
||||
supported_auth_types: Array<{
|
||||
type: string
|
||||
display_name: string
|
||||
credentials_schema?: Record<string, unknown>
|
||||
credentials_schema?: CredentialsSchema
|
||||
}>
|
||||
supported_actions: Array<{
|
||||
type: string
|
||||
|
||||
@@ -36,6 +36,6 @@ export async function getPublicGlobalModels(params?: {
|
||||
is_active?: boolean
|
||||
search?: string
|
||||
}): Promise<PublicGlobalModelListResponse> {
|
||||
const response = await client.get('/api/public/global-models', { params })
|
||||
const response = await client.get<PublicGlobalModelListResponse>('/api/public/global-models', { params })
|
||||
return response.data
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ export const referralApi = {
|
||||
async getAdminReferrals(
|
||||
params: ReferralRelationshipQuery = {}
|
||||
): Promise<ReferralListResponse<ReferralRelationshipRecord>> {
|
||||
const response = await apiClient.get('/api/admin/referrals', {
|
||||
const response = await apiClient.get<ReferralListResponse<ReferralRelationshipRecord>>('/api/admin/referrals', {
|
||||
params: cleanParams(params as Record<string, unknown>)
|
||||
})
|
||||
return response.data
|
||||
@@ -96,19 +96,19 @@ export const referralApi = {
|
||||
async getAdminReferralRewards(
|
||||
params: ReferralRewardQuery = {}
|
||||
): Promise<ReferralListResponse<ReferralRewardRecord>> {
|
||||
const response = await apiClient.get('/api/admin/referral-rewards', {
|
||||
const response = await apiClient.get<ReferralListResponse<ReferralRewardRecord>>('/api/admin/referral-rewards', {
|
||||
params: cleanParams(params as Record<string, unknown>)
|
||||
})
|
||||
return response.data
|
||||
},
|
||||
|
||||
async retryReferralReward(id: string, note?: string): Promise<{ reward: ReferralRewardRecord }> {
|
||||
const response = await apiClient.post(`/api/admin/referral-rewards/${id}/retry`, { note })
|
||||
const response = await apiClient.post<{ reward: ReferralRewardRecord }>(`/api/admin/referral-rewards/${id}/retry`, { note })
|
||||
return response.data
|
||||
},
|
||||
|
||||
async voidReferralReward(id: string, note?: string): Promise<{ reward: ReferralRewardRecord }> {
|
||||
const response = await apiClient.post(`/api/admin/referral-rewards/${id}/void`, { note })
|
||||
const response = await apiClient.post<{ reward: ReferralRewardRecord }>(`/api/admin/referral-rewards/${id}/void`, { note })
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,32 @@ export interface CandidateResponseBoundary {
|
||||
body_state?: string | null
|
||||
}
|
||||
|
||||
export interface CandidateProxyTiming {
|
||||
body_read_ms?: number | null
|
||||
decompress_ms?: number | null
|
||||
wire_size?: number | null
|
||||
body_size?: number | null
|
||||
ttfb_ms?: number | null
|
||||
upstream_ms?: number | null
|
||||
response_wait_ms?: number | null
|
||||
connection_acquire_ms?: number | null
|
||||
upstream_processing_ms?: number | null
|
||||
connect_ms?: number | null
|
||||
tls_ms?: number | null
|
||||
dns_ms?: number | null
|
||||
total_ms?: number | null
|
||||
connection_reused?: boolean | null
|
||||
}
|
||||
|
||||
export interface CandidateProxy {
|
||||
node_name?: string | null
|
||||
node_id?: string | null
|
||||
url?: string | null
|
||||
source?: string | null
|
||||
ttfb_ms?: number | null
|
||||
timing?: CandidateProxyTiming | null
|
||||
}
|
||||
|
||||
export interface CandidateRecord {
|
||||
id: string
|
||||
request_id: string
|
||||
@@ -70,6 +96,22 @@ export interface CandidateRecord {
|
||||
ranking?: CandidateRankingMetadata | null
|
||||
image_progress?: ImageProgress | null
|
||||
extra_data?: Record<string, unknown> & {
|
||||
first_byte_time_ms?: number | null
|
||||
needs_conversion?: boolean
|
||||
provider_api_format?: string | null
|
||||
proxy?: CandidateProxy | null
|
||||
pool_selection?: {
|
||||
reason: string
|
||||
cost_soft_threshold?: number | boolean | null
|
||||
cost_window_usage?: number | null
|
||||
cost_limit?: number | null
|
||||
} | null
|
||||
pool_skip?: {
|
||||
type: string
|
||||
cooldown_reason?: string | null
|
||||
cooldown_ttl?: number | null
|
||||
cost_window_usage?: number | null
|
||||
} | null
|
||||
upstream_response?: CandidateResponseBoundary
|
||||
image_progress?: ImageProgress | null
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ export const blacklistApi = {
|
||||
* 获取黑名单统计
|
||||
*/
|
||||
async getStats(): Promise<BlacklistStats> {
|
||||
const response = await apiClient.get('/api/admin/security/ip/blacklist/stats')
|
||||
const response = await apiClient.get<BlacklistStats>('/api/admin/security/ip/blacklist/stats')
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -67,7 +67,7 @@ export const blacklistApi = {
|
||||
* 获取黑名单列表
|
||||
*/
|
||||
async getList(): Promise<BlacklistResponse> {
|
||||
const response = await apiClient.get('/api/admin/security/ip/blacklist')
|
||||
const response = await apiClient.get<BlacklistResponse>('/api/admin/security/ip/blacklist')
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ export const whitelistApi = {
|
||||
* 获取白名单列表
|
||||
*/
|
||||
async getList(): Promise<WhitelistResponse> {
|
||||
const response = await apiClient.get('/api/admin/security/ip/whitelist')
|
||||
const response = await apiClient.get<WhitelistResponse>('/api/admin/security/ip/whitelist')
|
||||
return response.data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import apiClient from './client'
|
||||
import { cachedRequest, dedupedRequest, buildCacheKey } from '@/utils/cache'
|
||||
import type { ActivityHeatmap } from '@/types/activity'
|
||||
import type { ImageProgress } from './requestTrace'
|
||||
import type { UsageRecord as UsageListRecord } from './usageRecords'
|
||||
|
||||
const ACTIVITY_HEATMAP_CACHE_TTL_MS = 30 * 60 * 1000
|
||||
const USAGE_ANALYTICS_CACHE_TTL_MS = 30 * 1000
|
||||
@@ -58,6 +59,12 @@ export interface UsageStats {
|
||||
avg_response_time: number
|
||||
error_count?: number
|
||||
error_rate?: number
|
||||
cache_stats?: {
|
||||
cache_creation_tokens: number
|
||||
cache_read_tokens: number
|
||||
cache_creation_cost: number
|
||||
cache_read_cost: number
|
||||
}
|
||||
today?: {
|
||||
requests: number
|
||||
tokens: number
|
||||
@@ -67,6 +74,7 @@ export interface UsageStats {
|
||||
}
|
||||
|
||||
export interface UsageByModel {
|
||||
actual_cost?: number
|
||||
model: string
|
||||
request_count: number
|
||||
total_tokens: number
|
||||
@@ -511,7 +519,7 @@ export const usageApi = {
|
||||
limit?: number
|
||||
offset?: number
|
||||
}): Promise<{
|
||||
records: Array<Record<string, unknown>>
|
||||
records: UsageListRecord[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
@@ -519,7 +527,13 @@ export const usageApi = {
|
||||
}> {
|
||||
const key = buildCacheKey('usage:records', params as Record<string, unknown> | undefined)
|
||||
return dedupedRequest(key, async () => {
|
||||
const response = await apiClient.get('/api/admin/usage/records', { params })
|
||||
const response = await apiClient.get<{
|
||||
records: UsageListRecord[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
total_is_estimated?: boolean
|
||||
}>('/api/admin/usage/records', { params })
|
||||
return response.data
|
||||
})
|
||||
},
|
||||
@@ -630,7 +644,54 @@ export const usageApi = {
|
||||
if (typeof timeRange?.tz_offset_minutes === 'number') {
|
||||
params.tz_offset_minutes = timeRange.tz_offset_minutes
|
||||
}
|
||||
const response = await apiClient.get('/api/admin/usage/active', { params })
|
||||
const response = await apiClient.get<{
|
||||
requests: Array<{
|
||||
id: string
|
||||
status: 'pending' | 'streaming' | 'completed' | 'failed' | 'cancelled'
|
||||
input_tokens: number
|
||||
effective_input_tokens?: number | null
|
||||
output_tokens: number
|
||||
cache_creation_input_tokens?: number | null
|
||||
cache_creation_ephemeral_5m_input_tokens?: number | null
|
||||
cache_creation_ephemeral_1h_input_tokens?: number | null
|
||||
cache_read_input_tokens?: number | null
|
||||
cost: number
|
||||
actual_cost?: number | null
|
||||
rate_multiplier?: number | null
|
||||
response_time_ms: number | null
|
||||
first_byte_time_ms: number | null
|
||||
end_to_end_time_ms?: number | null
|
||||
end_to_end_first_byte_time_ms?: number | null
|
||||
updated_at?: string | null
|
||||
response_time_updated_at?: string | null
|
||||
status_code?: number | null
|
||||
error_message?: string | null
|
||||
provider?: string | null
|
||||
api_key_name?: string | null
|
||||
provider_key_name?: string | null
|
||||
api_format?: string | null
|
||||
endpoint_api_format?: string | null
|
||||
is_stream?: boolean | null
|
||||
is_websocket?: boolean | null
|
||||
websocket_transport?: string | null
|
||||
usage_available?: boolean | null
|
||||
usage_pricing_available?: boolean | null
|
||||
input_audio_tokens?: number | null
|
||||
output_audio_tokens?: number | null
|
||||
upstream_is_stream?: boolean | null
|
||||
client_requested_stream?: boolean | null
|
||||
client_is_stream?: boolean | null
|
||||
has_format_conversion?: boolean | null
|
||||
has_fallback?: boolean | null
|
||||
target_model?: string | null
|
||||
request_type?: string | null
|
||||
requested_reasoning_effort?: string | null
|
||||
reasoning_effort?: string | null
|
||||
service_tier?: string | null
|
||||
actual_service_tier?: string | null
|
||||
image_progress?: ImageProgress | null
|
||||
}>
|
||||
}>('/api/admin/usage/active', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { ImageProgress } from './requestTrace'
|
||||
|
||||
export type RequestStatus = 'pending' | 'streaming' | 'completed' | 'failed' | 'cancelled'
|
||||
|
||||
export interface UsageRecord {
|
||||
id: string
|
||||
user_id?: string
|
||||
username?: string
|
||||
user_email?: string
|
||||
api_key?: {
|
||||
id: string | null
|
||||
name: string | null
|
||||
display: string | null
|
||||
} | null
|
||||
provider?: string // 仅管理员可见
|
||||
api_key_name?: string
|
||||
provider_key_name?: string | null
|
||||
rate_multiplier?: number
|
||||
model: string
|
||||
target_model?: string | null // 映射后的目标模型名(若无映射则为空)
|
||||
model_version?: string | null // Provider 返回的实际模型版本(列表轻量字段)
|
||||
request_type?: string | null // 由请求语义识别出的操作类型
|
||||
requested_reasoning_effort?: string | null // 用户请求侧 reasoning 级别,用于展示转换关系
|
||||
reasoning_effort?: string | null // 从发送给 Provider 的请求体提取的 reasoning 级别
|
||||
service_tier?: string | null // 从发送给 Provider 的请求体提取的服务层级
|
||||
actual_service_tier?: string | null // 响应侧审计事实,不用于 Fast 展示或计费
|
||||
api_format?: string
|
||||
endpoint_api_format?: string // 端点原生格式
|
||||
has_format_conversion?: boolean // 是否发生了格式转换
|
||||
input_tokens: number
|
||||
effective_input_tokens?: number
|
||||
output_tokens: number
|
||||
reasoning_tokens?: number
|
||||
cache_creation_input_tokens?: number
|
||||
cache_creation_ephemeral_5m_input_tokens?: number
|
||||
cache_creation_ephemeral_1h_input_tokens?: number
|
||||
cache_read_input_tokens?: number
|
||||
total_tokens: number
|
||||
cost: number
|
||||
actual_cost?: number
|
||||
response_time_ms?: number | null
|
||||
first_byte_time_ms?: number | null // 首字时间 (TTFB)
|
||||
end_to_end_time_ms?: number | null // 客户端从请求进入网关到完成的总耗时
|
||||
end_to_end_first_byte_time_ms?: number | null // 客户端从请求进入网关到首字节的耗时
|
||||
is_stream: boolean
|
||||
is_websocket?: boolean
|
||||
websocket_transport?: string | null
|
||||
usage_available?: boolean
|
||||
usage_pricing_available?: boolean
|
||||
input_audio_tokens?: number | null
|
||||
output_audio_tokens?: number | null
|
||||
upstream_is_stream?: boolean
|
||||
client_requested_stream?: boolean
|
||||
client_is_stream?: boolean
|
||||
client_family?: string | null
|
||||
client_ip?: string | null
|
||||
user_agent?: string | null
|
||||
request_path?: string | null
|
||||
request_path_and_query?: string | null
|
||||
status_code?: number
|
||||
error_message?: string
|
||||
status?: RequestStatus // 请求状态: pending, streaming, completed, failed
|
||||
created_at: string
|
||||
updated_at?: string | null
|
||||
response_time_updated_at?: string | null
|
||||
has_fallback?: boolean
|
||||
has_retry?: boolean
|
||||
image_progress?: ImageProgress | null
|
||||
}
|
||||
@@ -60,7 +60,7 @@ export interface User {
|
||||
export interface CreateUserRequest {
|
||||
username: string
|
||||
password: string
|
||||
email: string
|
||||
email?: string
|
||||
role?: UserRole
|
||||
initial_gift_usd?: number | null
|
||||
unlimited?: boolean
|
||||
|
||||
@@ -223,7 +223,10 @@ export const walletApi = {
|
||||
order: PaymentOrder
|
||||
payment_instructions: Record<string, unknown>
|
||||
}> {
|
||||
const response = await apiClient.post('/api/wallet/recharge', payload)
|
||||
const response = await apiClient.post<{
|
||||
order: PaymentOrder
|
||||
payment_instructions: Record<string, unknown>
|
||||
}>('/api/wallet/recharge', payload)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -238,12 +241,17 @@ export const walletApi = {
|
||||
limit: number
|
||||
offset: number
|
||||
}> {
|
||||
const response = await apiClient.get('/api/wallet/recharge', { params })
|
||||
const response = await apiClient.get<{
|
||||
items: PaymentOrder[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
}>('/api/wallet/recharge', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
async getRechargeOrder(orderId: string): Promise<{ order: PaymentOrder }> {
|
||||
const response = await apiClient.get(`/api/wallet/recharge/${orderId}`)
|
||||
const response = await apiClient.get<{ order: PaymentOrder }>(`/api/wallet/recharge/${orderId}`)
|
||||
return response.data
|
||||
},
|
||||
|
||||
@@ -253,7 +261,12 @@ export const walletApi = {
|
||||
limit: number
|
||||
offset: number
|
||||
}> {
|
||||
const response = await apiClient.get('/api/wallet/refunds', { params })
|
||||
const response = await apiClient.get<{
|
||||
items: RefundRequest[]
|
||||
total: number
|
||||
limit: number
|
||||
offset: number
|
||||
}>('/api/wallet/refunds', { params })
|
||||
return response.data
|
||||
},
|
||||
|
||||
|
||||
Reference in New Issue
Block a user