SHA256
Звонки: preflight сессии перед вызовом и retry; таймаут вынесен в настройки
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { addSystemChatMessage, authService } from '../state.js';
|
||||
import { addSystemChatMessage, authService, authorizeSession, state } from '../state.js';
|
||||
|
||||
const TYPES = {
|
||||
INVITE: 100,
|
||||
@@ -31,6 +31,47 @@ function nowMs() {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
function resolveCallPreflightTimeoutMs() {
|
||||
const configured = Number(state?.entrySettings?.callPreflightTimeoutMs || 6000);
|
||||
return Math.max(1000, Math.min(20000, Number.isFinite(configured) ? configured : 6000));
|
||||
}
|
||||
|
||||
function isSessionReadyForCall() {
|
||||
const wsOpen = Boolean(authService?.ws?.ws && authService.ws.ws.readyState === WebSocket.OPEN);
|
||||
const hasSession = Boolean(state?.session?.isAuthorized && state?.session?.login && state?.session?.sessionId);
|
||||
return wsOpen && hasSession;
|
||||
}
|
||||
|
||||
async function withTimeout(promise, timeoutMs, timeoutMessage = 'timeout') {
|
||||
let timerId = 0;
|
||||
try {
|
||||
return await Promise.race([
|
||||
promise,
|
||||
new Promise((_, reject) => {
|
||||
timerId = window.setTimeout(() => reject(new Error(timeoutMessage)), timeoutMs);
|
||||
}),
|
||||
]);
|
||||
} finally {
|
||||
if (timerId) window.clearTimeout(timerId);
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureSessionForCall({ timeoutMs, force = false } = {}) {
|
||||
if (!force && isSessionReadyForCall()) return true;
|
||||
const login = String(state?.session?.login || '').trim();
|
||||
const sessionId = String(state?.session?.sessionId || '').trim();
|
||||
if (!login || !sessionId) return false;
|
||||
|
||||
try {
|
||||
await withTimeout(authService.ws.open(), timeoutMs, 'call_preflight_ws_timeout');
|
||||
const resumed = await withTimeout(authService.resumeSession(login, sessionId), timeoutMs, 'call_preflight_resume_timeout');
|
||||
authorizeSession(resumed);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function makeCallId() {
|
||||
return `${Date.now()}${Math.floor(Math.random() * 1_000_000_000)}`;
|
||||
}
|
||||
@@ -1314,6 +1355,12 @@ export async function startOutgoingCall(peerLogin) {
|
||||
}
|
||||
|
||||
const callId = makeCallId();
|
||||
const preflightTimeoutMs = resolveCallPreflightTimeoutMs();
|
||||
const preflightOk = await ensureSessionForCall({ timeoutMs: preflightTimeoutMs, force: false });
|
||||
if (!preflightOk) {
|
||||
throw new Error('Сервер временно недоступен');
|
||||
}
|
||||
|
||||
const call = {
|
||||
callId,
|
||||
peerLogin: cleanPeer,
|
||||
@@ -1359,6 +1406,19 @@ export async function startOutgoingCall(peerLogin) {
|
||||
try {
|
||||
await authService.callInviteBroadcast({ toLogin: cleanPeer, callId, type: TYPES.INVITE });
|
||||
} catch (error) {
|
||||
const text = String(error?.message || '').toUpperCase();
|
||||
const isNotAuth = text.includes('NOT_AUTHENTICATED');
|
||||
if (isNotAuth) {
|
||||
const recovered = await ensureSessionForCall({ timeoutMs: preflightTimeoutMs, force: true });
|
||||
if (recovered) {
|
||||
try {
|
||||
await authService.callInviteBroadcast({ toLogin: cleanPeer, callId, type: TYPES.INVITE });
|
||||
return;
|
||||
} catch {}
|
||||
}
|
||||
await finalizeCall(call, { localReasonCode: 'error', debugReason: 'invite_failed:not_authenticated_after_retry' });
|
||||
throw new Error('Сервер временно недоступен');
|
||||
}
|
||||
await finalizeCall(call, { localReasonCode: 'error', debugReason: `invite_failed:${toErrorText(error)}` });
|
||||
throw error;
|
||||
}
|
||||
@@ -1587,6 +1647,11 @@ export async function handleCallPushAction(action, payload = {}) {
|
||||
const normalized = String(action || '').trim().toLowerCase();
|
||||
if (normalized !== 'accept' && normalized !== 'decline') return;
|
||||
if (!isIncomingCallPushFresh(payload)) return;
|
||||
const timeoutMs = resolveCallPreflightTimeoutMs();
|
||||
const ok = await ensureSessionForCall({ timeoutMs, force: false });
|
||||
if (!ok) {
|
||||
throw new Error('Не удалось подключиться, вызов завершён');
|
||||
}
|
||||
await handleIncomingCallPush(payload);
|
||||
if (normalized === 'accept') {
|
||||
await acceptIncomingCall();
|
||||
|
||||
Reference in New Issue
Block a user