- 新增 web/src/api/ 目录(6 个文件):client.ts 基础设施 + 5 个领域模块 - 封装 18 处 fetch 调用为强类型函数,消除 17 处硬编码 /api/ 路径 - 统一 13 处重复的 fetch+json+错误处理模式 - 删除旧 web/src/components/Settings/api/expert.ts,合并到新 api/experts.ts - ConfigPage.tsx 替换 15 处 fetch 调用,ExpertSelector.tsx 改用新 API - 保持现有组件行为不变(静默失败仍静默,错误处理等价)
57 lines
1.8 KiB
TypeScript
57 lines
1.8 KiB
TypeScript
// 统一的 API 端点常量,消除硬编码字符串
|
||
export const API = {
|
||
config: '/api/config',
|
||
restart: '/api/restart',
|
||
health: '/health',
|
||
mcpStatus: '/api/mcp/status',
|
||
skills: '/api/skills',
|
||
skillsToggle: '/api/skills/toggle',
|
||
subagents: '/api/subagents',
|
||
subagentsToggle: '/api/subagents/toggle',
|
||
experts: '/api/experts',
|
||
expertsToggle: '/api/experts/toggle',
|
||
expertsCreate: '/api/experts/create',
|
||
expertsUpdate: '/api/experts/update',
|
||
expertsDelete: '/api/experts/delete',
|
||
expertsSelected: '/api/experts/selected',
|
||
expertsSelect: '/api/experts/select',
|
||
} as const
|
||
|
||
/**
|
||
* 基础 fetch 封装:自动添加 JSON headers,解析响应。
|
||
* 返回 [data, error] 元组,不抛异常。
|
||
*/
|
||
export async function apiFetch<T>(
|
||
endpoint: string,
|
||
options?: { method?: string; body?: unknown; signal?: AbortSignal }
|
||
): Promise<[T | null, { status: number; message: string } | null]> {
|
||
try {
|
||
const resp = await fetch(endpoint, {
|
||
method: options?.method ?? 'GET',
|
||
headers: options?.body ? { 'Content-Type': 'application/json' } : undefined,
|
||
body: options?.body ? JSON.stringify(options.body) : undefined,
|
||
signal: options?.signal,
|
||
})
|
||
const data = await resp.json().catch(() => null)
|
||
if (!resp.ok) {
|
||
return [null, { status: resp.status, message: data?.message || data?.error || `HTTP ${resp.status}` }]
|
||
}
|
||
return [data as T, null]
|
||
} catch (e) {
|
||
return [null, { status: 0, message: e instanceof Error ? e.message : 'Network error' }]
|
||
}
|
||
}
|
||
|
||
/**
|
||
* GET 请求,静默失败返回 null(用于列表加载等场景)。
|
||
*/
|
||
export async function apiGetSilent<T>(endpoint: string): Promise<T | null> {
|
||
try {
|
||
const resp = await fetch(endpoint)
|
||
if (!resp.ok) return null
|
||
return await resp.json() as T
|
||
} catch {
|
||
return null
|
||
}
|
||
}
|