import axios, { AxiosResponse, Method, ResponseType } from "axios"; import { getAuthToken, setAuthToken } from "../stores/auth"; export interface MakeRequestConfig { getAuthToken: () => string | undefined; setAuthToken: (token: string) => void; refreshUrl: string; logoutFn: () => void; handleComponentError?: (error: Error, info?: any) => void; clearAuthDataFn?: () => void; clearErrorHandlingConfig?: () => void; allowedDomains?: string[]; debugSecretKey?: string; logErrorFn?: (message: string, error?: any) => void; } let makeRequestConfig: MakeRequestConfig | null = null; export function createMakeRequestConfig( getAuthToken: () => string | undefined, setAuthToken: (token: string) => void, refreshUrl: string, logoutFn?: () => void, handleComponentError?: (error: Error, info?: any) => void, clearAuthDataFn?: () => void, clearErrorHandlingConfig?: () => void, allowedDomains?: string[], debugSecretKey?: string, logErrorFn?: (message: string, error?: any) => void, ) { makeRequestConfig = { getAuthToken, setAuthToken, refreshUrl, logoutFn: () => { clearMakeRequestConfig(); if (typeof clearErrorHandlingConfig === 'function') clearErrorHandlingConfig(); if (logoutFn) logoutFn(); }, handleComponentError, clearAuthDataFn, clearErrorHandlingConfig, allowedDomains, debugSecretKey, logErrorFn, }; } export function getMakeRequestConfig(): MakeRequestConfig | null { return makeRequestConfig; } export function clearMakeRequestConfig() { makeRequestConfig = null; } export async function makeRequest({ method = "post", url, body, useToken = true, contentType = false, responseType = "json", signal, withCredentials, }: { method?: Method; url: string; body?: TRequest; useToken?: boolean; contentType?: boolean; responseType?: ResponseType; signal?: AbortSignal; withCredentials?: boolean; }): Promise { const config = getMakeRequestConfig(); const headers: Record = {}; if (useToken) { const token = getAuthToken(); headers["Authorization"] = token ? `Bearer ${token}` : ""; } if (contentType) headers["Content-Type"] = "application/json"; try { const response = await axios>({ url, method, headers, data: body, signal, responseType, withCredentials, }); if (response.data?.accessToken) { setAuthToken(response.data.accessToken); } return response.data; } catch (error: any) { if (axios.isAxiosError(error) && error.response?.status === 401 && !withCredentials) { const refreshResponse = await refresh(getAuthToken()); if (axios.isAxiosError(refreshResponse) && error.response?.status === 401 && config !== null) { //токен так сильно сдох, что восстановлению не подлежит config.logoutFn(); throw new Error("Пожалуйста, войдите в свой профиль."); } if (refreshResponse.data?.accessToken) { setAuthToken(refreshResponse.data.accessToken); } headers["Authorization"] = refreshResponse.data.accessToken ? `Bearer ${refreshResponse.data.accessToken}` : ""; const response = await axios.request>({ url, method, headers, data: body, signal, }); return response.data; } // Централизованная обработка ошибок (400/500+) if ( error.response?.status && (error.response.status === 400 || error.response.status >= 500) && config?.handleComponentError ) { const errorMessage = `HTTP ${error.response.status}: ${error.response?.data?.message || error.message}`; const httpError = new Error(errorMessage); httpError.stack = error.stack; config.handleComponentError(httpError, { componentStack: null }); } // refreshToken is empty if ( error.response?.status === 400 && error.response?.data?.message === "refreshToken is empty" && config?.clearAuthDataFn ) { config.clearAuthDataFn(); } throw error; } } export async function refresh(token?: string): Promise> { if (!token) throw new Error("No refresh token provided"); return axios>(process.env.REACT_APP_DOMAIN + "/auth/refresh", { headers: { "Authorization": `Bearer ${token}`, "Content-Type": "application/json", }, method: "post" }); }