From 42d97a4312d0061c941992f91c873471587072bf Mon Sep 17 00:00:00 2001 From: Egor Date: Sun, 18 Jan 2026 05:20:49 +0300 Subject: [PATCH] Update client.ts --- src/api/client.ts | 86 +++++++++++------------------------------------ 1 file changed, 19 insertions(+), 67 deletions(-) diff --git a/src/api/client.ts b/src/api/client.ts index 40943c8..7788bb7 100644 --- a/src/api/client.ts +++ b/src/api/client.ts @@ -1,8 +1,11 @@ import axios, { AxiosError, InternalAxiosRequestConfig } from 'axios' -import { tokenStorage, isTokenExpired } from '../utils/token' +import { tokenStorage, isTokenExpired, tokenRefreshManager } from '../utils/token' const API_BASE_URL = import.meta.env.VITE_API_URL || '/api' +// Настраиваем endpoint для refresh +tokenRefreshManager.setRefreshEndpoint(`${API_BASE_URL}/cabinet/auth/refresh`) + const getTelegramInitData = (): string | null => { if (typeof window === 'undefined') return null @@ -22,61 +25,21 @@ export const apiClient = axios.create({ }, }) -// Флаг для предотвращения множественных refresh запросов -let isRefreshing = false -let refreshSubscribers: ((token: string) => void)[] = [] - -const subscribeTokenRefresh = (cb: (token: string) => void) => { - refreshSubscribers.push(cb) -} - -const onTokenRefreshed = (token: string) => { - refreshSubscribers.forEach((cb) => cb(token)) - refreshSubscribers = [] -} - -const refreshAccessToken = async (): Promise => { - const refreshToken = tokenStorage.getRefreshToken() - if (!refreshToken) return null - - try { - const response = await axios.post(`${API_BASE_URL}/cabinet/auth/refresh`, { - refresh_token: refreshToken, - }) - - const { access_token } = response.data - tokenStorage.setAccessToken(access_token) - return access_token - } catch { - tokenStorage.clearTokens() - window.location.href = '/login' - return null - } -} - // Request interceptor - add auth token with expiration check apiClient.interceptors.request.use(async (config: InternalAxiosRequestConfig) => { let token = tokenStorage.getAccessToken() // Проверяем срок действия токена перед запросом if (token && isTokenExpired(token)) { - // Токен истёк или скоро истечёт - обновляем - if (!isRefreshing) { - isRefreshing = true - const newToken = await refreshAccessToken() - isRefreshing = false - - if (newToken) { - token = newToken - onTokenRefreshed(newToken) - } else { - return config - } + // Используем централизованный менеджер для refresh + const newToken = await tokenRefreshManager.refreshAccessToken() + if (newToken) { + token = newToken } else { - // Другой запрос уже обновляет токен - ждём - token = await new Promise((resolve) => { - subscribeTokenRefresh(resolve) - }) + // Refresh не удался - редирект на логин + tokenStorage.clearTokens() + window.location.href = '/login' + return config } } @@ -101,27 +64,16 @@ apiClient.interceptors.response.use( if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true - if (!isRefreshing) { - isRefreshing = true - const newToken = await refreshAccessToken() - isRefreshing = false - - if (newToken) { - onTokenRefreshed(newToken) - if (originalRequest.headers) { - originalRequest.headers.Authorization = `Bearer ${newToken}` - } - return apiClient(originalRequest) - } - } else { - // Ждём завершения refresh от другого запроса - const token = await new Promise((resolve) => { - subscribeTokenRefresh(resolve) - }) + const newToken = await tokenRefreshManager.refreshAccessToken() + if (newToken) { if (originalRequest.headers) { - originalRequest.headers.Authorization = `Bearer ${token}` + originalRequest.headers.Authorization = `Bearer ${newToken}` } return apiClient(originalRequest) + } else { + // Refresh не удался + tokenStorage.clearTokens() + window.location.href = '/login' } }