fix(auth): ����-������ ������ ������ ��� 401 (����� ���������� ����� ���) #22
@ -3,7 +3,7 @@
|
||||
* Бэкенд: storys-микросервис, префикс /kubikon3d/...
|
||||
*/
|
||||
import axios from 'axios';
|
||||
import { STORYS_addres } from './API';
|
||||
import { STORYS_addres, USER_addres } from './API';
|
||||
|
||||
const api = axios.create({
|
||||
baseURL: STORYS_addres,
|
||||
@ -29,6 +29,61 @@ api.interceptors.request.use((config) => {
|
||||
return config;
|
||||
});
|
||||
|
||||
// ── Авто-рефреш access-токена при 401 (2026-06-02) ──────────────────────
|
||||
// Access-токен живёт 24ч. Раньше студия не рефрешила его → после суток работы
|
||||
// сохранение проектов падало 401 «через раз» (зависело от свежести токена).
|
||||
// Теперь при 401 один раз пытаемся обновить access по refreshToken через
|
||||
// /api/v1/auth/refresh и повторяем исходный запрос. single-flight: при пачке
|
||||
// параллельных 401 рефреш идёт ОДИН раз, остальные ждут его.
|
||||
let _refreshPromise = null;
|
||||
async function _refreshAccessToken() {
|
||||
const refreshToken = localStorage.getItem('RefreshToken');
|
||||
if (!refreshToken) return null;
|
||||
if (_refreshPromise) return _refreshPromise;
|
||||
_refreshPromise = (async () => {
|
||||
try {
|
||||
// Отдельный axios без интерсепторов — чтобы не зациклить на 401.
|
||||
const r = await axios.post(`${USER_addres}/api/v1/users/auth/refresh`,
|
||||
{ refreshToken }, { timeout: 15000 });
|
||||
const newToken = r.data && r.data.token;
|
||||
const newRefresh = r.data && r.data.refreshToken;
|
||||
if (newToken) {
|
||||
localStorage.setItem('Authorization', newToken);
|
||||
localStorage.setItem('player_jwt', newToken);
|
||||
if (newRefresh) localStorage.setItem('RefreshToken', newRefresh);
|
||||
return newToken;
|
||||
}
|
||||
return null;
|
||||
} catch (e) {
|
||||
// refresh протух (30 дней) → чистим, юзер перелогинится через минку.
|
||||
try { localStorage.removeItem('RefreshToken'); } catch (e2) { /* ignore */ }
|
||||
return null;
|
||||
} finally {
|
||||
_refreshPromise = null;
|
||||
}
|
||||
})();
|
||||
return _refreshPromise;
|
||||
}
|
||||
|
||||
api.interceptors.response.use(
|
||||
(resp) => resp,
|
||||
async (error) => {
|
||||
const cfg = error && error.config;
|
||||
const status = error && error.response && error.response.status;
|
||||
// Только 401 и только один раз на запрос (флаг _retried).
|
||||
if (status === 401 && cfg && !cfg._retried) {
|
||||
cfg._retried = true;
|
||||
const newToken = await _refreshAccessToken();
|
||||
if (newToken) {
|
||||
cfg.headers = cfg.headers || {};
|
||||
cfg.headers.Authorization = newToken;
|
||||
return api(cfg); // повторяем исходный запрос со свежим токеном
|
||||
}
|
||||
}
|
||||
return Promise.reject(error);
|
||||
},
|
||||
);
|
||||
|
||||
// ============ ПРОЕКТЫ ============
|
||||
|
||||
// Save-операции с увеличенным таймаутом (120с) — для больших карт.
|
||||
|
||||
@ -119,6 +119,10 @@ export function AuthProvider({ children }) {
|
||||
try {
|
||||
localStorage.setItem('Authorization', jwt);
|
||||
localStorage.setItem('player_jwt', jwt);
|
||||
// 2026-06-02: redeem теперь отдаёт refreshToken — сохраняем,
|
||||
// чтобы axios-интерсептор мог обновить истёкший access (24ч)
|
||||
// и сохранение проектов не падало 401 после суток работы.
|
||||
if (data.refreshToken) localStorage.setItem('RefreshToken', data.refreshToken);
|
||||
} catch {}
|
||||
startWithJwt(jwt);
|
||||
} else {
|
||||
|
||||
@ -16,6 +16,13 @@ import Icon from './Icon';
|
||||
function Hotbar({ visible, slots = [], activeIndex = 0, onSelect, mobileMode = false }) {
|
||||
if (!visible) return null;
|
||||
|
||||
// ГЛОБАЛЬНОЕ ПРАВИЛО (для всех игр тулбокса): если в инвентаре нет ни
|
||||
// одного предмета — панель инвентаря НЕ показываем вовсе. Пустой hotbar
|
||||
// из 5 серых ячеек загромождает экран в играх, где инвентарь не нужен.
|
||||
// Панель появится автоматически, как только в слот попадёт предмет.
|
||||
const hasAnyItem = Array.isArray(slots) && slots.some((s) => s != null);
|
||||
if (!hasAnyItem) return null;
|
||||
|
||||
const SLOT_COUNT = 5;
|
||||
const cells = [];
|
||||
for (let i = 0; i < SLOT_COUNT; i++) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user