fix and update

This commit is contained in:
lendry
2026-07-01 11:32:53 +03:00
parent 2c4b1fcc44
commit f8f25c8289
3 changed files with 37 additions and 16 deletions

View File

@@ -479,7 +479,7 @@ function notifyPinRequired(sessionId: string) {
}
export async function fetchAuthSession(token?: string | null): Promise<AuthSessionResponse> {
return apiFetch<AuthSessionResponse>('/auth/session', {}, token);
return apiFetch<AuthSessionResponse>('/auth/session', {}, token, true, { critical: true });
}
export async function approveOAuthAuthorization(params: URLSearchParams, token: string) {
@@ -688,7 +688,7 @@ export function buildSystemSettingPayload(setting: Pick<SystemSetting, 'key' | '
}
const GATEWAY_RETRY_STATUS = new Set([502, 503, 504]);
const GATEWAY_RETRY_ATTEMPTS = 2;
const GATEWAY_RETRY_ATTEMPTS = 3;
const GATEWAY_CRITICAL_RETRY_ATTEMPTS = 4;
export interface ApiFetchBehavior {
@@ -700,8 +700,8 @@ export interface ApiFetchBehavior {
const GATEWAY_STABLE_PROBE_INTERVAL_MS = 800;
const GATEWAY_STABLE_MAX_ROUNDS = 8;
const API_MAX_CONCURRENT = 6;
const API_BURST_MAX_CONCURRENT = 2;
const API_BURST_WINDOW_MS = 5000;
const API_BURST_MAX_CONCURRENT = 4;
const API_BURST_WINDOW_MS = 8000;
const API_READY_EVENT = 'idp-api-ready';
const API_NOT_READY_EVENT = 'idp-api-not-ready';
export const AUTH_SESSION_INVALID_EVENT = 'idp-auth-session-invalid';
@@ -746,6 +746,8 @@ function shouldBypassGatewayCircuit(behavior?: ApiFetchBehavior): boolean {
function openGatewayCircuit(behavior?: ApiFetchBehavior): void {
if (shouldBypassGatewayCircuit(behavior)) return;
// До стабильного прогрева /health circuit не открываем — иначе каждая перезагрузка ловит ложный 502.
if (!gatewayReadyResolved) return;
gatewayReadyResolved = false;
gatewayReadyAt = 0;
stableGatewayPromise = null;
@@ -757,6 +759,7 @@ function openGatewayCircuit(behavior?: ApiFetchBehavior): void {
function assertGatewayCircuitClosed(behavior?: ApiFetchBehavior): void {
if (typeof window === 'undefined' || shouldBypassGatewayCircuit(behavior)) return;
if (!gatewayReadyAt && !gatewayCircuitOpenUntil) return;
if (Date.now() < gatewayCircuitOpenUntil) {
throw new ApiError('Сервер API временно недоступен. Запрос отложен, чтобы не перегружать консоль ошибками.', 503, 'GATEWAY_UNAVAILABLE');
}
@@ -876,7 +879,7 @@ export async function ensureApiGatewayReady(force = false): Promise<void> {
gatewayCircuitOpenUntil = 0;
}
const probes = force ? 2 : 1;
const probes = force ? 2 : 2;
gatewayReadyPromise = waitForStableGateway(probes).finally(() => {
gatewayReadyPromise = null;
});
@@ -900,9 +903,6 @@ async function fetchWithGatewayRetry(
try {
assertGatewayCircuitClosed(behavior);
const response = await fetch(url, init);
if (response.ok) {
markApiGatewayReady();
}
if (GATEWAY_RETRY_STATUS.has(response.status) && attempt + 1 < maxAttempts) {
await sleep(gatewayRetryDelay(attempt));
continue;
@@ -935,6 +935,14 @@ export async function apiFetch<T>(
allowRetry = true,
behavior: ApiFetchBehavior = {}
): Promise<T> {
if (!gatewayReadyResolved && !behavior.silent) {
try {
await ensureApiGatewayReady();
} catch {
// fetchWithGatewayRetry попробует сам после кратковременного 502
}
}
await acquireApiSlot();
const resolvedToken = resolveAccessToken(token);
@@ -1000,7 +1008,6 @@ export async function apiFetch<T>(
throw error;
}
markApiGatewayReady();
return response.json() as Promise<T>;
} finally {
releaseCurrentApiSlot();
@@ -1263,11 +1270,11 @@ export async function fetchFamilyGroups(userId: string, token?: string | null) {
if (!accessToken) {
throw new ApiError('Не авторизован', 401, 'NO_TOKEN');
}
return apiFetch<{ groups?: FamilyGroup[] }>(`/family/users/${userId}/groups`, {}, accessToken);
return apiFetch<{ groups?: FamilyGroup[] }>(`/family/users/${userId}/groups`, {}, accessToken, true, { silent: true });
}
export async function fetchFamilyGroup(groupId: string, token?: string | null) {
return apiFetch<FamilyGroup>(`/family/groups/${groupId}`, {}, token);
return apiFetch<FamilyGroup>(`/family/groups/${groupId}`, {}, token, true, { silent: true });
}
export async function updateFamilyGroup(groupId: string, name: string, token?: string | null) {
@@ -1325,7 +1332,7 @@ export async function fetchNotifications(token?: string | null, unreadOnly = fal
}
export async function fetchUnreadNotificationCount(token?: string | null) {
return apiFetch<{ count: number }>('/notifications/unread-count', {}, token);
return apiFetch<{ count: number }>('/notifications/unread-count', {}, token, true, { silent: true });
}
export async function markNotificationRead(notificationId: string, token?: string | null) {
@@ -1584,7 +1591,7 @@ export async function fetchFamilyPresence(groupId: string, token?: string | null
if (!accessToken) {
throw new ApiError('Не авторизован', 401, 'NO_TOKEN');
}
return apiFetch<FamilyPresence>(`/family/groups/${groupId}/presence`, {}, accessToken);
return apiFetch<FamilyPresence>(`/family/groups/${groupId}/presence`, {}, accessToken, true, { silent: true });
}
/** @deprecated Используйте getWsUrl() — URL может переключаться на same-origin /idp-api/ws в браузере */