Files
ftb-project-management/apps/web/lib/api.ts
2026-06-25 15:21:32 +08:00

67 lines
2.4 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.

const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001/api/v1';
let apiAvailable: boolean | null = null;
let probePromise: Promise<boolean> | null = null;
async function checkApi(): Promise<boolean> {
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<T>(path: string, options?: RequestInit): Promise<T> {
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: <T>(path: string) => request<T>(path),
post: <T>(path: string, data: unknown) =>
request<T>(path, { method: 'POST', body: JSON.stringify(data) }),
put: <T>(path: string, data: unknown) =>
request<T>(path, { method: 'PUT', body: JSON.stringify(data) }),
patch: <T>(path: string, data: unknown) =>
request<T>(path, { method: 'PATCH', body: JSON.stringify(data) }),
delete: <T>(path: string) => request<T>(path, { method: 'DELETE' }),
postRaw: async <T>(path: string, data: unknown, timeoutMs = 120000): Promise<T> => {
// 调用 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);
}
},
};