import axios from 'axios'; import { Config } from '../conf/config'; import api from './client'; import { clearAuthTokens } from './utils/token'; import { createSingleFlight } from './utils/tokenRefresh'; export const AuthApi = { async login(username, password) { const { data } = await api.post('/auth/login', { username, password }); localStorage.setItem('access_token', data.access_token); localStorage.setItem('refresh_token', data.refresh_token); try { window.dispatchEvent(new CustomEvent('auth:changed', { detail: { loggedIn: true } })); } catch (error) { console.error('Ошибка входа', error); } return data; }, async refresh() { const refresh_token = localStorage.getItem('refresh_token'); if (!refresh_token) { throw new Error('Нет refresh-токена'); } // "Голый" axios, в обход клиента api и его интерсепторов — иначе // 401 на /auth/refresh сам же запустит повторное обновление токена. const { data } = await axios.post(`${Config.API_URL}/api/v1/auth/refresh`, { refresh_token }); localStorage.setItem('access_token', data.access_token); localStorage.setItem('refresh_token', data.refresh_token); try { window.dispatchEvent(new CustomEvent('auth:refreshed')); } catch (error) { console.error('Ошибка обновления токена', error); } return data; }, logout() { localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); try { window.dispatchEvent(new CustomEvent('auth:changed', { detail: { loggedIn: false } })); } catch (error) { console.error('Ошибка при выходе', error); } }, }; // Пока обновление токена в полёте, все одновременные вызыватели получают // один и тот же Promise — иначе параллельные 401 запускают по refresh-запросу каждый. // Неудача означает, что сессию больше не восстановить: токены чистим и шлём // auth:expired централизованно здесь, чтобы не дублировать это в каждом вызывающем месте. export const refreshTokens = createSingleFlight(async () => { try { return await AuthApi.refresh(); } catch (error) { clearAuthTokens(); try { window.dispatchEvent(new CustomEvent('auth:expired')); } catch (dispatchError) { console.error('Ошибка при оповещении об истечении сессии', dispatchError); } throw error; } });