mirror of
https://github.com/zyycn/codex-proxy-rs.git
synced 2026-11-06 08:06:19 +00:00
refactor(web): type the API layer end to end
Make request generic so the axios envelope is unwrapped as a typed value, and thread the known response shapes through every api module and their call sites so a wire rename surfaces as a compile error instead of silent any. Type-only: no runtime or visual change. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,186 @@
|
||||
import request from '../request'
|
||||
|
||||
export interface AccountQuotaWindow {
|
||||
key: string
|
||||
group: string
|
||||
windowSeconds: number | null
|
||||
labelDisplay: string
|
||||
usedPercent: number | null
|
||||
usedPercentDisplay: string
|
||||
localUsage?: unknown
|
||||
resetAtDisplay: string
|
||||
}
|
||||
|
||||
export interface AccountQuota {
|
||||
refreshedAtDisplay: string
|
||||
windows: AccountQuotaWindow[]
|
||||
}
|
||||
|
||||
export interface AccountCurrencyCost {
|
||||
currency: string
|
||||
estimatedAmount: string
|
||||
estimatedAmountDisplay: string
|
||||
}
|
||||
|
||||
export interface AccountModelUsage {
|
||||
model: string
|
||||
requestCount: number
|
||||
requestCountDisplay: string
|
||||
successRate: number | null
|
||||
successRateDisplay: string
|
||||
inputTokens: number | null
|
||||
inputTokensDisplay: string
|
||||
outputTokens: number | null
|
||||
outputTokensDisplay: string
|
||||
cachedTokens: number | null
|
||||
cachedTokensDisplay: string
|
||||
imageInputTokens: number | null
|
||||
imageInputTokensDisplay: string
|
||||
imageOutputTokens: number | null
|
||||
imageOutputTokensDisplay: string
|
||||
imageRequestCount: number
|
||||
imageRequestCountDisplay: string
|
||||
imageRequestFailedCount: number
|
||||
imageRequestFailedCountDisplay: string
|
||||
totalTokens: number | null
|
||||
totalTokensDisplay: string
|
||||
billingAmountUsd: string | null
|
||||
billingAmountUsdDisplay: string
|
||||
costEstimateStatus: string
|
||||
knownCostCount: number
|
||||
partialCostCount: number
|
||||
unknownCostCount: number
|
||||
costs: AccountCurrencyCost[]
|
||||
lastUsedAt: string
|
||||
lastUsedAtDisplay: string
|
||||
}
|
||||
|
||||
export interface AccountUsage {
|
||||
requestCount: number | null
|
||||
requestCountDisplay: string
|
||||
inputTokens: number | null
|
||||
inputTokensDisplay: string
|
||||
outputTokens: number | null
|
||||
outputTokensDisplay: string
|
||||
cachedTokens: number | null
|
||||
cachedTokensDisplay: string
|
||||
imageInputTokens: number | null
|
||||
imageInputTokensDisplay: string
|
||||
imageOutputTokens: number | null
|
||||
imageOutputTokensDisplay: string
|
||||
imageRequestCount: number | null
|
||||
imageRequestCountDisplay: string
|
||||
imageRequestFailedCount: number | null
|
||||
imageRequestFailedCountDisplay: string
|
||||
totalTokens: number | null
|
||||
totalTokensDisplay: string
|
||||
createdTokens: number | null
|
||||
createdTokensDisplay: string
|
||||
readTokens: number | null
|
||||
readTokensDisplay: string
|
||||
lastUsedAt: string | null
|
||||
lastUsedAtDisplay: string
|
||||
costEstimateStatus: string
|
||||
knownCostCount: number | null
|
||||
partialCostCount: number | null
|
||||
unknownCostCount: number | null
|
||||
costs: AccountCurrencyCost[]
|
||||
models: AccountModelUsage[]
|
||||
}
|
||||
|
||||
export interface Account {
|
||||
id: string
|
||||
name: string
|
||||
provider: string
|
||||
resourceRef: string
|
||||
email: string | null
|
||||
accountId: string | null
|
||||
userId: string | null
|
||||
label: string | null
|
||||
planType: string | null
|
||||
authenticationKind: string
|
||||
hasRefreshToken: boolean
|
||||
status: string
|
||||
displayStatus: string
|
||||
tokenRefreshing: boolean
|
||||
availability: string
|
||||
enabled: boolean
|
||||
credentialRevision: number
|
||||
stateRevision: number | null
|
||||
accessTokenExpiresAt: string | null
|
||||
accessTokenExpiresAtDisplay: string | null
|
||||
refreshTokenExpiresAt: string | null
|
||||
nextRefreshAt: string | null
|
||||
addedAt: string
|
||||
addedAtDisplay: string
|
||||
updatedAt: string
|
||||
updatedAtDisplay: string
|
||||
quota: AccountQuota
|
||||
usage: AccountUsage
|
||||
}
|
||||
|
||||
export interface AccountPageMeta {
|
||||
page: number
|
||||
pageSize: number
|
||||
total: number
|
||||
totalPages: number
|
||||
}
|
||||
|
||||
export interface AccountSummary {
|
||||
total: number
|
||||
active: number
|
||||
quotaExhausted: number
|
||||
unavailable: number
|
||||
}
|
||||
|
||||
export interface AccountListResponse {
|
||||
configRevision: number
|
||||
items: Account[]
|
||||
page: AccountPageMeta
|
||||
summary: AccountSummary
|
||||
}
|
||||
|
||||
export interface AccountRefreshResponse {
|
||||
configRevision: number
|
||||
account: Account
|
||||
result?: string
|
||||
error?: string
|
||||
}
|
||||
|
||||
export interface AccountQuotaResponse {
|
||||
account: Account
|
||||
}
|
||||
|
||||
export interface AccountModelsResponse {
|
||||
models: Array<{ id: string, label: string }>
|
||||
}
|
||||
|
||||
export interface AccountImportResponse {
|
||||
configRevision: number
|
||||
importedCount: number
|
||||
accountIds: string[]
|
||||
}
|
||||
|
||||
export interface AccountMutationResponse {
|
||||
configRevision: number
|
||||
accountId: string
|
||||
credentialRevision?: number
|
||||
}
|
||||
|
||||
export interface AccountDeletionResponse {
|
||||
configRevision: number
|
||||
deletedCount: number
|
||||
accountIds: string[]
|
||||
}
|
||||
|
||||
export interface AccountOAuthStartResponse {
|
||||
flowId: string
|
||||
authorizationUrl: string
|
||||
expiresAt: string
|
||||
}
|
||||
|
||||
export function getAccounts(data: object) {
|
||||
return request({
|
||||
return request<AccountListResponse>({
|
||||
url: '/api/admin/accounts',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -9,7 +188,7 @@ export function getAccounts(data: object) {
|
||||
}
|
||||
|
||||
export function exportAccounts(data: object) {
|
||||
return request({
|
||||
return request<unknown>({
|
||||
url: '/api/admin/accounts/export',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -17,7 +196,7 @@ export function exportAccounts(data: object) {
|
||||
}
|
||||
|
||||
export function refreshAccount(data: object) {
|
||||
return request({
|
||||
return request<AccountRefreshResponse>({
|
||||
url: '/api/admin/accounts/refresh',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -25,7 +204,7 @@ export function refreshAccount(data: object) {
|
||||
}
|
||||
|
||||
export function getAccountQuota(data: object) {
|
||||
return request({
|
||||
return request<AccountQuotaResponse>({
|
||||
url: '/api/admin/accounts/quota',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -33,7 +212,7 @@ export function getAccountQuota(data: object) {
|
||||
}
|
||||
|
||||
export function refreshAccountQuota(data: object) {
|
||||
return request({
|
||||
return request<AccountQuotaResponse>({
|
||||
url: '/api/admin/accounts/quota/refresh',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -41,7 +220,7 @@ export function refreshAccountQuota(data: object) {
|
||||
}
|
||||
|
||||
export function getAccountModels(data: object) {
|
||||
return request({
|
||||
return request<AccountModelsResponse>({
|
||||
url: '/api/admin/accounts/models',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -49,7 +228,7 @@ export function getAccountModels(data: object) {
|
||||
}
|
||||
|
||||
export function refreshAccountModels(data: object) {
|
||||
return request({
|
||||
return request<AccountModelsResponse>({
|
||||
url: '/api/admin/accounts/models/refresh',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -57,7 +236,7 @@ export function refreshAccountModels(data: object) {
|
||||
}
|
||||
|
||||
export function importAccounts(data: object) {
|
||||
return request({
|
||||
return request<AccountImportResponse>({
|
||||
url: '/api/admin/accounts/import',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -65,7 +244,7 @@ export function importAccounts(data: object) {
|
||||
}
|
||||
|
||||
export function enableAccount(data: object) {
|
||||
return request({
|
||||
return request<AccountMutationResponse>({
|
||||
url: '/api/admin/accounts/enable',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -73,7 +252,7 @@ export function enableAccount(data: object) {
|
||||
}
|
||||
|
||||
export function disableAccount(data: object) {
|
||||
return request({
|
||||
return request<AccountMutationResponse>({
|
||||
url: '/api/admin/accounts/disable',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -81,7 +260,7 @@ export function disableAccount(data: object) {
|
||||
}
|
||||
|
||||
export function deleteAccounts(data: object) {
|
||||
return request({
|
||||
return request<AccountDeletionResponse>({
|
||||
url: '/api/admin/accounts/delete',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -89,7 +268,7 @@ export function deleteAccounts(data: object) {
|
||||
}
|
||||
|
||||
export function startAccountOAuth(data: object) {
|
||||
return request({
|
||||
return request<AccountOAuthStartResponse>({
|
||||
url: '/api/admin/accounts/oauth/start',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -97,7 +276,7 @@ export function startAccountOAuth(data: object) {
|
||||
}
|
||||
|
||||
export function completeAccountOAuth(data: object) {
|
||||
return request({
|
||||
return request<AccountMutationResponse>({
|
||||
url: '/api/admin/accounts/oauth/complete',
|
||||
method: 'POST',
|
||||
data,
|
||||
|
||||
@@ -1,7 +1,45 @@
|
||||
import request from '../request'
|
||||
|
||||
export interface ApiKey {
|
||||
id: string
|
||||
name: string
|
||||
label: string | null
|
||||
providerKind: string
|
||||
prefix: string
|
||||
enabled: boolean
|
||||
maxConcurrency: number
|
||||
requestsPerMinute: number
|
||||
createdAt: string
|
||||
updatedAt: string
|
||||
lastUsedAt: string | null
|
||||
}
|
||||
|
||||
export interface ApiKeyListResponse {
|
||||
configRevision: number
|
||||
items: ApiKey[]
|
||||
nextCursor: string | null
|
||||
total: number
|
||||
}
|
||||
|
||||
export interface ApiKeyCreateResponse {
|
||||
configRevision: number
|
||||
id: string
|
||||
prefix: string
|
||||
plaintextKey: string
|
||||
}
|
||||
|
||||
export interface ApiKeyRevealResponse {
|
||||
id: string
|
||||
plaintextKey: string
|
||||
}
|
||||
|
||||
export interface ApiKeyMutationResponse {
|
||||
configRevision: number
|
||||
id: string
|
||||
}
|
||||
|
||||
export function getApiKeys(data: object) {
|
||||
return request({
|
||||
return request<ApiKeyListResponse>({
|
||||
url: '/api/admin/client-keys',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -9,7 +47,7 @@ export function getApiKeys(data: object) {
|
||||
}
|
||||
|
||||
export function createApiKey(data: object) {
|
||||
return request({
|
||||
return request<ApiKeyCreateResponse>({
|
||||
url: '/api/admin/client-keys/create',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -17,7 +55,7 @@ export function createApiKey(data: object) {
|
||||
}
|
||||
|
||||
export function revealApiKey(data: object) {
|
||||
return request({
|
||||
return request<ApiKeyRevealResponse>({
|
||||
url: '/api/admin/client-keys/reveal',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -25,7 +63,7 @@ export function revealApiKey(data: object) {
|
||||
}
|
||||
|
||||
export function deleteApiKey(data: object) {
|
||||
return request({
|
||||
return request<ApiKeyMutationResponse>({
|
||||
url: '/api/admin/client-keys/delete',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -33,7 +71,7 @@ export function deleteApiKey(data: object) {
|
||||
}
|
||||
|
||||
export function disableApiKey(data: object) {
|
||||
return request({
|
||||
return request<ApiKeyMutationResponse>({
|
||||
url: '/api/admin/client-keys/disable',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -41,7 +79,7 @@ export function disableApiKey(data: object) {
|
||||
}
|
||||
|
||||
export function enableApiKey(data: object) {
|
||||
return request({
|
||||
return request<ApiKeyMutationResponse>({
|
||||
url: '/api/admin/client-keys/enable',
|
||||
method: 'POST',
|
||||
data,
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
import request from '../request'
|
||||
|
||||
export interface LoginResponse {
|
||||
expiresAt: string
|
||||
}
|
||||
|
||||
export interface AuthStatusResponse {
|
||||
authenticated: boolean
|
||||
}
|
||||
|
||||
export interface LogoutResponse {
|
||||
message: string
|
||||
}
|
||||
|
||||
export function login(data: object) {
|
||||
return request({
|
||||
return request<LoginResponse>({
|
||||
url: '/api/admin/auth/login',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -9,14 +21,14 @@ export function login(data: object) {
|
||||
}
|
||||
|
||||
export function getAuthStatus() {
|
||||
return request({
|
||||
return request<AuthStatusResponse>({
|
||||
url: '/api/admin/auth/status',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
export function logout() {
|
||||
return request({
|
||||
return request<LogoutResponse>({
|
||||
url: '/api/admin/auth/logout',
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
@@ -1,7 +1,214 @@
|
||||
import type { UsageRecord } from './usage'
|
||||
import request from '../request'
|
||||
|
||||
export type DashboardTrendKind = 'usage' | 'latency' | 'errors'
|
||||
|
||||
export type DashboardHealthStatus
|
||||
= 'future' | 'no_data' | 'unavailable' | 'unstable' | 'low_sample' | 'stable'
|
||||
|
||||
export interface DashboardTrendPoint {
|
||||
time: string
|
||||
bucket: string
|
||||
label: string
|
||||
requests: string
|
||||
requestsValue: number
|
||||
inputTokens: string
|
||||
inputTokensValue: number
|
||||
outputTokens: string
|
||||
outputTokensValue: number
|
||||
cachedTokens: string
|
||||
cachedTokensValue: number
|
||||
cacheHitRateValue: number
|
||||
tokensValue: number
|
||||
errors: string
|
||||
errorsValue: number
|
||||
latency: string
|
||||
latencyValue: number | null
|
||||
firstTokenLatency: string
|
||||
firstTokenLatencyValue: number | null
|
||||
maxLatency: string
|
||||
maxLatencyValue: number | null
|
||||
minLatency: string
|
||||
minLatencyValue: number | null
|
||||
successRate: string
|
||||
successRateValue: number | null
|
||||
}
|
||||
|
||||
export interface DashboardTrendSummaryItem {
|
||||
label: string
|
||||
value: string
|
||||
ratio: string | null
|
||||
}
|
||||
|
||||
export interface DashboardTrendResponse {
|
||||
kind: DashboardTrendKind
|
||||
points: DashboardTrendPoint[]
|
||||
summary: DashboardTrendSummaryItem[]
|
||||
}
|
||||
|
||||
export interface DashboardCards {
|
||||
credentials: {
|
||||
total: string
|
||||
totalValue: number
|
||||
available: string
|
||||
availableValue: number
|
||||
unavailable: string
|
||||
unavailableValue: number
|
||||
}
|
||||
traffic: {
|
||||
todayRequests: string
|
||||
todayRequestsValue: number
|
||||
yesterdayRequestsValue: number
|
||||
totalRequests: string
|
||||
}
|
||||
tokens: {
|
||||
todayTokens: string
|
||||
todayTokensValue: number
|
||||
yesterdayTokensValue: number
|
||||
totalTokens: string
|
||||
totalBillingAmountUsd: string
|
||||
}
|
||||
cache: {
|
||||
todayHitRate: string
|
||||
todayHitRateValue: number | null
|
||||
yesterdayHitRateValue: number | null
|
||||
totalHitRate: string
|
||||
totalCachedTokens: string
|
||||
averageFirstTokenLatencyMs: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface DashboardHealthTimelinePoint {
|
||||
time: string
|
||||
status: DashboardHealthStatus
|
||||
reliabilityDisplay: string
|
||||
successRequests: number
|
||||
failedRequests: number
|
||||
cancelledRequests: number
|
||||
incompleteRequests: number
|
||||
callerErrorRequests: number
|
||||
}
|
||||
|
||||
export interface DashboardHealthTimeline {
|
||||
title: string
|
||||
description: string
|
||||
reliabilityDisplay: string
|
||||
status: DashboardHealthStatus
|
||||
successRequests: number
|
||||
failedRequests: number
|
||||
cancelledRequests: number
|
||||
incompleteRequests: number
|
||||
callerErrorRequests: number
|
||||
points: DashboardHealthTimelinePoint[]
|
||||
}
|
||||
|
||||
export interface DashboardWireProfile {
|
||||
provider: string
|
||||
product: string
|
||||
version: string
|
||||
build?: string
|
||||
target: {
|
||||
osType: string
|
||||
osVersion: string
|
||||
arch: string
|
||||
terminal: string
|
||||
}
|
||||
userAgent: string
|
||||
attributes: Array<{ label: string, value: string }>
|
||||
verifiedAt?: string
|
||||
release?: {
|
||||
status: 'unchecked' | 'aligned' | 'review_required' | 'check_failed'
|
||||
checkedAt?: string
|
||||
latestVersion?: string
|
||||
latestBuild?: string
|
||||
publishedAt?: string
|
||||
minimumSystemVersion?: string
|
||||
hardwareRequirements?: string
|
||||
downloadUrl?: string
|
||||
downloadSize?: number
|
||||
signaturePresent?: boolean
|
||||
error?: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface DashboardAccountRequestBucket {
|
||||
bucketStart: string
|
||||
requestCount: number
|
||||
}
|
||||
|
||||
export interface DashboardAccountUsage {
|
||||
id: string
|
||||
provider: string
|
||||
authenticationKind: string
|
||||
email: string
|
||||
planType: string | null
|
||||
tokens: string
|
||||
requestCount: number
|
||||
requestBuckets: DashboardAccountRequestBucket[]
|
||||
quotaUsedPercent: number | null
|
||||
lastUsed: string
|
||||
}
|
||||
|
||||
export interface DashboardCredentialUsage {
|
||||
id: string
|
||||
displayName: string
|
||||
planType: string | null
|
||||
tokens: string
|
||||
tokensValue: number | null
|
||||
lastUsed: string
|
||||
provider: string
|
||||
availability: string
|
||||
requestCount: number
|
||||
}
|
||||
|
||||
export interface DashboardPoolSummary {
|
||||
total: number
|
||||
active: number
|
||||
expired: number
|
||||
quotaExhausted: number
|
||||
refreshing: number | null
|
||||
disabled: number
|
||||
banned: number
|
||||
}
|
||||
|
||||
export interface DashboardCapacityInfo {
|
||||
maxConcurrentPerAccount: number
|
||||
totalSlots: number
|
||||
usedSlots: number | null
|
||||
availableSlots: number | null
|
||||
}
|
||||
|
||||
export interface DashboardRequestMetrics {
|
||||
requestCount: number
|
||||
successCount: number
|
||||
failureCount: number
|
||||
cancelledCount: number
|
||||
incompleteCount: number
|
||||
callerErrorCount: number
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
cachedTokens: number
|
||||
cacheWriteTokens: number
|
||||
reasoningTokens: number
|
||||
totalTokens: number
|
||||
}
|
||||
|
||||
export interface DashboardSummaryResponse {
|
||||
cards: DashboardCards
|
||||
trend: DashboardTrendResponse
|
||||
healthTimeline: DashboardHealthTimeline
|
||||
wireProfiles: DashboardWireProfile[]
|
||||
accountUsage: DashboardAccountUsage[]
|
||||
credentialUsage: DashboardCredentialUsage[]
|
||||
usageRecords: UsageRecord[]
|
||||
poolSummary: DashboardPoolSummary
|
||||
capacityInfo: DashboardCapacityInfo
|
||||
rotationStrategy: string
|
||||
logicalRequests: DashboardRequestMetrics
|
||||
}
|
||||
|
||||
export function getDashboardSummary(data: object) {
|
||||
return request({
|
||||
return request<DashboardSummaryResponse>({
|
||||
url: '/api/admin/dashboard/summary',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -9,7 +216,7 @@ export function getDashboardSummary(data: object) {
|
||||
}
|
||||
|
||||
export function getDashboardTrend(data: object) {
|
||||
return request({
|
||||
return request<DashboardTrendResponse>({
|
||||
url: '/api/admin/dashboard/trend',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
|
||||
@@ -1,14 +1,42 @@
|
||||
import request from '../request'
|
||||
|
||||
export type RotationStrategy = 'smart' | 'quota_reset_priority' | 'round_robin' | 'sticky'
|
||||
|
||||
export interface RuntimeSettings {
|
||||
configRevision: number
|
||||
modelMappings: Record<string, string>
|
||||
refreshMarginSeconds: number
|
||||
refreshConcurrency: number
|
||||
maxConcurrentPerAccount: number
|
||||
requestIntervalMs: number
|
||||
rotationStrategy: RotationStrategy
|
||||
usageRetentionDays: number
|
||||
opsEventRetentionDays: number
|
||||
auditRetentionDays: number
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface AdminApiKeyStatus {
|
||||
exists: boolean
|
||||
}
|
||||
|
||||
export interface RegeneratedAdminApiKey {
|
||||
key: string
|
||||
}
|
||||
|
||||
export interface DeletedAdminApiKey {
|
||||
message: string
|
||||
}
|
||||
|
||||
export function getSettings() {
|
||||
return request({
|
||||
return request<RuntimeSettings>({
|
||||
url: '/api/admin/settings',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
export function updateSettings(data: object) {
|
||||
return request({
|
||||
return request<RuntimeSettings>({
|
||||
url: '/api/admin/settings/update',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -16,21 +44,21 @@ export function updateSettings(data: object) {
|
||||
}
|
||||
|
||||
export function getAdminApiKeyStatus() {
|
||||
return request({
|
||||
return request<AdminApiKeyStatus>({
|
||||
url: '/api/admin/settings/admin-api-key',
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
export function regenerateAdminApiKey() {
|
||||
return request({
|
||||
return request<RegeneratedAdminApiKey>({
|
||||
url: '/api/admin/settings/admin-api-key/regenerate',
|
||||
method: 'POST',
|
||||
})
|
||||
}
|
||||
|
||||
export function deleteAdminApiKey() {
|
||||
return request({
|
||||
return request<DeletedAdminApiKey>({
|
||||
url: '/api/admin/settings/admin-api-key/delete',
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
@@ -1,7 +1,49 @@
|
||||
import request from '../request'
|
||||
|
||||
export interface SystemVersion {
|
||||
version: string
|
||||
gitSha: string
|
||||
buildTime: string
|
||||
deploymentMode: string
|
||||
deploymentModeLabel: string
|
||||
updateChannel: string
|
||||
latestVersion: string
|
||||
hasUpdate: boolean
|
||||
updateCached: boolean
|
||||
updateWarning: string | null
|
||||
}
|
||||
|
||||
export interface SystemUpdateDetail {
|
||||
currentVersion: string
|
||||
latestVersion: string
|
||||
hasUpdate: boolean
|
||||
deploymentMode: string
|
||||
deploymentModeLabel: string
|
||||
buildType: string
|
||||
buildTypeLabel: string
|
||||
releaseUrl: string | null
|
||||
notes: string | null
|
||||
cached: boolean
|
||||
updateSupported: boolean
|
||||
unsupportedReason: string | null
|
||||
warning: string | null
|
||||
}
|
||||
|
||||
export interface SystemUpdateAccepted {
|
||||
operationId: string
|
||||
deploymentMode: string
|
||||
message: string
|
||||
needRestart: boolean
|
||||
targetVersion: string
|
||||
}
|
||||
|
||||
export interface SystemRestartAccepted {
|
||||
message: string
|
||||
operationId: string
|
||||
}
|
||||
|
||||
export function getSystemVersion(timeout = 0) {
|
||||
return request({
|
||||
return request<SystemVersion>({
|
||||
url: '/api/admin/system/version',
|
||||
method: 'GET',
|
||||
...(timeout ? { timeout } : {}),
|
||||
@@ -9,7 +51,7 @@ export function getSystemVersion(timeout = 0) {
|
||||
}
|
||||
|
||||
export function getSystemUpdateDetail(data: object) {
|
||||
return request({
|
||||
return request<SystemUpdateDetail>({
|
||||
url: '/api/admin/system/update/detail',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -17,7 +59,7 @@ export function getSystemUpdateDetail(data: object) {
|
||||
}
|
||||
|
||||
export function performSystemUpdate(data: object) {
|
||||
return request({
|
||||
return request<SystemUpdateAccepted>({
|
||||
url: '/api/admin/system/update',
|
||||
method: 'POST',
|
||||
data,
|
||||
@@ -25,7 +67,7 @@ export function performSystemUpdate(data: object) {
|
||||
}
|
||||
|
||||
export function restartSystem() {
|
||||
return request({
|
||||
return request<SystemRestartAccepted>({
|
||||
url: '/api/admin/system/restart',
|
||||
method: 'POST',
|
||||
})
|
||||
|
||||
@@ -1,7 +1,331 @@
|
||||
import request from '../request'
|
||||
|
||||
export interface UsagePageMeta {
|
||||
page: number
|
||||
pageSize: number
|
||||
total: number
|
||||
totalPages: number
|
||||
}
|
||||
|
||||
export interface UsageTokenDetails {
|
||||
inputTokens: number | null
|
||||
outputTokens: number | null
|
||||
cachedTokens: number | null
|
||||
cacheWriteTokens: number | null
|
||||
reasoningTokens: number | null
|
||||
imageInputTokens: number | null
|
||||
imageOutputTokens: number | null
|
||||
totalTokens: number | null
|
||||
inputTokensDisplay: string
|
||||
outputTokensDisplay: string
|
||||
cachedTokensDisplay: string
|
||||
cacheWriteTokensDisplay: string
|
||||
reasoningTokensDisplay: string
|
||||
imageInputTokensDisplay: string
|
||||
imageOutputTokensDisplay: string
|
||||
totalTokensDisplay: string
|
||||
}
|
||||
|
||||
export interface UsageCost {
|
||||
currency: string
|
||||
estimatedAmount: string
|
||||
}
|
||||
|
||||
export interface UsageCostCoverage {
|
||||
known: number
|
||||
partial: number
|
||||
unknown: number
|
||||
notBillable: number
|
||||
}
|
||||
|
||||
export interface UsageBilling {
|
||||
inputAmountDisplay: string
|
||||
outputAmountDisplay: string
|
||||
cacheReadAmountDisplay: string
|
||||
cacheWriteAmountDisplay: string
|
||||
standardAmountDisplay: string
|
||||
totalAmountDisplay: string
|
||||
inputPriceDisplay: string
|
||||
outputPriceDisplay: string
|
||||
cacheReadPriceDisplay: string
|
||||
cacheWritePriceDisplay: string
|
||||
serviceTierDisplay: string
|
||||
multiplierDisplay: string
|
||||
}
|
||||
|
||||
export interface UsageLatencyDetails {
|
||||
transportDecisionWaitMs?: number
|
||||
wsConnectMs?: number
|
||||
upstreamHeadersMs?: number
|
||||
firstEventMs?: number
|
||||
firstReasoningMs?: number
|
||||
firstTextMs?: number
|
||||
firstTokenMs?: number
|
||||
openaiProcessingMs?: number
|
||||
}
|
||||
|
||||
// 已知字段之外还会平铺 Provider 安全观测字段,因此保留索引签名。
|
||||
export interface UsageRecordMetadata {
|
||||
[key: string]: unknown
|
||||
protocol: string
|
||||
logicalOutcome: string
|
||||
attemptCount: number
|
||||
requestedModel: string
|
||||
upstreamModel?: string
|
||||
clientIp?: string
|
||||
userAgent?: string
|
||||
reasoningEffort?: string
|
||||
reasoningPreset?: string
|
||||
compact: boolean
|
||||
requestKind?: string
|
||||
subagentKind?: string
|
||||
transport?: string
|
||||
httpVersion?: string
|
||||
clientStatusCode?: number
|
||||
upstreamStatusCode?: number
|
||||
responseId?: string
|
||||
upstreamRequestId?: string
|
||||
websocketPool: { kind: string } | null
|
||||
imageGenerationRequested: boolean
|
||||
imageGenerationSucceeded: boolean | null
|
||||
latencyDetails: UsageLatencyDetails
|
||||
}
|
||||
|
||||
export interface UsageRecord {
|
||||
id: string
|
||||
requestId: string
|
||||
clientApiKeyId: string | null
|
||||
kind: string
|
||||
provider: string | null
|
||||
authenticationKind: string | null
|
||||
accountId: string | null
|
||||
accountEmail: string | null
|
||||
route: string
|
||||
model: string
|
||||
requestedModel: string | null
|
||||
upstreamModel: string | null
|
||||
serviceTier: string | null
|
||||
statusCode: number | null
|
||||
transport: string | null
|
||||
attemptIndex: number | null
|
||||
attemptCount: number
|
||||
responseId: string | null
|
||||
upstreamRequestId: string | null
|
||||
latencyMs: number | null
|
||||
firstTokenMs: number | null
|
||||
inputTokens: number | null
|
||||
outputTokens: number | null
|
||||
cachedTokens: number | null
|
||||
cacheWriteTokens: number | null
|
||||
reasoningTokens: number | null
|
||||
imageInputTokens: number | null
|
||||
imageOutputTokens: number | null
|
||||
message: string
|
||||
metadata: UsageRecordMetadata
|
||||
createdAt: string
|
||||
createdAtDisplay: string
|
||||
clientIp: string | null
|
||||
userAgent: string | null
|
||||
reasoningEffort: string | null
|
||||
reasoningPreset: string | null
|
||||
compact: boolean | null
|
||||
requestKind: string | null
|
||||
subagentKind: string | null
|
||||
tokenDetails: UsageTokenDetails
|
||||
billing: UsageBilling | null
|
||||
costs: UsageCost[]
|
||||
costCoverage: UsageCostCoverage
|
||||
firstTokenLatencyMs: number | null
|
||||
firstTokenLatencyMsDisplay: string
|
||||
latencyMsDisplay: string
|
||||
logicalOutcome: string
|
||||
}
|
||||
|
||||
export interface UsageAttempt {
|
||||
id: string
|
||||
attemptIndex: number
|
||||
trigger: string
|
||||
provider: string
|
||||
model: string
|
||||
transport: string
|
||||
sendState: string
|
||||
outcome: string
|
||||
downstreamCommitted: boolean
|
||||
statusCode: number | null
|
||||
providerErrorCode: string | null
|
||||
failureClass: string | null
|
||||
costEstimateStatus: string
|
||||
estimatedCostAmount: string | null
|
||||
estimatedCostCurrency: string | null
|
||||
inputTokens: number | null
|
||||
outputTokens: number | null
|
||||
cachedTokens: number | null
|
||||
totalTokens: number | null
|
||||
firstTokenMs: number | null
|
||||
latencyMs: number | null
|
||||
credentialName: string | null
|
||||
accountEmail: string | null
|
||||
startedAt: string
|
||||
completedAt: string | null
|
||||
}
|
||||
|
||||
export type UsageRecordDetail = UsageRecord & {
|
||||
attempts: UsageAttempt[]
|
||||
}
|
||||
|
||||
export interface UsageRecordsResponse {
|
||||
items: UsageRecord[]
|
||||
page: UsagePageMeta
|
||||
nextCursor: string | null
|
||||
}
|
||||
|
||||
export interface OpsErrorMetadata {
|
||||
source: string
|
||||
component: string
|
||||
attemptId: string | null
|
||||
accountLabel: string | null
|
||||
}
|
||||
|
||||
export interface OpsError {
|
||||
id: string
|
||||
requestId: string | null
|
||||
clientApiKeyId: string | null
|
||||
kind: string
|
||||
provider: string | null
|
||||
accountId: string | null
|
||||
route: string
|
||||
model: string | null
|
||||
clientStatusCode: number | null
|
||||
upstreamStatusCode: number | null
|
||||
transport: string | null
|
||||
attemptIndex: number | null
|
||||
failureClass: string
|
||||
responseId: string | null
|
||||
upstreamRequestId: string | null
|
||||
latencyMs: number | null
|
||||
message: string
|
||||
metadata: OpsErrorMetadata
|
||||
createdAt: string
|
||||
createdAtDisplay: string
|
||||
}
|
||||
|
||||
export interface OpsErrorsResponse {
|
||||
items: OpsError[]
|
||||
page: UsagePageMeta
|
||||
nextCursor: string | null
|
||||
}
|
||||
|
||||
export interface UsageSummaryResponse {
|
||||
totalRequests: string
|
||||
inputTokens: string
|
||||
outputTokens: string
|
||||
cachedTokens: string
|
||||
cacheWriteTokens: string
|
||||
totalTokens: string
|
||||
averageLatencyMs: string
|
||||
}
|
||||
|
||||
export interface UsageOverviewHealthPoint {
|
||||
bucket: string
|
||||
label: string
|
||||
successRequests: number
|
||||
failedRequests: number
|
||||
cancelledRequests: number
|
||||
incompleteRequests: number
|
||||
callerErrorRequests: number
|
||||
errorRate: number
|
||||
}
|
||||
|
||||
export interface UsageOverviewHealth {
|
||||
totalRequests: number
|
||||
successRequests: number
|
||||
failedRequests: number
|
||||
cancelledRequests: number
|
||||
incompleteRequests: number
|
||||
callerErrorRequests: number
|
||||
successRate: number
|
||||
requestChangeRate: number | null
|
||||
successRateChange: number | null
|
||||
points: UsageOverviewHealthPoint[]
|
||||
}
|
||||
|
||||
export interface UsageOverviewPerformancePoint {
|
||||
bucket: string
|
||||
label: string
|
||||
latencyP50Ms: number | null
|
||||
latencyP95Ms: number | null
|
||||
latencyP99Ms: number | null
|
||||
firstTokenP50Ms: number | null
|
||||
firstTokenP95Ms: number | null
|
||||
firstTokenP99Ms: number | null
|
||||
}
|
||||
|
||||
export interface UsageOverviewPerformance {
|
||||
latencyP50Ms: number | null
|
||||
latencyP95Ms: number | null
|
||||
latencyP99Ms: number | null
|
||||
firstTokenP50Ms: number | null
|
||||
firstTokenP95Ms: number | null
|
||||
firstTokenP99Ms: number | null
|
||||
latencyCoverage: number
|
||||
firstTokenCoverage: number
|
||||
points: UsageOverviewPerformancePoint[]
|
||||
}
|
||||
|
||||
export interface UsageOverviewCostPoint {
|
||||
bucket: string
|
||||
label: string
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
cachedTokens: number
|
||||
totalTokens: number
|
||||
estimatedCost: string | null
|
||||
standardCost: string | null
|
||||
cachedTokenRate: number
|
||||
cacheHitRequestRate: number | null
|
||||
}
|
||||
|
||||
export interface UsageOverviewCost {
|
||||
estimatedCost: string | null
|
||||
standardCost: string | null
|
||||
costPerRequest: string | null
|
||||
tokensPerRequest: number
|
||||
cachedTokenRate: number
|
||||
cacheHitRequestRate: number | null
|
||||
inputTokens: number
|
||||
outputTokens: number
|
||||
cachedTokens: number
|
||||
totalTokens: number
|
||||
points: UsageOverviewCostPoint[]
|
||||
}
|
||||
|
||||
export interface UsageInsightsOverviewResponse {
|
||||
granularity: string
|
||||
health: UsageOverviewHealth
|
||||
performance: UsageOverviewPerformance
|
||||
cost: UsageOverviewCost
|
||||
}
|
||||
|
||||
export interface UsageDiagnosticItem {
|
||||
name: string
|
||||
requestCount: number
|
||||
successCount: number
|
||||
errorCount: number
|
||||
errorRate: number
|
||||
requestShare: number
|
||||
averageLatencyMs: number | null
|
||||
estimatedCost: string | null
|
||||
attemptCount: number
|
||||
totalTokens: number
|
||||
}
|
||||
|
||||
export interface UsageDiagnosticsResponse {
|
||||
dimension: string
|
||||
items: UsageDiagnosticItem[]
|
||||
}
|
||||
|
||||
export function getUsageRecords(data: object) {
|
||||
return request({
|
||||
return request<UsageRecordsResponse>({
|
||||
url: '/api/admin/usage/records',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -9,7 +333,7 @@ export function getUsageRecords(data: object) {
|
||||
}
|
||||
|
||||
export function getOpsErrors(data: object) {
|
||||
return request({
|
||||
return request<OpsErrorsResponse>({
|
||||
url: '/api/admin/operations/errors',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -17,7 +341,7 @@ export function getOpsErrors(data: object) {
|
||||
}
|
||||
|
||||
export function getUsageRecordDetail(data: object) {
|
||||
return request({
|
||||
return request<UsageRecordDetail>({
|
||||
url: '/api/admin/usage/records/detail',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -25,7 +349,7 @@ export function getUsageRecordDetail(data: object) {
|
||||
}
|
||||
|
||||
export function getUsageRecordSummary(data: object) {
|
||||
return request({
|
||||
return request<UsageSummaryResponse>({
|
||||
url: '/api/admin/usage/records/summary',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -33,7 +357,7 @@ export function getUsageRecordSummary(data: object) {
|
||||
}
|
||||
|
||||
export function getUsageRecordInsightsOverview(data: object) {
|
||||
return request({
|
||||
return request<UsageInsightsOverviewResponse>({
|
||||
url: '/api/admin/usage/insights/overview',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
@@ -41,7 +365,7 @@ export function getUsageRecordInsightsOverview(data: object) {
|
||||
}
|
||||
|
||||
export function getUsageRecordInsightsDiagnostics(data: object) {
|
||||
return request({
|
||||
return request<UsageDiagnosticsResponse>({
|
||||
url: '/api/admin/usage/insights/diagnostics',
|
||||
method: 'GET',
|
||||
params: data,
|
||||
|
||||
@@ -70,7 +70,13 @@ http.interceptors.response.use(
|
||||
},
|
||||
)
|
||||
|
||||
function isApiEnvelope(value: any) {
|
||||
interface ApiEnvelope {
|
||||
code: number
|
||||
message: string
|
||||
data: unknown
|
||||
}
|
||||
|
||||
function isApiEnvelope(value: unknown): value is ApiEnvelope {
|
||||
return (
|
||||
typeof value === 'object'
|
||||
&& value !== null
|
||||
@@ -80,14 +86,14 @@ function isApiEnvelope(value: any) {
|
||||
)
|
||||
}
|
||||
|
||||
export default async function request(config: AxiosRequestConfig) {
|
||||
const response = await http.request({
|
||||
export default async function request<T = unknown>(config: AxiosRequestConfig): Promise<T> {
|
||||
const response = await http.request<unknown>({
|
||||
...config,
|
||||
})
|
||||
|
||||
if (isApiEnvelope(response.data)) {
|
||||
return response.data.data
|
||||
return response.data.data as T
|
||||
}
|
||||
|
||||
return response.data
|
||||
return response.data as T
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { clamp, sumBy } from 'es-toolkit'
|
||||
|
||||
export type TableRow = Record<string, unknown>
|
||||
// 行类型保持结构化约束;动态列 key 的取值统一走 cellValue。
|
||||
export type TableRow = object
|
||||
|
||||
export interface BaseTableColumn<Row extends TableRow = TableRow> {
|
||||
key: string
|
||||
@@ -111,7 +112,7 @@ export function alignClass<Row extends TableRow>(column: BaseTableColumn<Row>) {
|
||||
}
|
||||
|
||||
export function cellValue(row: TableRow, key: string) {
|
||||
return row[key]
|
||||
return (row as Record<string, unknown>)[key]
|
||||
}
|
||||
|
||||
function isEmptyCellValue(value: unknown) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<script setup lang="ts" generic="Row extends Record<string, unknown> = Record<string, unknown>">
|
||||
<script setup lang="ts" generic="Row extends object = Record<string, unknown>">
|
||||
import type { BaseTableColumn, BaseTableSort, ResolvedTableColumn } from './columns'
|
||||
import type { BaseTablePagination as BaseTablePaginationConfig } from './pagination'
|
||||
|
||||
@@ -149,7 +149,7 @@ function getRowKey(row: Row, index: number) {
|
||||
return props.rowKey(row, index)
|
||||
}
|
||||
|
||||
const value = row[props.rowKey]
|
||||
const value = cellValue(row, props.rowKey)
|
||||
return typeof value === 'string' || typeof value === 'number' ? value : index
|
||||
}
|
||||
|
||||
|
||||
@@ -167,7 +167,7 @@ const requestBars = computed(() => {
|
||||
:aria-label="window.labelDisplay"
|
||||
aria-valuemin="0"
|
||||
aria-valuemax="100"
|
||||
:aria-valuenow="window.usedPercent"
|
||||
:aria-valuenow="window.usedPercent ?? undefined"
|
||||
>
|
||||
<div
|
||||
class="h-full rounded-full transition-[width,background-color] duration-200"
|
||||
|
||||
@@ -7,7 +7,8 @@ import { toast } from '@/components/base/BaseToast'
|
||||
import { errorMessage } from '@/utils/async'
|
||||
import { buildCodexCcSwitchImportDeeplink } from '../utils/ccswitchImport'
|
||||
|
||||
type ApiKeyRow = Awaited<ReturnType<typeof getApiKeys>>['items'][number]
|
||||
// “使用密钥”弹窗展示明文时,在列表行上补挂 reveal 得到的完整 key。
|
||||
type ApiKeyRow = Awaited<ReturnType<typeof getApiKeys>>['items'][number] & { key?: string }
|
||||
|
||||
// 密钥使用与 CCSwitch 导入编排:服务根地址推导、deeplink 跳转、
|
||||
// “使用密钥”弹窗的明文补全与打开。
|
||||
|
||||
@@ -14,7 +14,10 @@ export function useApiKeysQuery() {
|
||||
const pageSize = shallowRef(20)
|
||||
const total = shallowRef(0)
|
||||
const apiKeys = shallowRef<
|
||||
Array<Awaited<ReturnType<typeof getApiKeys>>['items'][number]>
|
||||
Array<Awaited<ReturnType<typeof getApiKeys>>['items'][number] & {
|
||||
createdAtDisplay: string
|
||||
lastUsedAtDisplay: string
|
||||
}>
|
||||
>([])
|
||||
const loading = shallowRef(false)
|
||||
const configRevision = shallowRef(0)
|
||||
|
||||
@@ -140,7 +140,7 @@ const statusBars = computed(() => {
|
||||
if (!p || p.total === 0)
|
||||
return []
|
||||
const active = (p.active / p.total) * 100
|
||||
const refreshing = (p.refreshing / p.total) * 100
|
||||
const refreshing = ((p.refreshing ?? 0) / p.total) * 100
|
||||
const quota = (p.quotaExhausted / p.total) * 100
|
||||
const unavailable = ((p.expired + p.disabled + p.banned) / p.total) * 100
|
||||
return [
|
||||
|
||||
@@ -82,7 +82,13 @@ const sortedItems = computed(() =>
|
||||
),
|
||||
)
|
||||
|
||||
const displayItems = computed(() =>
|
||||
// P95 列消费的 latencyP95Ms 目前不在诊断聚合响应中,缺失时渲染占位符。
|
||||
type DiagnosticDisplayItem = Diagnostics['items'][number] & {
|
||||
nameDisplay: ReturnType<typeof diagnosticNameDisplay>
|
||||
latencyP95Ms?: number
|
||||
}
|
||||
|
||||
const displayItems = computed<DiagnosticDisplayItem[]>(() =>
|
||||
sortedItems.value.map(item => ({
|
||||
...item,
|
||||
nameDisplay: diagnosticNameDisplay(item.name),
|
||||
|
||||
Reference in New Issue
Block a user