const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001/api/v1'; let apiAvailable: boolean | null = null; let probePromise: Promise | null = null; async function checkApi(): Promise { if (apiAvailable !== null) return apiAvailable; if (probePromise) return probePromise; probePromise = (async () => { try { // 探测一个不依赖数据库的端点(/config/ai 总是返回 200,只要 NestJS 起来了) const res = await fetch(`${API_BASE}/config/ai`, { method: 'GET', signal: AbortSignal.timeout(1500) }); apiAvailable = res.ok; } catch { apiAvailable = false; } return apiAvailable; })(); return probePromise; } async function request(path: string, options?: RequestInit): Promise { const available = await checkApi(); if (!available) throw new Error('API 不可用'); const res = await fetch(`${API_BASE}${path}`, { headers: { 'Content-Type': 'application/json', ...options?.headers }, ...options, }).catch(() => null); if (!res) throw new Error('API 不可用'); if (!res.ok) { const error = await res.json().catch(() => ({})); throw new Error(error.message || `请求失败: ${res.status}`); } return res.json(); } export const api = { get: (path: string) => request(path), post: (path: string, data: unknown) => request(path, { method: 'POST', body: JSON.stringify(data) }), put: (path: string, data: unknown) => request(path, { method: 'PUT', body: JSON.stringify(data) }), patch: (path: string, data: unknown) => request(path, { method: 'PATCH', body: JSON.stringify(data) }), delete: (path: string) => request(path, { method: 'DELETE' }), postRaw: async (path: string, data: unknown, timeoutMs = 120000): Promise => { // 调用 AI 类长耗时接口时使用,跳过 checkApi 短路(确保走真实请求) const controller = new AbortController(); const tid = setTimeout(() => controller.abort(), timeoutMs); try { const res = await fetch(`${API_BASE}${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), signal: controller.signal, }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(err.message || `请求失败: ${res.status}`); } return res.json(); } finally { clearTimeout(tid); } }, };