memowake-front/lib/server-api-util.ts
2025-06-20 18:20:31 +08:00

142 lines
4.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.

import { useAuth } from '@/contexts/auth-context';
import { setCredentials } from '@/features/auth/authSlice';
import { store } from '@/store';
import { User } from '@/types/user';
// 定义错误码常量
const ERROR_CODES = {
UNAUTHORIZED: 1004010001,
} as const;
export interface ApiResponse<T> {
code: number;
data: T | null;
message: string | null;
}
export interface PagedResult<T> {
items: T[];
has_more: boolean;
}
// 获取.env文件中的变量
export const API_ENDPOINT = "http://192.168.31.115:18080/api"
// 更新 access_token 的逻辑 - 用于React组件中
export const useAuthToken = async<T>(message: string | null) => {
try {
const { login } = useAuth();
const response = await fetch(`${API_ENDPOINT}/v1/iam/access-token-refresh`);
const apiResponse: ApiResponse<T> = await response.json();
// 如果接口报错,页面弹出来错误信息
if (apiResponse.code != 0) {
console.log(message || 'Unknown error');
throw new Error(message || 'Unknown error');
} else {
const userData = apiResponse.data as User;
// 接口未报错通过auth-context文件的login函数来更新access token
login(userData, userData.access_token || "");
}
return apiResponse;
} catch (error) {
console.error(`Error fetching data from /iam/access-token-refresh:`, error);
throw error;
}
}
// 使用Redux存储token的刷新token函数
export const refreshAuthToken = async<T>(message: string | null): Promise<User> => {
try {
// 退出刷新会重新填充数据
let response;
response = await fetch(`${API_ENDPOINT}/v1/iam/access-token-refresh`);
const apiResponse: ApiResponse<T> = await response.json();
if (apiResponse.code != 0) {
// addToast({
// title: message || 'Unknown error',
// color: "danger",
// })
throw new Error(message || 'Unknown error');
}
const userData = apiResponse.data as User;
if (userData && userData.access_token) {
// 使用Redux更新token
store.dispatch(setCredentials({
user: userData,
token: userData.access_token
}));
}
return userData;
} catch (error) {
console.error(`Error refreshing token:`, error);
throw error;
}
}
// 处理 API 错误
const handleApiError = (error: unknown, needToast = true, defaultMessage = 'Unknown error') => {
const message = error instanceof Error ? error.message : defaultMessage;
if (needToast) {
console.log(message);
}
throw new Error(message);
};
// 处理必须携带 token 的接口
export const fetchApi = async <T>(
path: string,
options: RequestInit = {},
needToast = true
): Promise<T> => {
const makeRequest = async (isRetry = false): Promise<ApiResponse<T>> => {
try {
const token = store.getState().auth.token;
const headers = new Headers(options.headers);
// 添加必要的 headers
if (options.method !== 'GET' && !headers.has('Content-Type')) {
headers.set('Content-Type', 'application/json');
}
if (token != null) {
headers.set('Authorization', `Bearer ${token}`);
}
const url = `${API_ENDPOINT}/v1${path}`;
const response = await fetch(url, {
...options,
headers,
});
const apiResponse: ApiResponse<T> = await response.json();
// 处理未授权错误(需要刷新 token
if (apiResponse.code === ERROR_CODES.UNAUTHORIZED && !isRetry) {
await refreshAuthToken(apiResponse.message);
return makeRequest(true); // 重试请求
}
// 处理其他错误
if (apiResponse.code !== 0) {
throw new Error(apiResponse.message || 'Request failed');
}
return apiResponse;
} catch (error) {
if (error instanceof Error) {
throw error;
}
throw new Error('An unknown error occurred');
}
};
try {
const response = await makeRequest();
return response.data || ({} as T);
} catch (error) {
return handleApiError(error, needToast);
}
};