баг-фикс 30/34

This commit is contained in:
Zuev
2026-07-19 14:40:43 +03:00
parent 3d798c13e3
commit bc0e1ab1b4
172 changed files with 13431 additions and 2910 deletions

119
frontend/auth-session.js Normal file
View File

@@ -0,0 +1,119 @@
const LEGACY_AUTH_KEYS = ['token', 'role', 'departmentId', 'userId'];
let accessToken = null;
let authState = null;
let refreshPromise = null;
purgeLegacyAuthStorage();
export function getAccessToken() {
return accessToken;
}
export function getAuthState() {
return authState ? { ...authState } : null;
}
export function storeAuthState(data) {
if (!data?.token || !data?.role) {
clearAuthState();
return null;
}
accessToken = data.token;
authState = {
role: data.role,
departmentId: normalizeOptionalId(data.departmentId),
userId: normalizeOptionalId(data.userId),
redirect: typeof data.redirect === 'string' ? data.redirect : '/'
};
return getAuthState();
}
export function clearAuthState() {
accessToken = null;
authState = null;
}
export async function restoreSession() {
if (accessToken && authState) {
return getAuthState();
}
const refreshed = await refreshAccessToken();
return refreshed ? getAuthState() : null;
}
export async function refreshAccessToken() {
if (refreshPromise) {
return refreshPromise;
}
refreshPromise = (async () => {
let response;
try {
response = await fetch('/api/auth/refresh', {
method: 'POST',
credentials: 'same-origin'
});
} catch (error) {
clearAuthState();
return false;
}
const data = await response.json().catch(() => null);
if (!response.ok || !storeAuthState(data)) {
clearAuthState();
return false;
}
return true;
})().finally(() => {
refreshPromise = null;
});
return refreshPromise;
}
export async function fetchWithAuth(endpoint, options = {}, retryOnUnauthorized = true) {
const headers = new Headers(options.headers || {});
if (accessToken) {
headers.set('Authorization', `Bearer ${accessToken}`);
}
const response = await fetch(endpoint, {
...options,
headers,
credentials: 'same-origin'
});
if (response.status === 401 && retryOnUnauthorized && await refreshAccessToken()) {
return fetchWithAuth(endpoint, options, false);
}
return response;
}
export async function logoutSession() {
try {
await fetch('/api/auth/logout', {
method: 'POST',
credentials: 'same-origin'
});
} catch (error) {
console.warn('Не удалось завершить серверную сессию');
} finally {
clearAuthState();
}
}
function purgeLegacyAuthStorage() {
try {
LEGACY_AUTH_KEYS.forEach(key => localStorage.removeItem(key));
LEGACY_AUTH_KEYS.forEach(key => sessionStorage.removeItem(key));
} catch (error) {
console.warn('Не удалось очистить устаревшие данные авторизации в браузере');
}
}
function normalizeOptionalId(value) {
return value === undefined || value === null || value === '' ? null : String(value);
}