fix(saas): P1 审计修复 — 连接池断路器 + Worker重试 + XSS防护 + 状态机SQL解析器
P1 修复内容: - F7: health handler 连接池容量检查 (80%阈值返回503 degraded) - F9: SSE spawned task 并发限制 (Semaphore 16 permits) - F10: Key Pool 单次 JOIN 查询优化 (消除 N+1) - F12: CORS panic → 配置错误 - F14: 连接池使用率计算修正 (ratio = used*100/total) - F15: SQL 迁移解析器替换为状态机 (支持 $$, DO $body$, 存储过程) - Worker 重试机制: 失败任务通过 mpsc channel 重新入队 - DOMPurify XSS 防护 (PipelineResultPreview) - Admin V2: ErrorBoundary + SWR全局配置 + 请求优化
This commit is contained in:
@@ -4,6 +4,7 @@ import { RouterProvider } from 'react-router-dom'
|
||||
import { ConfigProvider, App as AntApp } from 'antd'
|
||||
import zhCN from 'antd/locale/zh_CN'
|
||||
import { router } from './router'
|
||||
import { ErrorBoundary } from './components/ErrorBoundary'
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
@@ -16,11 +17,13 @@ const queryClient = new QueryClient({
|
||||
})
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<ConfigProvider locale={zhCN}>
|
||||
<AntApp>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RouterProvider router={router} />
|
||||
</QueryClientProvider>
|
||||
</AntApp>
|
||||
</ConfigProvider>,
|
||||
<ErrorBoundary>
|
||||
<ConfigProvider locale={zhCN}>
|
||||
<AntApp>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<RouterProvider router={router} />
|
||||
</QueryClientProvider>
|
||||
</AntApp>
|
||||
</ConfigProvider>
|
||||
</ErrorBoundary>,
|
||||
)
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function Accounts() {
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['accounts'],
|
||||
queryFn: () => accountService.list(),
|
||||
queryFn: ({ signal }) => accountService.list(signal),
|
||||
})
|
||||
|
||||
const updateMutation = useMutation({
|
||||
|
||||
@@ -26,7 +26,7 @@ export default function AgentTemplates() {
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['agent-templates'],
|
||||
queryFn: () => agentTemplateService.list(),
|
||||
queryFn: ({ signal }) => agentTemplateService.list(signal),
|
||||
})
|
||||
|
||||
const createMutation = useMutation({
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function ApiKeys() {
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['api-keys'],
|
||||
queryFn: () => apiKeyService.list(),
|
||||
queryFn: ({ signal }) => apiKeyService.list(signal),
|
||||
})
|
||||
|
||||
const createMutation = useMutation({
|
||||
|
||||
@@ -20,7 +20,7 @@ export default function Config() {
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['config', category],
|
||||
queryFn: () => configService.list({ category }),
|
||||
queryFn: ({ signal }) => configService.list({ category }, signal),
|
||||
})
|
||||
|
||||
const updateMutation = useMutation({
|
||||
|
||||
@@ -42,12 +42,12 @@ const actionColors: Record<string, string> = {
|
||||
export default function Dashboard() {
|
||||
const { data: stats, isLoading: statsLoading, error: statsError } = useQuery({
|
||||
queryKey: ['dashboard-stats'],
|
||||
queryFn: () => statsService.dashboard(),
|
||||
queryFn: ({ signal }) => statsService.dashboard(signal),
|
||||
})
|
||||
|
||||
const { data: logsData, isLoading: logsLoading } = useQuery({
|
||||
queryKey: ['recent-logs'],
|
||||
queryFn: () => logService.list({ page: 1, page_size: 10 }),
|
||||
queryFn: ({ signal }) => logService.list({ page: 1, page_size: 10 }, signal),
|
||||
})
|
||||
|
||||
if (statsError) {
|
||||
|
||||
@@ -42,7 +42,7 @@ export default function Logs() {
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['logs', page, actionFilter],
|
||||
queryFn: () => logService.list({ page, page_size: 20, action: actionFilter }),
|
||||
queryFn: ({ signal }) => logService.list({ page, page_size: 20, action: actionFilter }, signal),
|
||||
})
|
||||
|
||||
const columns: ProColumns<OperationLog>[] = [
|
||||
|
||||
@@ -20,12 +20,12 @@ export default function Models() {
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['models'],
|
||||
queryFn: () => modelService.list(),
|
||||
queryFn: ({ signal }) => modelService.list(signal),
|
||||
})
|
||||
|
||||
const { data: providersData } = useQuery({
|
||||
queryKey: ['providers-for-select'],
|
||||
queryFn: () => providerService.list(),
|
||||
queryFn: ({ signal }) => providerService.list(signal),
|
||||
})
|
||||
|
||||
const createMutation = useMutation({
|
||||
|
||||
@@ -26,18 +26,18 @@ export default function Prompts() {
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['prompts'],
|
||||
queryFn: () => promptService.list(),
|
||||
queryFn: ({ signal }) => promptService.list(signal),
|
||||
})
|
||||
|
||||
const { data: detailData } = useQuery({
|
||||
queryKey: ['prompt-detail', detailName],
|
||||
queryFn: () => promptService.get(detailName!),
|
||||
queryFn: ({ signal }) => promptService.get(detailName!, signal),
|
||||
enabled: !!detailName,
|
||||
})
|
||||
|
||||
const { data: versionsData } = useQuery({
|
||||
queryKey: ['prompt-versions', detailName],
|
||||
queryFn: () => promptService.listVersions(detailName!),
|
||||
queryFn: ({ signal }) => promptService.listVersions(detailName!, signal),
|
||||
enabled: !!detailName,
|
||||
})
|
||||
|
||||
|
||||
@@ -22,12 +22,12 @@ export default function Providers() {
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['providers'],
|
||||
queryFn: () => providerService.list(),
|
||||
queryFn: ({ signal }) => providerService.list(signal),
|
||||
})
|
||||
|
||||
const { data: keysData, isLoading: keysLoading } = useQuery({
|
||||
queryKey: ['provider-keys', keyModalProviderId],
|
||||
queryFn: () => providerService.listKeys(keyModalProviderId!),
|
||||
queryFn: ({ signal }) => providerService.listKeys(keyModalProviderId!, signal),
|
||||
enabled: !!keyModalProviderId,
|
||||
})
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export default function Relay() {
|
||||
|
||||
const { data, isLoading } = useQuery({
|
||||
queryKey: ['relay-tasks', page, statusFilter],
|
||||
queryFn: () => relayService.list({ page, page_size: 20, status: statusFilter }),
|
||||
queryFn: ({ signal }) => relayService.list({ page, page_size: 20, status: statusFilter }, signal),
|
||||
})
|
||||
|
||||
const columns: ProColumns<RelayTask>[] = [
|
||||
|
||||
@@ -19,12 +19,12 @@ export default function Usage() {
|
||||
|
||||
const { data: dailyData, isLoading: dailyLoading, error: dailyError } = useQuery({
|
||||
queryKey: ['usage-daily', days],
|
||||
queryFn: () => telemetryService.dailyStats({ days }),
|
||||
queryFn: ({ signal }) => telemetryService.dailyStats({ days }, signal),
|
||||
})
|
||||
|
||||
const { data: modelData, isLoading: modelLoading } = useQuery({
|
||||
queryKey: ['usage-model', days],
|
||||
queryFn: () => telemetryService.modelStats({}),
|
||||
queryFn: ({ signal }) => telemetryService.modelStats({}, signal),
|
||||
})
|
||||
|
||||
if (dailyError) {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { AccountPublic, PaginatedResponse } from '@/types'
|
||||
|
||||
export const accountService = {
|
||||
list: (params?: Record<string, unknown>) =>
|
||||
request.get<PaginatedResponse<AccountPublic>>('/accounts', { params }).then((r) => r.data),
|
||||
list: (params?: Record<string, unknown>, signal?: AbortSignal) =>
|
||||
request.get<PaginatedResponse<AccountPublic>>('/accounts', withSignal({ params }, signal)).then((r) => r.data),
|
||||
|
||||
get: (id: string) =>
|
||||
request.get<AccountPublic>(`/accounts/${id}`).then((r) => r.data),
|
||||
get: (id: string, signal?: AbortSignal) =>
|
||||
request.get<AccountPublic>(`/accounts/${id}`, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
update: (id: string, data: Partial<Pick<AccountPublic, 'display_name' | 'email' | 'role'>>) =>
|
||||
request.patch<AccountPublic>(`/accounts/${id}`, data).then((r) => r.data),
|
||||
update: (id: string, data: Partial<Pick<AccountPublic, 'display_name' | 'email' | 'role'>>, signal?: AbortSignal) =>
|
||||
request.patch<AccountPublic>(`/accounts/${id}`, data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
updateStatus: (id: string, data: { status: AccountPublic['status'] }) =>
|
||||
request.patch(`/accounts/${id}/status`, data).then((r) => r.data),
|
||||
updateStatus: (id: string, data: { status: AccountPublic['status'] }, signal?: AbortSignal) =>
|
||||
request.patch(`/accounts/${id}/status`, data, withSignal({}, signal)).then((r) => r.data),
|
||||
}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { AgentTemplate, PaginatedResponse } from '@/types'
|
||||
|
||||
export const agentTemplateService = {
|
||||
list: (params?: Record<string, unknown>) =>
|
||||
request.get<PaginatedResponse<AgentTemplate>>('/agent-templates', { params }).then((r) => r.data),
|
||||
list: (params?: Record<string, unknown>, signal?: AbortSignal) =>
|
||||
request.get<PaginatedResponse<AgentTemplate>>('/agent-templates', withSignal({ params }, signal)).then((r) => r.data),
|
||||
|
||||
get: (id: string) =>
|
||||
request.get<AgentTemplate>(`/agent-templates/${id}`).then((r) => r.data),
|
||||
get: (id: string, signal?: AbortSignal) =>
|
||||
request.get<AgentTemplate>(`/agent-templates/${id}`, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
create: (data: {
|
||||
name: string; description?: string; category?: string; source?: string
|
||||
model?: string; system_prompt?: string; tools?: string[]
|
||||
capabilities?: string[]; temperature?: number; max_tokens?: number
|
||||
visibility?: string
|
||||
}) =>
|
||||
request.post<AgentTemplate>('/agent-templates', data).then((r) => r.data),
|
||||
}, signal?: AbortSignal) =>
|
||||
request.post<AgentTemplate>('/agent-templates', data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
update: (id: string, data: {
|
||||
description?: string; model?: string; system_prompt?: string
|
||||
tools?: string[]; capabilities?: string[]; temperature?: number
|
||||
max_tokens?: number; visibility?: string; status?: string
|
||||
}) =>
|
||||
request.post<AgentTemplate>(`/agent-templates/${id}`, data).then((r) => r.data),
|
||||
}, signal?: AbortSignal) =>
|
||||
request.post<AgentTemplate>(`/agent-templates/${id}`, data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
archive: (id: string) =>
|
||||
request.delete<AgentTemplate>(`/agent-templates/${id}`).then((r) => r.data),
|
||||
archive: (id: string, signal?: AbortSignal) =>
|
||||
request.delete<AgentTemplate>(`/agent-templates/${id}`, withSignal({}, signal)).then((r) => r.data),
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { TokenInfo, CreateTokenRequest, PaginatedResponse } from '@/types'
|
||||
|
||||
export const apiKeyService = {
|
||||
list: (params?: Record<string, unknown>) =>
|
||||
request.get<PaginatedResponse<TokenInfo>>('/keys', { params }).then((r) => r.data),
|
||||
list: (params?: Record<string, unknown>, signal?: AbortSignal) =>
|
||||
request.get<PaginatedResponse<TokenInfo>>('/keys', withSignal({ params }, signal)).then((r) => r.data),
|
||||
|
||||
create: (data: CreateTokenRequest) =>
|
||||
request.post<TokenInfo>('/keys', data).then((r) => r.data),
|
||||
create: (data: CreateTokenRequest, signal?: AbortSignal) =>
|
||||
request.post<TokenInfo>('/keys', data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
revoke: (id: string) =>
|
||||
request.delete(`/keys/${id}`).then((r) => r.data),
|
||||
revoke: (id: string, signal?: AbortSignal) =>
|
||||
request.delete(`/keys/${id}`, withSignal({}, signal)).then((r) => r.data),
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { AccountPublic, LoginRequest, LoginResponse } from '@/types'
|
||||
|
||||
export const authService = {
|
||||
login: (data: LoginRequest) =>
|
||||
request.post<LoginResponse>('/auth/login', data).then((r) => r.data),
|
||||
login: (data: LoginRequest, signal?: AbortSignal) =>
|
||||
request.post<LoginResponse>('/auth/login', data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
me: () =>
|
||||
request.get<AccountPublic>('/auth/me').then((r) => r.data),
|
||||
me: (signal?: AbortSignal) =>
|
||||
request.get<AccountPublic>('/auth/me', withSignal({}, signal)).then((r) => r.data),
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { ConfigItem, PaginatedResponse } from '@/types'
|
||||
|
||||
export const configService = {
|
||||
list: (params?: Record<string, unknown>) =>
|
||||
request.get<PaginatedResponse<ConfigItem>>('/config/items', { params })
|
||||
list: (params?: Record<string, unknown>, signal?: AbortSignal) =>
|
||||
request.get<PaginatedResponse<ConfigItem>>('/config/items', withSignal({ params }, signal))
|
||||
.then((r) => r.data.items),
|
||||
|
||||
update: (id: string, data: { value: string | number | boolean }) =>
|
||||
request.patch<ConfigItem>(`/config/items/${id}`, data).then((r) => r.data),
|
||||
update: (id: string, data: { value: string | number | boolean }, signal?: AbortSignal) =>
|
||||
request.patch<ConfigItem>(`/config/items/${id}`, data, withSignal({}, signal)).then((r) => r.data),
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { OperationLog, PaginatedResponse } from '@/types'
|
||||
|
||||
export const logService = {
|
||||
list: (params?: Record<string, unknown>) =>
|
||||
request.get<PaginatedResponse<OperationLog>>('/logs/operations', { params }).then((r) => r.data),
|
||||
list: (params?: Record<string, unknown>, signal?: AbortSignal) =>
|
||||
request.get<PaginatedResponse<OperationLog>>('/logs/operations', withSignal({ params }, signal)).then((r) => r.data),
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { Model, PaginatedResponse } from '@/types'
|
||||
|
||||
export const modelService = {
|
||||
list: (params?: Record<string, unknown>) =>
|
||||
request.get<PaginatedResponse<Model>>('/models', { params }).then((r) => r.data),
|
||||
list: (params?: Record<string, unknown>, signal?: AbortSignal) =>
|
||||
request.get<PaginatedResponse<Model>>('/models', withSignal({ params }, signal)).then((r) => r.data),
|
||||
|
||||
create: (data: Partial<Omit<Model, 'id'>>) =>
|
||||
request.post<Model>('/models', data).then((r) => r.data),
|
||||
create: (data: Partial<Omit<Model, 'id'>>, signal?: AbortSignal) =>
|
||||
request.post<Model>('/models', data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
update: (id: string, data: Partial<Omit<Model, 'id'>>) =>
|
||||
request.patch<Model>(`/models/${id}`, data).then((r) => r.data),
|
||||
update: (id: string, data: Partial<Omit<Model, 'id'>>, signal?: AbortSignal) =>
|
||||
request.patch<Model>(`/models/${id}`, data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
delete: (id: string) =>
|
||||
request.delete(`/models/${id}`).then((r) => r.data),
|
||||
delete: (id: string, signal?: AbortSignal) =>
|
||||
request.delete(`/models/${id}`, withSignal({}, signal)).then((r) => r.data),
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { PromptTemplate, PromptVersion, PaginatedResponse } from '@/types'
|
||||
|
||||
export const promptService = {
|
||||
list: (params?: Record<string, unknown>) =>
|
||||
request.get<PaginatedResponse<PromptTemplate>>('/prompts', { params }).then((r) => r.data),
|
||||
list: (params?: Record<string, unknown>, signal?: AbortSignal) =>
|
||||
request.get<PaginatedResponse<PromptTemplate>>('/prompts', withSignal({ params }, signal)).then((r) => r.data),
|
||||
|
||||
get: (name: string) =>
|
||||
request.get<PromptTemplate>(`/prompts/${encodeURIComponent(name)}`).then((r) => r.data),
|
||||
get: (name: string, signal?: AbortSignal) =>
|
||||
request.get<PromptTemplate>(`/prompts/${encodeURIComponent(name)}`, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
create: (data: {
|
||||
name: string; category: string; description?: string; source?: string
|
||||
system_prompt: string; user_prompt_template?: string
|
||||
variables?: unknown[]; min_app_version?: string
|
||||
}) =>
|
||||
request.post<PromptTemplate>('/prompts', data).then((r) => r.data),
|
||||
}, signal?: AbortSignal) =>
|
||||
request.post<PromptTemplate>('/prompts', data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
update: (name: string, data: { description?: string; status?: string }) =>
|
||||
request.put<PromptTemplate>(`/prompts/${encodeURIComponent(name)}`, data).then((r) => r.data),
|
||||
update: (name: string, data: { description?: string; status?: string }, signal?: AbortSignal) =>
|
||||
request.put<PromptTemplate>(`/prompts/${encodeURIComponent(name)}`, data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
archive: (name: string) =>
|
||||
request.delete<PromptTemplate>(`/prompts/${encodeURIComponent(name)}`).then((r) => r.data),
|
||||
archive: (name: string, signal?: AbortSignal) =>
|
||||
request.delete<PromptTemplate>(`/prompts/${encodeURIComponent(name)}`, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
listVersions: (name: string) =>
|
||||
request.get<PromptVersion[]>(`/prompts/${encodeURIComponent(name)}/versions`).then((r) => r.data),
|
||||
listVersions: (name: string, signal?: AbortSignal) =>
|
||||
request.get<PromptVersion[]>(`/prompts/${encodeURIComponent(name)}/versions`, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
createVersion: (name: string, data: {
|
||||
system_prompt: string; user_prompt_template?: string
|
||||
variables?: unknown[]; changelog?: string; min_app_version?: string
|
||||
}) =>
|
||||
request.post<PromptVersion>(`/prompts/${encodeURIComponent(name)}/versions`, data).then((r) => r.data),
|
||||
}, signal?: AbortSignal) =>
|
||||
request.post<PromptVersion>(`/prompts/${encodeURIComponent(name)}/versions`, data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
rollback: (name: string, version: number) =>
|
||||
request.post<PromptTemplate>(`/prompts/${encodeURIComponent(name)}/rollback/${version}`).then((r) => r.data),
|
||||
rollback: (name: string, version: number, signal?: AbortSignal) =>
|
||||
request.post<PromptTemplate>(`/prompts/${encodeURIComponent(name)}/rollback/${version}`, undefined, withSignal({}, signal)).then((r) => r.data),
|
||||
}
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { Provider, ProviderKey, PaginatedResponse } from '@/types'
|
||||
|
||||
export const providerService = {
|
||||
list: (params?: Record<string, unknown>) =>
|
||||
request.get<PaginatedResponse<Provider>>('/providers', { params }).then((r) => r.data),
|
||||
list: (params?: Record<string, unknown>, signal?: AbortSignal) =>
|
||||
request.get<PaginatedResponse<Provider>>('/providers', withSignal({ params }, signal)).then((r) => r.data),
|
||||
|
||||
create: (data: Partial<Omit<Provider, 'id' | 'created_at' | 'updated_at'>>) =>
|
||||
request.post<Provider>('/providers', data).then((r) => r.data),
|
||||
create: (data: Partial<Omit<Provider, 'id' | 'created_at' | 'updated_at'>>, signal?: AbortSignal) =>
|
||||
request.post<Provider>('/providers', data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
update: (id: string, data: Partial<Omit<Provider, 'id' | 'created_at' | 'updated_at'>>) =>
|
||||
request.patch<Provider>(`/providers/${id}`, data).then((r) => r.data),
|
||||
update: (id: string, data: Partial<Omit<Provider, 'id' | 'created_at' | 'updated_at'>>, signal?: AbortSignal) =>
|
||||
request.patch<Provider>(`/providers/${id}`, data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
delete: (id: string) =>
|
||||
request.delete(`/providers/${id}`).then((r) => r.data),
|
||||
delete: (id: string, signal?: AbortSignal) =>
|
||||
request.delete(`/providers/${id}`, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
listKeys: (providerId: string) =>
|
||||
request.get<ProviderKey[]>(`/providers/${providerId}/keys`).then((r) => r.data),
|
||||
listKeys: (providerId: string, signal?: AbortSignal) =>
|
||||
request.get<ProviderKey[]>(`/providers/${providerId}/keys`, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
addKey: (providerId: string, data: {
|
||||
key_label: string; key_value: string; priority?: number
|
||||
max_rpm?: number; max_tpm?: number; quota_reset_interval?: string
|
||||
}) =>
|
||||
request.post<{ ok: boolean; key_id: string }>(`/providers/${providerId}/keys`, data).then((r) => r.data),
|
||||
}, signal?: AbortSignal) =>
|
||||
request.post<{ ok: boolean; key_id: string }>(`/providers/${providerId}/keys`, data, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
toggleKey: (providerId: string, keyId: string, active: boolean) =>
|
||||
request.put<{ ok: boolean }>(`/providers/${providerId}/keys/${keyId}/toggle`, { active }).then((r) => r.data),
|
||||
toggleKey: (providerId: string, keyId: string, active: boolean, signal?: AbortSignal) =>
|
||||
request.put<{ ok: boolean }>(`/providers/${providerId}/keys/${keyId}/toggle`, { active }, withSignal({}, signal)).then((r) => r.data),
|
||||
|
||||
deleteKey: (providerId: string, keyId: string) =>
|
||||
request.delete<{ ok: boolean }>(`/providers/${providerId}/keys/${keyId}`).then((r) => r.data),
|
||||
deleteKey: (providerId: string, keyId: string, signal?: AbortSignal) =>
|
||||
request.delete<{ ok: boolean }>(`/providers/${providerId}/keys/${keyId}`, withSignal({}, signal)).then((r) => r.data),
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { RelayTask, PaginatedResponse } from '@/types'
|
||||
|
||||
export const relayService = {
|
||||
list: (params?: Record<string, unknown>) =>
|
||||
request.get<PaginatedResponse<RelayTask>>('/relay/tasks', { params }).then((r) => r.data),
|
||||
list: (params?: Record<string, unknown>, signal?: AbortSignal) =>
|
||||
request.get<PaginatedResponse<RelayTask>>('/relay/tasks', withSignal({ params }, signal)).then((r) => r.data),
|
||||
|
||||
get: (id: string) =>
|
||||
request.get<RelayTask>(`/relay/tasks/${id}`).then((r) => r.data),
|
||||
get: (id: string, signal?: AbortSignal) =>
|
||||
request.get<RelayTask>(`/relay/tasks/${id}`, withSignal({}, signal)).then((r) => r.data),
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
import axios from 'axios'
|
||||
import type { AxiosError, InternalAxiosRequestConfig } from 'axios'
|
||||
import type { AxiosRequestConfig } from 'axios'
|
||||
import type { ApiError } from '@/types'
|
||||
import { useAuthStore } from '@/stores/authStore'
|
||||
|
||||
@@ -106,3 +107,11 @@ request.interceptors.response.use(
|
||||
)
|
||||
|
||||
export default request
|
||||
|
||||
/** 将 AbortSignal 注入 Axios config,用于 TanStack Query 的请求取消 */
|
||||
export function withSignal(config: AxiosRequestConfig = {}, signal?: AbortSignal): AxiosRequestConfig {
|
||||
if (signal) {
|
||||
return { ...config, signal }
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { DashboardStats } from '@/types'
|
||||
|
||||
export const statsService = {
|
||||
dashboard: () =>
|
||||
request.get<DashboardStats>('/stats/dashboard').then((r) => r.data),
|
||||
dashboard: (signal?: AbortSignal) =>
|
||||
request.get<DashboardStats>('/stats/dashboard', withSignal({}, signal)).then((r) => r.data),
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { ModelUsageStat, DailyUsageStat } from '@/types'
|
||||
|
||||
export const telemetryService = {
|
||||
modelStats: (params?: Record<string, unknown>) =>
|
||||
request.get<ModelUsageStat[]>('/telemetry/stats', { params }).then((r) => r.data),
|
||||
modelStats: (params?: Record<string, unknown>, signal?: AbortSignal) =>
|
||||
request.get<ModelUsageStat[]>('/telemetry/stats', withSignal({ params }, signal)).then((r) => r.data),
|
||||
|
||||
dailyStats: (params?: { days?: number }) =>
|
||||
request.get<DailyUsageStat[]>('/telemetry/daily', { params }).then((r) => r.data),
|
||||
dailyStats: (params?: { days?: number }, signal?: AbortSignal) =>
|
||||
request.get<DailyUsageStat[]>('/telemetry/daily', withSignal({ params }, signal)).then((r) => r.data),
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import request from './request'
|
||||
import request, { withSignal } from './request'
|
||||
import type { UsageRecord, UsageByModel } from '@/types'
|
||||
|
||||
export const usageService = {
|
||||
daily: (params?: { days?: number }) =>
|
||||
request.get<{ by_day: UsageRecord[] }>('/usage', { params: { ...params, group_by: 'day' } })
|
||||
daily: (params?: { days?: number }, signal?: AbortSignal) =>
|
||||
request.get<{ by_day: UsageRecord[] }>('/usage', withSignal({ params: { ...params, group_by: 'day' } }, signal))
|
||||
.then((r) => r.data.by_day || []),
|
||||
|
||||
byModel: (params?: { days?: number }) =>
|
||||
request.get<{ by_model: UsageByModel[] }>('/usage', { params: { ...params, group_by: 'model' } })
|
||||
byModel: (params?: { days?: number }, signal?: AbortSignal) =>
|
||||
request.get<{ by_model: UsageByModel[] }>('/usage', withSignal({ params: { ...params, group_by: 'model' } }, signal))
|
||||
.then((r) => r.data.by_model || []),
|
||||
}
|
||||
|
||||
@@ -15,6 +15,16 @@ export default defineConfig({
|
||||
'/api': {
|
||||
target: 'http://localhost:8080',
|
||||
changeOrigin: true,
|
||||
timeout: 30_000,
|
||||
proxyTimeout: 30_000,
|
||||
configure: (proxy) => {
|
||||
proxy.on('proxyReq', (proxyReq) => {
|
||||
proxyReq.setTimeout(30_000)
|
||||
})
|
||||
proxy.on('proxyRes', (proxyRes) => {
|
||||
proxyRes.setTimeout(30_000)
|
||||
})
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user