Исправить маршрутизацию call push по sessionId
This commit is contained in:
parent
47574100f9
commit
cc074a941f
@ -0,0 +1,16 @@
|
|||||||
|
# Фикс привязки call push к целевой sessionId
|
||||||
|
|
||||||
|
- краткое описание:
|
||||||
|
- push-события `incoming_call` и `stop_call` теперь помечаются целевой `sessionId`;
|
||||||
|
- UI и service worker обрабатывают call push только для своей целевой сессии;
|
||||||
|
- `stop_call` для лишних сессий закрывает локальный экран тихо, без обратных сигналов и без лишних тех-сообщений.
|
||||||
|
- что проверять:
|
||||||
|
- держать несколько сессий одного пользователя в одном браузере/на одном origin;
|
||||||
|
- позвонить этому пользователю и убедиться, что входящий экран закрывается корректно только на целевых сессиях;
|
||||||
|
- после `ACCEPT` одной сессии остальные должны тихо убрать экран вызова и не ломать выбранную пару;
|
||||||
|
- после отмены входящей сессией исходящая сессия должна централизованно завершить сценарий.
|
||||||
|
- ожидаемый результат:
|
||||||
|
- push одного session endpoint больше не влияет на чужие сессии этого же origin;
|
||||||
|
- исчезают ложные `stop_call_push:accepted_on_other_device` и `terminal_call_signal_150` на неправильных сессиях.
|
||||||
|
- статус:
|
||||||
|
- pending
|
||||||
@ -89,6 +89,7 @@ public class Net_CallInviteBroadcast_Handler implements JsonMessageHandler {
|
|||||||
+ ",\"text\":\"Вам звонит " + jsonEscape(from) + "\""
|
+ ",\"text\":\"Вам звонит " + jsonEscape(from) + "\""
|
||||||
+ ",\"fromLogin\":\"" + jsonEscape(from) + "\""
|
+ ",\"fromLogin\":\"" + jsonEscape(from) + "\""
|
||||||
+ ",\"fromSessionId\":\"" + jsonEscape(ctx.getSessionId()) + "\""
|
+ ",\"fromSessionId\":\"" + jsonEscape(ctx.getSessionId()) + "\""
|
||||||
|
+ ",\"targetSessionId\":\"" + jsonEscape(sessionId) + "\""
|
||||||
+ ",\"toLogin\":\"" + jsonEscape(to) + "\""
|
+ ",\"toLogin\":\"" + jsonEscape(to) + "\""
|
||||||
+ ",\"callId\":\"" + jsonEscape(callId) + "\""
|
+ ",\"callId\":\"" + jsonEscape(callId) + "\""
|
||||||
+ ",\"sentAtMs\":" + timeMs
|
+ ",\"sentAtMs\":" + timeMs
|
||||||
|
|||||||
@ -164,6 +164,7 @@ public class Net_CallSignalToSession_Handler implements JsonMessageHandler {
|
|||||||
+ ",\"reason\":\"" + jsonEscape(reason) + "\""
|
+ ",\"reason\":\"" + jsonEscape(reason) + "\""
|
||||||
+ ",\"fromLogin\":\"" + jsonEscape(fromLogin) + "\""
|
+ ",\"fromLogin\":\"" + jsonEscape(fromLogin) + "\""
|
||||||
+ ",\"fromSessionId\":\"" + jsonEscape(fromSessionId) + "\""
|
+ ",\"fromSessionId\":\"" + jsonEscape(fromSessionId) + "\""
|
||||||
|
+ ",\"targetSessionId\":\"" + jsonEscape(sessionId) + "\""
|
||||||
+ ",\"toLogin\":\"" + jsonEscape(targetLogin) + "\""
|
+ ",\"toLogin\":\"" + jsonEscape(targetLogin) + "\""
|
||||||
+ ",\"sentAtMs\":" + sentAtMs
|
+ ",\"sentAtMs\":" + sentAtMs
|
||||||
+ "}";
|
+ "}";
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
client.version=1.2.217
|
client.version=1.2.218
|
||||||
server.version=1.2.205
|
server.version=1.2.206
|
||||||
|
|||||||
@ -100,6 +100,7 @@ self.addEventListener('push', (event) => {
|
|||||||
const json = decodePushJson(rawText);
|
const json = decodePushJson(rawText);
|
||||||
const callId = String(json.callId || '').trim();
|
const callId = String(json.callId || '').trim();
|
||||||
const fromSessionId = String(json.fromSessionId || '').trim();
|
const fromSessionId = String(json.fromSessionId || '').trim();
|
||||||
|
const targetSessionId = String(json.targetSessionId || '').trim();
|
||||||
const toLogin = String(json.toLogin || '').trim();
|
const toLogin = String(json.toLogin || '').trim();
|
||||||
const reason = String(json.reason || '').trim();
|
const reason = String(json.reason || '').trim();
|
||||||
const sentAtMs = Number(json.sentAtMs || 0);
|
const sentAtMs = Number(json.sentAtMs || 0);
|
||||||
@ -139,6 +140,7 @@ self.addEventListener('push', (event) => {
|
|||||||
callId,
|
callId,
|
||||||
fromLogin,
|
fromLogin,
|
||||||
fromSessionId,
|
fromSessionId,
|
||||||
|
targetSessionId,
|
||||||
toLogin,
|
toLogin,
|
||||||
sentAtMs,
|
sentAtMs,
|
||||||
expiresAtMs,
|
expiresAtMs,
|
||||||
@ -165,6 +167,7 @@ self.addEventListener('push', (event) => {
|
|||||||
body,
|
body,
|
||||||
fromLogin,
|
fromLogin,
|
||||||
fromSessionId,
|
fromSessionId,
|
||||||
|
targetSessionId,
|
||||||
toLogin,
|
toLogin,
|
||||||
callId,
|
callId,
|
||||||
sentAtMs,
|
sentAtMs,
|
||||||
@ -186,6 +189,7 @@ self.addEventListener('notificationclick', (event) => {
|
|||||||
callId: String(data.callId || '').trim(),
|
callId: String(data.callId || '').trim(),
|
||||||
fromLogin: String(data.fromLogin || '').trim(),
|
fromLogin: String(data.fromLogin || '').trim(),
|
||||||
fromSessionId: String(data.fromSessionId || '').trim(),
|
fromSessionId: String(data.fromSessionId || '').trim(),
|
||||||
|
targetSessionId: String(data.targetSessionId || '').trim(),
|
||||||
toLogin: String(data.toLogin || '').trim(),
|
toLogin: String(data.toLogin || '').trim(),
|
||||||
sentAtMs: Number(data.sentAtMs || 0),
|
sentAtMs: Number(data.sentAtMs || 0),
|
||||||
expiresAtMs: Number(data.expiresAtMs || 0),
|
expiresAtMs: Number(data.expiresAtMs || 0),
|
||||||
|
|||||||
@ -269,6 +269,13 @@ function savePendingCallPushAction(action, payload = {}) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isCallPushTargetForCurrentSession(payload = {}) {
|
||||||
|
const targetSessionId = String(payload?.targetSessionId || '').trim();
|
||||||
|
if (!targetSessionId) return true;
|
||||||
|
const currentSessionId = String(state?.session?.sessionId || '').trim();
|
||||||
|
return Boolean(currentSessionId) && currentSessionId === targetSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
function loadPendingCallPushAction() {
|
function loadPendingCallPushAction() {
|
||||||
try {
|
try {
|
||||||
const raw = localStorage.getItem(CALL_PUSH_PENDING_ACTION_KEY);
|
const raw = localStorage.getItem(CALL_PUSH_PENDING_ACTION_KEY);
|
||||||
@ -322,6 +329,7 @@ async function processPendingCallPushActionIfPossible() {
|
|||||||
if (!state.session.isAuthorized) return;
|
if (!state.session.isAuthorized) return;
|
||||||
const pending = loadPendingCallPushAction();
|
const pending = loadPendingCallPushAction();
|
||||||
if (!pending) return;
|
if (!pending) return;
|
||||||
|
if (!isCallPushTargetForCurrentSession(pending.payload || {})) return;
|
||||||
clearPendingCallPushAction();
|
clearPendingCallPushAction();
|
||||||
try {
|
try {
|
||||||
await handleCallPushAction(pending.action, pending.payload || {});
|
await handleCallPushAction(pending.action, pending.payload || {});
|
||||||
@ -827,6 +835,7 @@ async function init() {
|
|||||||
const action = String(data.action || '').trim().toLowerCase();
|
const action = String(data.action || '').trim().toLowerCase();
|
||||||
const payload = data.payload || {};
|
const payload = data.payload || {};
|
||||||
if (action === 'accept' || action === 'decline') {
|
if (action === 'accept' || action === 'decline') {
|
||||||
|
if (!isCallPushTargetForCurrentSession(payload)) return;
|
||||||
savePendingCallPushAction(action, payload);
|
savePendingCallPushAction(action, payload);
|
||||||
void processPendingCallPushActionIfPossible();
|
void processPendingCallPushActionIfPossible();
|
||||||
}
|
}
|
||||||
@ -835,6 +844,7 @@ async function init() {
|
|||||||
if (data.type !== 'SHINE_WEB_PUSH_EVENT') return;
|
if (data.type !== 'SHINE_WEB_PUSH_EVENT') return;
|
||||||
|
|
||||||
const payload = data.payload || {};
|
const payload = data.payload || {};
|
||||||
|
if (!isCallPushTargetForCurrentSession(payload)) return;
|
||||||
const kind = String(payload.kind || '').trim();
|
const kind = String(payload.kind || '').trim();
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -831,6 +831,9 @@ async function finalizeCall(call, {
|
|||||||
localReasonCode = 'error',
|
localReasonCode = 'error',
|
||||||
debugReason = '',
|
debugReason = '',
|
||||||
notifyRemoteHangup = false,
|
notifyRemoteHangup = false,
|
||||||
|
suppressRemoteSignal = false,
|
||||||
|
suppressReports = false,
|
||||||
|
suppressSummary = false,
|
||||||
} = {}) {
|
} = {}) {
|
||||||
if (!call) return;
|
if (!call) return;
|
||||||
const diagnosticsBeforeClose = getCallDiagnosticsContext(call);
|
const diagnosticsBeforeClose = getCallDiagnosticsContext(call);
|
||||||
@ -839,7 +842,8 @@ async function finalizeCall(call, {
|
|||||||
stopTone();
|
stopTone();
|
||||||
|
|
||||||
const shouldNotifyRemoteFailure =
|
const shouldNotifyRemoteFailure =
|
||||||
!notifyRemoteHangup
|
!suppressRemoteSignal
|
||||||
|
&& !notifyRemoteHangup
|
||||||
&& Boolean(call.remoteSessionId)
|
&& Boolean(call.remoteSessionId)
|
||||||
&& String(localReasonCode || '') !== 'completed'
|
&& String(localReasonCode || '') !== 'completed'
|
||||||
&& String(debugReason || '') !== 'remote_hangup';
|
&& String(debugReason || '') !== 'remote_hangup';
|
||||||
@ -868,7 +872,7 @@ async function finalizeCall(call, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const reasonText = debugReason || localReasonCode;
|
const reasonText = debugReason || localReasonCode;
|
||||||
if (String(localReasonCode || '') !== 'completed') {
|
if (!suppressReports && String(localReasonCode || '') !== 'completed') {
|
||||||
const failureStage = call.phase || '';
|
const failureStage = call.phase || '';
|
||||||
const failureContext = {
|
const failureContext = {
|
||||||
failureStage,
|
failureStage,
|
||||||
@ -890,7 +894,9 @@ async function finalizeCall(call, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!suppressSummary) {
|
||||||
pushCallSummary(call, localReasonCode);
|
pushCallSummary(call, localReasonCode);
|
||||||
|
}
|
||||||
|
|
||||||
call.phase = 'ended';
|
call.phase = 'ended';
|
||||||
call.statusText = 'Звонок завершён';
|
call.statusText = 'Звонок завершён';
|
||||||
@ -1128,6 +1134,13 @@ function isIncomingCallPushFresh(payload) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isCallPushForCurrentSession(payload = {}) {
|
||||||
|
const targetSessionId = String(payload?.targetSessionId || '').trim();
|
||||||
|
if (!targetSessionId) return true;
|
||||||
|
const currentSessionId = String(state?.session?.sessionId || '').trim();
|
||||||
|
return Boolean(currentSessionId) && currentSessionId === targetSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
async function handleIncomingInvitePayload(payload, { source = 'ws' } = {}) {
|
async function handleIncomingInvitePayload(payload, { source = 'ws' } = {}) {
|
||||||
const callId = String(payload?.callId || '').trim();
|
const callId = String(payload?.callId || '').trim();
|
||||||
const fromLogin = String(payload?.fromLogin || '').trim();
|
const fromLogin = String(payload?.fromLogin || '').trim();
|
||||||
@ -1627,11 +1640,13 @@ export async function hangupActiveCall() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function handleIncomingCallPush(payload = {}) {
|
export async function handleIncomingCallPush(payload = {}) {
|
||||||
|
if (!isCallPushForCurrentSession(payload)) return;
|
||||||
if (!isIncomingCallPushFresh(payload)) return;
|
if (!isIncomingCallPushFresh(payload)) return;
|
||||||
await handleIncomingInvitePayload(payload, { source: 'push' });
|
await handleIncomingInvitePayload(payload, { source: 'push' });
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function handleStopCallPush(payload = {}) {
|
export async function handleStopCallPush(payload = {}) {
|
||||||
|
if (!isCallPushForCurrentSession(payload)) return;
|
||||||
const callId = String(payload?.callId || '').trim();
|
const callId = String(payload?.callId || '').trim();
|
||||||
if (!callId) return;
|
if (!callId) return;
|
||||||
const call = getCall(callId);
|
const call = getCall(callId);
|
||||||
@ -1646,10 +1661,14 @@ export async function handleStopCallPush(payload = {}) {
|
|||||||
await finalizeCall(call, {
|
await finalizeCall(call, {
|
||||||
localReasonCode: call.connectedAtMs ? 'completed' : 'no_answer',
|
localReasonCode: call.connectedAtMs ? 'completed' : 'no_answer',
|
||||||
debugReason: `stop_call_push:${reason}`,
|
debugReason: `stop_call_push:${reason}`,
|
||||||
|
suppressRemoteSignal: true,
|
||||||
|
suppressReports: true,
|
||||||
|
suppressSummary: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function handleCallPushAction(action, payload = {}) {
|
export async function handleCallPushAction(action, payload = {}) {
|
||||||
|
if (!isCallPushForCurrentSession(payload)) return;
|
||||||
const normalized = String(action || '').trim().toLowerCase();
|
const normalized = String(action || '').trim().toLowerCase();
|
||||||
if (normalized !== 'accept' && normalized !== 'decline') return;
|
if (normalized !== 'accept' && normalized !== 'decline') return;
|
||||||
if (!isIncomingCallPushFresh(payload)) return;
|
if (!isIncomingCallPushFresh(payload)) return;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user