import { setCredentials } from '@/features/auth/authSlice'; import Constants from 'expo-constants'; import * as SecureStore from 'expo-secure-store'; import { Platform } from 'react-native'; import Toast from 'react-native-toast-message'; import { useAuth } from '../contexts/auth-context'; import { store } from '../store'; import { User } from '../types/user'; // 定义错误码常量 const ERROR_CODES = { UNAUTHORIZED: 1004010001, } as const; export interface ApiResponse { code: number; data: T | null; message: string | null; } export interface PagedResult { items: T[]; has_more: boolean; } // 获取.env文件中的变量 export const API_ENDPOINT = process.env.EXPO_PUBLIC_API_ENDPOINT || Constants.expoConfig?.extra?.API_ENDPOINT; // 更新 access_token 的逻辑 - 用于React组件中 export const useAuthToken = async(message: string | null) => { try { const { login } = useAuth(); const response = await fetch(`${API_ENDPOINT}/v1/iam/access-token-refresh`); const apiResponse: ApiResponse = await response.json(); // 如果接口报错,页面弹出来错误信息 if (apiResponse.code != 0) { if (Platform.OS === 'web') { Toast.show({ type: 'error', text1: apiResponse.message || 'Request failed' }); } 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(message: string | null): Promise => { try { let cookie = ""; let userId = ""; if (Platform.OS === 'web') { cookie = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user') || "")?.refresh_token || "" : ""; userId = localStorage.getItem('user') ? JSON.parse(localStorage.getItem('user') || "")?.user_id || "" : ""; } else { await SecureStore.getItemAsync('user').then((user: string | null) => { cookie = JSON.parse(user || "")?.refresh_token || ""; userId = JSON.parse(user || "")?.user_id || ""; }) } // 退出刷新会重新填充数据 let response; response = await fetch(`${API_ENDPOINT}/v1/iam/access-token-refresh`, { method: "POST", body: JSON.stringify({ "refresh_token": cookie, "user_id": userId }), headers: { 'Content-Type': 'application/json', } }); const apiResponse: ApiResponse = await response.json(); if (apiResponse.code != 0) { 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 })); if (Platform.OS === 'web') { localStorage.setItem('user', JSON.stringify(userData)); localStorage.setItem('token', userData.access_token); } else { SecureStore.setItemAsync('user', JSON.stringify(userData)); SecureStore.setItemAsync('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 ( path: string, options: RequestInit = {}, needToast = true, needToken = true, ): Promise => { // console.log("API_ENDPOINT", Constants.expoConfig?.extra?.API_ENDPOINT); const makeRequest = async (isRetry = false): Promise> => { try { let token = ""; if (Platform.OS === 'web') { token = localStorage.getItem('token') || ""; } else { token = await SecureStore.getItemAsync('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 && needToken) { headers.set('Authorization', `Bearer ${token}`); } const url = `${API_ENDPOINT}/v1${path}`; const response = await fetch(url, { ...options, headers, }); const apiResponse: ApiResponse = await response.json(); // 处理未授权错误(需要刷新 token) if (apiResponse.code === ERROR_CODES.UNAUTHORIZED && !isRetry) { await refreshAuthToken(apiResponse.message); return makeRequest(true); // 重试请求 } // 处理其他错误 if (apiResponse.code !== 0) { // 如果是web端则显示提示 if (Platform.OS === 'web') { Toast.show({ type: 'error', text1: apiResponse.message || 'Request failed' }); } 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); } };