let refreshPromise = null; const AUTH_KEYS = ['token', 'role', 'departmentId', 'userId']; export function getToken() { return localStorage.getItem('token'); } export function isAuthenticatedAsAdmin() { const role = localStorage.getItem('role'); return getToken() && role === 'ADMIN'; } export function isAuthenticatedAsRole(expectedRole) { const role = localStorage.getItem('role'); return getToken() && role === expectedRole; } export function isAuthenticatedAsAny(roles) { const role = localStorage.getItem('role'); return getToken() && roles.includes(role); } function getHeaders(contentType = 'application/json') { const headers = {}; const token = getToken(); if (token) { headers.Authorization = `Bearer ${token}`; } if (contentType) { headers['Content-Type'] = contentType; } return headers; } export async function apiFetch(endpoint, method = 'GET', body = null, retryOnUnauthorized = true) { const options = { method, headers: getHeaders(body ? 'application/json' : null), credentials: 'same-origin' }; if (body) { options.body = JSON.stringify(body); } const response = await fetch(endpoint, options); let data; try { data = await response.json(); } catch (e) { data = null; } if (response.status === 401 && retryOnUnauthorized) { const refreshed = await refreshAccessToken(); if (refreshed) { return apiFetch(endpoint, method, body, false); } clearAuthState(); window.location.href = '/'; } if (!response.ok) { throw new Error(data?.message || `Ошибка HTTP: ${response.status}`); } return data; } export async function refreshAccessToken() { if (refreshPromise) { return refreshPromise; } refreshPromise = (async () => { const response = await fetch('/api/auth/refresh', { method: 'POST', credentials: 'same-origin' }); const data = await response.json().catch(() => null); if (!response.ok || !data?.token) { return false; } storeAuthState(data); return true; })().finally(() => { refreshPromise = null; }); return refreshPromise; } export async function logout() { try { await fetch('/api/auth/logout', { method: 'POST', credentials: 'same-origin' }); } catch (e) { console.warn('Не удалось завершить серверную сессию:', e.message); } finally { clearAuthState(); } } export function storeAuthState(data) { if (data.token) localStorage.setItem('token', data.token); if (data.role) localStorage.setItem('role', data.role); if (data.departmentId !== undefined && data.departmentId !== null) { localStorage.setItem('departmentId', data.departmentId); } if (data.userId !== undefined && data.userId !== null) { localStorage.setItem('userId', data.userId); } } export function clearAuthState() { AUTH_KEYS.forEach(key => localStorage.removeItem(key)); } export const api = { get: (url) => apiFetch(url, 'GET'), post: (url, body) => apiFetch(url, 'POST', body), put: (url, body) => apiFetch(url, 'PUT', body), delete: (url, body = null) => apiFetch(url, 'DELETE', body) };