PicoBot/web/src/api/client.ts
oudecheng ffc1f79de4 refactor: 建立前端 API 客户端层,消除硬编码路径与重复 fetch
- 新增 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
- 保持现有组件行为不变(静默失败仍静默,错误处理等价)
2026-07-08 10:03:34 +08:00

57 lines
1.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 统一的 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
}
}