Files
2026-07-02 09:12:57 +07:00

171 lines
4.3 KiB
JavaScript

/**
* WebSocket client for backone-webhub (127.0.0.1, authtoken auth).
*/
window.BackOneWebHubClient = (function () {
let ws = null;
let connected = false;
let reconnectDelay = 1000;
let reconnectTimer = null;
let pollTimer = null;
let port = 9994;
let selfPeerId = '';
const seenIncoming = new Set();
const handlers = new Map();
function emit(type, msg) {
const set = handlers.get(type);
if (!set) return;
set.forEach(fn => {
try { fn(msg); } catch (_) { /* ignore */ }
});
const all = handlers.get('*');
if (all) {
all.forEach(fn => {
try { fn(type, msg); } catch (_) { /* ignore */ }
});
}
}
function subscribe(type, fn) {
if (!handlers.has(type)) handlers.set(type, new Set());
handlers.get(type).add(fn);
return () => handlers.get(type)?.delete(fn);
}
function send(msg) {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(msg));
return true;
}
return false;
}
function registerPeer() {
const core = BackOneNetworkSession.getCoreStatus();
selfPeerId = ((core && core.address) || '').toLowerCase();
const networkId = BackOneNetworkSession.getNetworkId();
if (!selfPeerId) return;
send({ type: 'register', peerId: selfPeerId, networkId });
}
function stopHttpPoll() {
if (pollTimer) {
clearInterval(pollTimer);
pollTimer = null;
}
}
function startHttpPoll() {
stopHttpPoll();
pollTimer = setInterval(async () => {
if (connected) return;
const peerId = selfPeerId || ((BackOneNetworkSession.getCoreStatus().address || '').toLowerCase());
if (!peerId) return;
try {
const res = await BackOneAuth.fetchWithAuth(
`/controller/comms/call/incoming?peerId=${encodeURIComponent(peerId)}`
);
if (!res.ok) return;
const data = await res.json();
const calls = Array.isArray(data.calls) ? data.calls : [];
for (const call of calls) {
if (seenIncoming.has(call.callId)) continue;
seenIncoming.add(call.callId);
emit('incoming_call', {
type: 'incoming_call',
callId: call.callId,
callType: call.callType || 'voice',
callerId: call.callerId,
callerName: call.callerName || call.callerId,
networkId: call.networkId,
sdp: call.sdp
});
}
} catch (_) { /* ignore */ }
}, 2000);
}
function scheduleReconnect() {
if (reconnectTimer) return;
reconnectTimer = setTimeout(() => {
reconnectTimer = null;
connect();
}, reconnectDelay);
reconnectDelay = Math.min(reconnectDelay * 2, 30000);
}
async function loadPort() {
try {
const res = await BackOneAuth.fetchWithAuth('/app/api/webrtc-config');
if (res.ok) {
const cfg = await res.json();
if (cfg.webhubPort) port = cfg.webhubPort;
}
} catch (_) { /* ignore */ }
}
async function connect() {
await loadPort();
const token = await BackOneAuth.getToken();
if (!token) return false;
if (ws) {
try { ws.close(); } catch (_) { /* ignore */ }
ws = null;
}
return new Promise((resolve) => {
const url = `ws://127.0.0.1:${port}/?auth=${encodeURIComponent(token)}`;
try {
ws = new WebSocket(url);
} catch (_) {
connected = false;
startHttpPoll();
scheduleReconnect();
resolve(false);
return;
}
ws.onopen = () => {
connected = true;
reconnectDelay = 1000;
stopHttpPoll();
send({ type: 'auth', token });
registerPeer();
resolve(true);
};
ws.onmessage = (ev) => {
try {
const msg = JSON.parse(ev.data);
if (msg && msg.type) emit(msg.type, msg);
} catch (_) { /* ignore */ }
};
ws.onclose = () => {
connected = false;
ws = null;
startHttpPoll();
scheduleReconnect();
};
ws.onerror = () => {
connected = false;
};
});
}
BackOneNetworkSession.subscribe(() => {
if (connected) registerPeer();
});
return {
connect,
send,
subscribe,
isConnected: () => connected,
getSelfPeerId: () => selfPeerId,
registerPeer
};
})();