1. Frontend: replace hardcoded antigravityDefaultMappings with async fetch from GET /admin/accounts/antigravity/default-model-mapping, eliminating the duplicate data source that caused frontend/backend mapping inconsistency. 2. Backend: convert handleSmartRetry and antigravityRetryLoop from standalone functions to AntigravityGatewayService methods, enabling Redis cache sync (updateAccountModelRateLimitInCache) after both rate-limit write paths — long-delay branch and retry-exhausted branch.
431 lines
11 KiB
TypeScript
431 lines
11 KiB
TypeScript
/**
|
|
* Admin Accounts API endpoints
|
|
* Handles AI platform account management for administrators
|
|
*/
|
|
|
|
import { apiClient } from '../client'
|
|
import type {
|
|
Account,
|
|
CreateAccountRequest,
|
|
UpdateAccountRequest,
|
|
PaginatedResponse,
|
|
AccountUsageInfo,
|
|
WindowStats,
|
|
ClaudeModel,
|
|
AccountUsageStatsResponse,
|
|
TempUnschedulableStatus,
|
|
AdminDataPayload,
|
|
AdminDataImportResult
|
|
} from '@/types'
|
|
|
|
/**
|
|
* List all accounts with pagination
|
|
* @param page - Page number (default: 1)
|
|
* @param pageSize - Items per page (default: 20)
|
|
* @param filters - Optional filters
|
|
* @returns Paginated list of accounts
|
|
*/
|
|
export async function list(
|
|
page: number = 1,
|
|
pageSize: number = 20,
|
|
filters?: {
|
|
platform?: string
|
|
type?: string
|
|
status?: string
|
|
search?: string
|
|
},
|
|
options?: {
|
|
signal?: AbortSignal
|
|
}
|
|
): Promise<PaginatedResponse<Account>> {
|
|
const { data } = await apiClient.get<PaginatedResponse<Account>>('/admin/accounts', {
|
|
params: {
|
|
page,
|
|
page_size: pageSize,
|
|
...filters
|
|
},
|
|
signal: options?.signal
|
|
})
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Get account by ID
|
|
* @param id - Account ID
|
|
* @returns Account details
|
|
*/
|
|
export async function getById(id: number): Promise<Account> {
|
|
const { data } = await apiClient.get<Account>(`/admin/accounts/${id}`)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Create new account
|
|
* @param accountData - Account data
|
|
* @returns Created account
|
|
*/
|
|
export async function create(accountData: CreateAccountRequest): Promise<Account> {
|
|
const { data } = await apiClient.post<Account>('/admin/accounts', accountData)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Update account
|
|
* @param id - Account ID
|
|
* @param updates - Fields to update
|
|
* @returns Updated account
|
|
*/
|
|
export async function update(id: number, updates: UpdateAccountRequest): Promise<Account> {
|
|
const { data } = await apiClient.put<Account>(`/admin/accounts/${id}`, updates)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Delete account
|
|
* @param id - Account ID
|
|
* @returns Success confirmation
|
|
*/
|
|
export async function deleteAccount(id: number): Promise<{ message: string }> {
|
|
const { data } = await apiClient.delete<{ message: string }>(`/admin/accounts/${id}`)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Toggle account status
|
|
* @param id - Account ID
|
|
* @param status - New status
|
|
* @returns Updated account
|
|
*/
|
|
export async function toggleStatus(id: number, status: 'active' | 'inactive'): Promise<Account> {
|
|
return update(id, { status })
|
|
}
|
|
|
|
/**
|
|
* Test account connectivity
|
|
* @param id - Account ID
|
|
* @returns Test result
|
|
*/
|
|
export async function testAccount(id: number): Promise<{
|
|
success: boolean
|
|
message: string
|
|
latency_ms?: number
|
|
}> {
|
|
const { data } = await apiClient.post<{
|
|
success: boolean
|
|
message: string
|
|
latency_ms?: number
|
|
}>(`/admin/accounts/${id}/test`)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Refresh account credentials
|
|
* @param id - Account ID
|
|
* @returns Updated account
|
|
*/
|
|
export async function refreshCredentials(id: number): Promise<Account> {
|
|
const { data } = await apiClient.post<Account>(`/admin/accounts/${id}/refresh`)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Get account usage statistics
|
|
* @param id - Account ID
|
|
* @param days - Number of days (default: 30)
|
|
* @returns Account usage statistics with history, summary, and models
|
|
*/
|
|
export async function getStats(id: number, days: number = 30): Promise<AccountUsageStatsResponse> {
|
|
const { data } = await apiClient.get<AccountUsageStatsResponse>(`/admin/accounts/${id}/stats`, {
|
|
params: { days }
|
|
})
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Clear account error
|
|
* @param id - Account ID
|
|
* @returns Updated account
|
|
*/
|
|
export async function clearError(id: number): Promise<Account> {
|
|
const { data } = await apiClient.post<Account>(`/admin/accounts/${id}/clear-error`)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Get account usage information (5h/7d window)
|
|
* @param id - Account ID
|
|
* @returns Account usage info
|
|
*/
|
|
export async function getUsage(id: number): Promise<AccountUsageInfo> {
|
|
const { data } = await apiClient.get<AccountUsageInfo>(`/admin/accounts/${id}/usage`)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Clear account rate limit status
|
|
* @param id - Account ID
|
|
* @returns Success confirmation
|
|
*/
|
|
export async function clearRateLimit(id: number): Promise<{ message: string }> {
|
|
const { data } = await apiClient.post<{ message: string }>(
|
|
`/admin/accounts/${id}/clear-rate-limit`
|
|
)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Get temporary unschedulable status
|
|
* @param id - Account ID
|
|
* @returns Status with detail state if active
|
|
*/
|
|
export async function getTempUnschedulableStatus(id: number): Promise<TempUnschedulableStatus> {
|
|
const { data } = await apiClient.get<TempUnschedulableStatus>(
|
|
`/admin/accounts/${id}/temp-unschedulable`
|
|
)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Reset temporary unschedulable status
|
|
* @param id - Account ID
|
|
* @returns Success confirmation
|
|
*/
|
|
export async function resetTempUnschedulable(id: number): Promise<{ message: string }> {
|
|
const { data } = await apiClient.delete<{ message: string }>(
|
|
`/admin/accounts/${id}/temp-unschedulable`
|
|
)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Generate OAuth authorization URL
|
|
* @param endpoint - API endpoint path
|
|
* @param config - Proxy configuration
|
|
* @returns Auth URL and session ID
|
|
*/
|
|
export async function generateAuthUrl(
|
|
endpoint: string,
|
|
config: { proxy_id?: number }
|
|
): Promise<{ auth_url: string; session_id: string }> {
|
|
const { data } = await apiClient.post<{ auth_url: string; session_id: string }>(endpoint, config)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Exchange authorization code for tokens
|
|
* @param endpoint - API endpoint path
|
|
* @param exchangeData - Session ID, code, and optional proxy config
|
|
* @returns Token information
|
|
*/
|
|
export async function exchangeCode(
|
|
endpoint: string,
|
|
exchangeData: { session_id: string; code: string; proxy_id?: number }
|
|
): Promise<Record<string, unknown>> {
|
|
const { data } = await apiClient.post<Record<string, unknown>>(endpoint, exchangeData)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Batch create accounts
|
|
* @param accounts - Array of account data
|
|
* @returns Results of batch creation
|
|
*/
|
|
export async function batchCreate(accounts: CreateAccountRequest[]): Promise<{
|
|
success: number
|
|
failed: number
|
|
results: Array<{ success: boolean; account?: Account; error?: string }>
|
|
}> {
|
|
const { data } = await apiClient.post<{
|
|
success: number
|
|
failed: number
|
|
results: Array<{ success: boolean; account?: Account; error?: string }>
|
|
}>('/admin/accounts/batch', { accounts })
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Batch update credentials fields for multiple accounts
|
|
* @param request - Batch update request containing account IDs, field name, and value
|
|
* @returns Results of batch update
|
|
*/
|
|
export async function batchUpdateCredentials(request: {
|
|
account_ids: number[]
|
|
field: string
|
|
value: any
|
|
}): Promise<{
|
|
success: number
|
|
failed: number
|
|
results: Array<{ account_id: number; success: boolean; error?: string }>
|
|
}> {
|
|
const { data } = await apiClient.post<{
|
|
success: number
|
|
failed: number
|
|
results: Array<{ account_id: number; success: boolean; error?: string }>
|
|
}>('/admin/accounts/batch-update-credentials', request)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Bulk update multiple accounts
|
|
* @param accountIds - Array of account IDs
|
|
* @param updates - Fields to update
|
|
* @returns Success confirmation
|
|
*/
|
|
export async function bulkUpdate(
|
|
accountIds: number[],
|
|
updates: Record<string, unknown>
|
|
): Promise<{
|
|
success: number
|
|
failed: number
|
|
success_ids?: number[]
|
|
failed_ids?: number[]
|
|
results: Array<{ account_id: number; success: boolean; error?: string }>
|
|
}> {
|
|
const { data } = await apiClient.post<{
|
|
success: number
|
|
failed: number
|
|
success_ids?: number[]
|
|
failed_ids?: number[]
|
|
results: Array<{ account_id: number; success: boolean; error?: string }>
|
|
}>('/admin/accounts/bulk-update', {
|
|
account_ids: accountIds,
|
|
...updates
|
|
})
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Get account today statistics
|
|
* @param id - Account ID
|
|
* @returns Today's stats (requests, tokens, cost)
|
|
*/
|
|
export async function getTodayStats(id: number): Promise<WindowStats> {
|
|
const { data } = await apiClient.get<WindowStats>(`/admin/accounts/${id}/today-stats`)
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Set account schedulable status
|
|
* @param id - Account ID
|
|
* @param schedulable - Whether the account should participate in scheduling
|
|
* @returns Updated account
|
|
*/
|
|
export async function setSchedulable(id: number, schedulable: boolean): Promise<Account> {
|
|
const { data } = await apiClient.post<Account>(`/admin/accounts/${id}/schedulable`, {
|
|
schedulable
|
|
})
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Get available models for an account
|
|
* @param id - Account ID
|
|
* @returns List of available models for this account
|
|
*/
|
|
export async function getAvailableModels(id: number): Promise<ClaudeModel[]> {
|
|
const { data } = await apiClient.get<ClaudeModel[]>(`/admin/accounts/${id}/models`)
|
|
return data
|
|
}
|
|
|
|
export async function syncFromCrs(params: {
|
|
base_url: string
|
|
username: string
|
|
password: string
|
|
sync_proxies?: boolean
|
|
}): Promise<{
|
|
created: number
|
|
updated: number
|
|
skipped: number
|
|
failed: number
|
|
items: Array<{
|
|
crs_account_id: string
|
|
kind: string
|
|
name: string
|
|
action: string
|
|
error?: string
|
|
}>
|
|
}> {
|
|
const { data } = await apiClient.post('/admin/accounts/sync/crs', params)
|
|
return data
|
|
}
|
|
|
|
export async function exportData(options?: {
|
|
ids?: number[]
|
|
filters?: {
|
|
platform?: string
|
|
type?: string
|
|
status?: string
|
|
search?: string
|
|
}
|
|
includeProxies?: boolean
|
|
}): Promise<AdminDataPayload> {
|
|
const params: Record<string, string> = {}
|
|
if (options?.ids && options.ids.length > 0) {
|
|
params.ids = options.ids.join(',')
|
|
} else if (options?.filters) {
|
|
const { platform, type, status, search } = options.filters
|
|
if (platform) params.platform = platform
|
|
if (type) params.type = type
|
|
if (status) params.status = status
|
|
if (search) params.search = search
|
|
}
|
|
if (options?.includeProxies === false) {
|
|
params.include_proxies = 'false'
|
|
}
|
|
const { data } = await apiClient.get<AdminDataPayload>('/admin/accounts/data', { params })
|
|
return data
|
|
}
|
|
|
|
export async function importData(payload: {
|
|
data: AdminDataPayload
|
|
skip_default_group_bind?: boolean
|
|
}): Promise<AdminDataImportResult> {
|
|
const { data } = await apiClient.post<AdminDataImportResult>('/admin/accounts/data', {
|
|
data: payload.data,
|
|
skip_default_group_bind: payload.skip_default_group_bind
|
|
})
|
|
return data
|
|
}
|
|
|
|
/**
|
|
* Get Antigravity default model mapping from backend
|
|
* @returns Default model mapping (from -> to)
|
|
*/
|
|
export async function getAntigravityDefaultModelMapping(): Promise<Record<string, string>> {
|
|
const { data } = await apiClient.get<Record<string, string>>(
|
|
'/admin/accounts/antigravity/default-model-mapping'
|
|
)
|
|
return data
|
|
}
|
|
|
|
export const accountsAPI = {
|
|
list,
|
|
getById,
|
|
create,
|
|
update,
|
|
delete: deleteAccount,
|
|
toggleStatus,
|
|
testAccount,
|
|
refreshCredentials,
|
|
getStats,
|
|
clearError,
|
|
getUsage,
|
|
getTodayStats,
|
|
clearRateLimit,
|
|
getTempUnschedulableStatus,
|
|
resetTempUnschedulable,
|
|
setSchedulable,
|
|
getAvailableModels,
|
|
generateAuthUrl,
|
|
exchangeCode,
|
|
batchCreate,
|
|
batchUpdateCredentials,
|
|
bulkUpdate,
|
|
syncFromCrs,
|
|
exportData,
|
|
importData,
|
|
getAntigravityDefaultModelMapping
|
|
}
|
|
|
|
export default accountsAPI
|