142 lines
4.4 KiB
TypeScript
142 lines
4.4 KiB
TypeScript
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);
|
||
}
|
||
}; |