139 lines
4.3 KiB
TypeScript
139 lines
4.3 KiB
TypeScript
import supabase from './supabase';
|
||
|
||
// Define response type for API
|
||
export interface ApiResponse<T = unknown> {
|
||
success: boolean;
|
||
data?: T;
|
||
error?: string;
|
||
message?: string;
|
||
}
|
||
|
||
/**
|
||
* 通用的LIMQ API请求函数,包含重试机制和错误处理
|
||
*/
|
||
export async function limqRequest<T = unknown>(
|
||
endpoint: string,
|
||
method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
|
||
data?: Record<string, unknown>,
|
||
options?: {
|
||
retryCount?: number;
|
||
retryDelay?: number;
|
||
timeout?: number;
|
||
}
|
||
): Promise<ApiResponse<T>> {
|
||
// 默认配置
|
||
const retryCount = options?.retryCount ?? 2; // 默认重试2次
|
||
const retryDelay = options?.retryDelay ?? 1000; // 默认延迟1秒
|
||
const timeout = options?.timeout ?? 10000; // 默认超时10秒
|
||
|
||
let lastError: Error | null = null;
|
||
let currentRetry = 0;
|
||
|
||
// 创建延迟函数
|
||
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
||
|
||
// 重试循环
|
||
while (currentRetry <= retryCount) {
|
||
try {
|
||
console.log(`[API] ${method} ${endpoint} 尝试 ${currentRetry + 1}/${retryCount + 1}`);
|
||
|
||
// 获取会话
|
||
const { data: { session } } = await supabase.auth.getSession();
|
||
|
||
// 检查会话是否存在
|
||
if (!session) {
|
||
console.error(`[API] 未找到活跃会话,用户需要登录`);
|
||
|
||
if (currentRetry < retryCount) {
|
||
currentRetry++;
|
||
console.log(`[API] 等待 ${retryDelay}ms 后重试获取会话...`);
|
||
await delay(retryDelay);
|
||
continue;
|
||
}
|
||
|
||
return {
|
||
success: false,
|
||
error: '需要登录才能访问API'
|
||
};
|
||
}
|
||
|
||
// 获取API基础URL
|
||
const baseUrl = process.env.NEXT_PUBLIC_LIMQ_API;
|
||
if (!baseUrl) {
|
||
throw new Error('API URL未配置');
|
||
}
|
||
|
||
const url = `${baseUrl}${endpoint.startsWith('/') ? endpoint : '/' + endpoint}`;
|
||
console.log(`[API] 请求URL: ${url}`);
|
||
|
||
// 构建请求选项
|
||
const fetchOptions: RequestInit = {
|
||
method,
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${session.access_token}`
|
||
},
|
||
mode: 'cors'
|
||
};
|
||
|
||
if (data && (method === 'POST' || method === 'PUT')) {
|
||
fetchOptions.body = JSON.stringify(data);
|
||
console.log(`[API] 请求数据:`, data);
|
||
}
|
||
|
||
// 添加超时控制
|
||
const controller = new AbortController();
|
||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||
fetchOptions.signal = controller.signal;
|
||
|
||
// 发送请求
|
||
const response = await fetch(url, fetchOptions);
|
||
clearTimeout(timeoutId); // 清除超时控制
|
||
|
||
// 处理响应
|
||
if (!response.ok) {
|
||
const errorData = await response.json().catch(() => null);
|
||
console.error(`[API] 请求失败: ${response.status} ${response.statusText}`, errorData);
|
||
|
||
// 对于认证错误,尝试重试
|
||
if ((response.status === 401 || response.status === 403) && currentRetry < retryCount) {
|
||
currentRetry++;
|
||
console.log(`[API] 认证错误,等待 ${retryDelay}ms 后重试...`);
|
||
await delay(retryDelay);
|
||
continue;
|
||
}
|
||
|
||
throw new Error(errorData?.error || `请求失败: ${response.status}`);
|
||
}
|
||
|
||
// 成功响应
|
||
const responseData = await response.json();
|
||
console.log(`[API] ${method} ${endpoint} 成功`);
|
||
return responseData;
|
||
|
||
} catch (error) {
|
||
lastError = error instanceof Error ? error : new Error(String(error));
|
||
console.error(`[API] 请求出错:`, lastError);
|
||
|
||
// 对于超时和网络错误,尝试重试
|
||
if (currentRetry < retryCount &&
|
||
(error instanceof DOMException && error.name === 'AbortError' ||
|
||
error instanceof TypeError && error.message.includes('network'))) {
|
||
currentRetry++;
|
||
console.log(`[API] 网络错误,等待 ${retryDelay}ms 后重试...`);
|
||
await delay(retryDelay);
|
||
continue;
|
||
}
|
||
|
||
// 已达到最大重试次数或不是网络错误
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 所有重试均失败
|
||
console.error(`[API] ${method} ${endpoint} 失败,已重试 ${currentRetry} 次`);
|
||
return {
|
||
success: false,
|
||
error: lastError?.message || '请求失败,请稍后重试'
|
||
};
|
||
}
|