From 813d3aa06efc313aeee30c279b6f7ac0da434a55 Mon Sep 17 00:00:00 2001 From: ariskadatabisnis Date: Thu, 2 Jul 2026 09:12:57 +0700 Subject: [PATCH] secure chat addon and stun --- OpenType-TT/BackOneLogo-Regular-SVG.ttf | Bin 0 -> 26352 bytes OpenType-TT/BackOneLogo-Regular.ttf | Bin 0 -> 23304 bytes addons/chat/addon.json | 2 +- addons/chat/frontend/index.js | 145 ++++--- addons/secure-chat/addon.json | 27 ++ addons/secure-chat/frontend/index.js | 186 ++++++++ addons/updater/backone-updater | 3 + .../migrations/004_webrtc_ice_defaults.sh | 14 + .../migrations/005_enable_webhub_defaults.sh | 44 ++ addons/video/addon.json | 2 +- addons/video/frontend/index.js | 122 +++--- addons/voice/addon.json | 2 +- addons/voice/frontend/index.js | 103 +++-- addons/webhub/addon.json | 15 + addons/webhub/backone-webhub | 343 +++++++++++++++ addons/webhub/backone-webhub.service | 15 + app-shell/dist/index.html | 2 + app-shell/dist/main.js | 32 +- app-shell/dist/styles.css | 331 +++++++++++++- app-shell/dist/webhub-client.js | 170 ++++++++ app-shell/dist/webrtc-session.js | 352 +++++++++++++++ ci/scripts/bump_version.sh | 2 +- controller/CommsController.cpp | 405 ++++++++++++++++++ controller/CommsController.hpp | 35 ++ debian/backone-addon-common.postinst | 2 +- .../backone-addon-secure-chat/DEBIAN/control | 12 + .../backone-addon-secure-chat/DEBIAN/postinst | 12 + debian/backone-addon-webhub.postinst | 16 + debian/control | 20 +- debian/rules | 3 + make/install-addons.mk | 47 +- objects.mk | 1 + pkg/common/platform-paths.sh | 34 ++ pkg/common/resolve-install-path.sh | 18 + pkg/mac/build-addon-pkgs.sh | 2 + pkg/windows/build-addon-packages.ps1 | 2 + production_webrtc_ab20e1d2.plan.md | 214 +++++++++ secure_chat_addon_plan_a08dbee2.plan.md | 212 +++++++++ service/AddonHost.cpp | 1 + service/OneService.cpp | 8 + 40 files changed, 2767 insertions(+), 189 deletions(-) create mode 100755 OpenType-TT/BackOneLogo-Regular-SVG.ttf create mode 100755 OpenType-TT/BackOneLogo-Regular.ttf create mode 100644 addons/secure-chat/addon.json create mode 100644 addons/secure-chat/frontend/index.js create mode 100755 addons/updater/migrations/004_webrtc_ice_defaults.sh create mode 100755 addons/updater/migrations/005_enable_webhub_defaults.sh create mode 100644 addons/webhub/addon.json create mode 100755 addons/webhub/backone-webhub create mode 100644 addons/webhub/backone-webhub.service create mode 100644 app-shell/dist/webhub-client.js create mode 100644 app-shell/dist/webrtc-session.js create mode 100644 controller/CommsController.cpp create mode 100644 controller/CommsController.hpp create mode 100644 debian/backone-addon-secure-chat/DEBIAN/control create mode 100755 debian/backone-addon-secure-chat/DEBIAN/postinst create mode 100644 debian/backone-addon-webhub.postinst create mode 100755 pkg/common/resolve-install-path.sh create mode 100644 production_webrtc_ab20e1d2.plan.md create mode 100644 secure_chat_addon_plan_a08dbee2.plan.md diff --git a/OpenType-TT/BackOneLogo-Regular-SVG.ttf b/OpenType-TT/BackOneLogo-Regular-SVG.ttf new file mode 100755 index 0000000000000000000000000000000000000000..68a070e0a33d0de04c7c6c23381fdfc43b324aea GIT binary patch literal 26352 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 literal 0 HcmV?d00001 diff --git a/OpenType-TT/BackOneLogo-Regular.ttf b/OpenType-TT/BackOneLogo-Regular.ttf new file mode 100755 index 0000000000000000000000000000000000000000..e2c1b6631fcaa847cffa8431584024edd852d17e GIT binary patch literal 23304 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 OMvh;f%_EkH%l`p&g|=V- literal 0 HcmV?d00001 diff --git a/addons/chat/addon.json b/addons/chat/addon.json index f2cc745..6b0ccb6 100644 --- a/addons/chat/addon.json +++ b/addons/chat/addon.json @@ -2,7 +2,7 @@ "id": "chat", "name": "Chat P2P", "version": "1.17.1", - "requires": ["comms-core"], + "requires": ["comms-core", "webhub"], "backoneCore": { "apis": [ "/app/api/peers", diff --git a/addons/chat/frontend/index.js b/addons/chat/frontend/index.js index 14f0d81..9688588 100644 --- a/addons/chat/frontend/index.js +++ b/addons/chat/frontend/index.js @@ -4,12 +4,27 @@ window.BackOneAddons.chat = function chatAddon() { const messagesByPeer = {}; let selectedPeer = null; let root = null; + let unsubChat = null; + + function escapeHtml(s) { + return String(s).replace(/&/g, '&').replace(//g, '>'); + } + + function clearSessionMessages() { + Object.keys(messagesByPeer).forEach(k => delete messagesByPeer[k]); + } function getThread(peerId) { if (!messagesByPeer[peerId]) messagesByPeer[peerId] = []; return messagesByPeer[peerId]; } + function formatTime(ts) { + if (!ts) return ''; + const d = new Date(ts); + return d.toLocaleString(undefined, { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }); + } + function networkLabel() { const n = BackOneNetworkSession.getActiveNetwork(); return n ? (n.name || n.id) : BackOneNetworkSession.getNetworkId(); @@ -32,35 +47,37 @@ window.BackOneAddons.chat = function chatAddon() { header.textContent = net ? `Select a contact on ${net}` : 'Join a network in the header bar'; return; } - header.innerHTML = `${selectedPeer.displayName || selectedPeer.peerId} ${selectedPeer.peerId}${net ? ' · ' + net : ''}`; + header.innerHTML = `${escapeHtml(selectedPeer.displayName || selectedPeer.peerId)} ${escapeHtml(selectedPeer.peerId)}${net ? ' · ' + escapeHtml(net) : ''}`; updateConnectionBar(); } + function selfName() { + const addr = (BackOneNetworkSession.getCoreStatus().address || '').toLowerCase(); + return addr ? addr.slice(0, 10) + '…' : 'me'; + } + return { mount(container) { root = container; + clearSessionMessages(); container.innerHTML = ` -
+
-
-
Chat P2P > Select contact
-
Select a contact
-
+
-
@@ -71,31 +88,63 @@ window.BackOneAddons.chat = function chatAddon() { const thread = container.querySelector('#chat-thread'); const input = container.querySelector('#chat-input'); - const fileInput = container.querySelector('#chat-file'); const sendBtn = container.querySelector('#chat-send'); - function updateInputs() { - const ok = !!BackOneNetworkSession.getNetworkId() && !!selectedPeer; - input.disabled = !ok; - fileInput.disabled = !ok; - sendBtn.disabled = !ok; - renderHeader(); - const bc = root.querySelector('#chat-breadcrumb-peer'); - if (bc) bc.textContent = selectedPeer ? (selectedPeer.displayName || selectedPeer.peerId) : 'Select contact'; - } - const render = () => { if (!selectedPeer) { - thread.innerHTML = '

Choose a peer from the list

'; + thread.innerHTML = '

Choose a contact from the list

'; return; } const messages = getThread(selectedPeer.peerId); - thread.innerHTML = messages.map(m => - `
${m.senderName || m.sender || 'me'}: ${m.body || ''}${m.file ? ` [${m.file}]` : ''}
` - ).join(''); + if (!messages.length) { + thread.innerHTML = '

No messages yet — start the conversation

'; + return; + } + thread.innerHTML = messages.map(m => ` +
+
+ ${escapeHtml(m.senderName || m.sender || 'peer')} + ${escapeHtml(formatTime(m.at))} +
+ ${m.body ? `
${escapeHtml(m.body)}
` : ''} +
`).join(''); thread.scrollTop = thread.scrollHeight; }; + function updateInputs() { + const networkId = BackOneNetworkSession.getNetworkId(); + const ok = !!networkId && !!selectedPeer; + input.disabled = !ok; + sendBtn.disabled = !ok; + renderHeader(); + } + + if (unsubChat) unsubChat(); + unsubChat = BackOneWebHubClient.subscribe('chat_message', (msg) => { + const networkId = BackOneNetworkSession.getNetworkId(); + if (!networkId || (msg.networkId && msg.networkId.toLowerCase() !== networkId)) return; + const senderId = (msg.senderId || '').toLowerCase(); + const peerId = selectedPeer?.peerId?.toLowerCase(); + if (!peerId || senderId !== peerId) { + if (senderId) { + getThread(senderId).push({ + sender: senderId, + senderName: msg.senderName || senderId, + body: msg.body || '', + at: msg.at || Date.now() + }); + } + return; + } + getThread(peerId).push({ + sender: senderId, + senderName: msg.senderName || senderId, + body: msg.body || '', + at: msg.at || Date.now() + }); + render(); + }); + BackOnePeerPicker(container.querySelector('#chat-peer-picker'), { layout: 'sidebar', showActionButton: false, @@ -106,6 +155,7 @@ window.BackOneAddons.chat = function chatAddon() { }, onNetworkChange() { selectedPeer = null; + clearSessionMessages(); updateInputs(); render(); } @@ -116,40 +166,27 @@ window.BackOneAddons.chat = function chatAddon() { render(); }); - sendBtn.onclick = async () => { + sendBtn.onclick = () => { const networkId = BackOneNetworkSession.getNetworkId(); if (!selectedPeer || !networkId) return; const body = input.value.trim(); - const selfName = 'me'; - if (fileInput.files.length) { - const fd = new FormData(); - fd.append('file', fileInput.files[0]); - fd.append('networkId', networkId); - await BackOneAuth.fetchWithAuth(`/controller/comms/network/${networkId}/chat/attachment`, { - method: 'POST', - body: fd - }).catch(() => {}); - getThread(selectedPeer.peerId).push({ - sender: selfName, - senderName: selfName, - body: '', - file: fileInput.files[0].name - }); - fileInput.value = ''; - } - if (body) { - await BackOneAuth.fetchWithAuth(`/controller/comms/network/${networkId}/chat`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ body, targetId: selectedPeer.peerId, networkId }) - }).catch(() => {}); - getThread(selectedPeer.peerId).push({ - sender: selfName, - senderName: selfName, - body - }); - input.value = ''; - } + if (!body) return; + const now = Date.now(); + const name = selfName(); + BackOneWebHubClient.send({ + type: 'chat_message', + networkId, + targetId: selectedPeer.peerId, + senderName: name, + body + }); + getThread(selectedPeer.peerId).push({ + sender: 'me', + senderName: name, + body, + at: now + }); + input.value = ''; render(); }; diff --git a/addons/secure-chat/addon.json b/addons/secure-chat/addon.json new file mode 100644 index 0000000..42dba0d --- /dev/null +++ b/addons/secure-chat/addon.json @@ -0,0 +1,27 @@ +{ + "id": "secure-chat", + "name": "Secure Chat", + "version": "1.17.1", + "requires": ["comms-core", "webhub"], + "backoneCore": { + "apis": [ + "/app/api/peers", + "/app/api/networks", + "POST /app/api/network/{nwid}", + "DELETE /app/api/network/{nwid}", + "/app/api/network/{nwid}", + "/app/api/network/{nwid}/members", + "/app/api/authtoken", + "/app/api/contacts" + ] + }, + "backend": { + "routes": ["/controller/comms/network"], + "websocketEvents": ["secure_chat_message"] + }, + "frontend": { + "entry": "/app/addons/secure-chat/index.js", + "navLabel": "Secure Chat", + "icon": "chat" + } +} diff --git a/addons/secure-chat/frontend/index.js b/addons/secure-chat/frontend/index.js new file mode 100644 index 0000000..c4110f6 --- /dev/null +++ b/addons/secure-chat/frontend/index.js @@ -0,0 +1,186 @@ +window.BackOneAddons = window.BackOneAddons || {}; + +window.BackOneAddons['secure-chat'] = function secureChatAddon() { + const messagesByPeer = {}; + let selectedPeer = null; + let root = null; + let unsubChat = null; + + function escapeHtml(s) { + return String(s).replace(/&/g, '&').replace(//g, '>'); + } + + function clearSessionMessages() { + Object.keys(messagesByPeer).forEach(k => delete messagesByPeer[k]); + } + + function getThread(peerId) { + if (!messagesByPeer[peerId]) messagesByPeer[peerId] = []; + return messagesByPeer[peerId]; + } + + function formatTime(ts) { + if (!ts) return ''; + const d = new Date(ts); + return d.toLocaleString(undefined, { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }); + } + + function networkLabel() { + const n = BackOneNetworkSession.getActiveNetwork(); + return n ? (n.name || n.id) : BackOneNetworkSession.getNetworkId(); + } + + function renderHeader() { + const header = root?.querySelector('#secure-chat-header'); + if (!header) return; + const net = networkLabel(); + if (!selectedPeer) { + header.textContent = net ? `Select a contact on ${net}` : 'Join a network in the header bar'; + return; + } + header.innerHTML = `${escapeHtml(selectedPeer.displayName || selectedPeer.peerId)} ${escapeHtml(selectedPeer.peerId)}${net ? ' · ' + escapeHtml(net) : ''}`; + } + + function selfName() { + const addr = (BackOneNetworkSession.getCoreStatus().address || '').toLowerCase(); + return addr ? addr.slice(0, 10) + '…' : 'me'; + } + + return { + mount(container) { + root = container; + clearSessionMessages(); + container.innerHTML = ` +
+ +
+
+
Select a contact
+
+
+ +
+ +
+
+
+
+
`; + + const thread = container.querySelector('#secure-chat-thread'); + const input = container.querySelector('#secure-chat-input'); + const sendBtn = container.querySelector('#secure-chat-send'); + + const render = () => { + if (!selectedPeer) { + thread.innerHTML = '

Choose a contact from the list

'; + return; + } + const messages = getThread(selectedPeer.peerId); + if (!messages.length) { + thread.innerHTML = '

No messages yet — start the conversation

'; + return; + } + thread.innerHTML = messages.map(m => ` +
+
+ ${escapeHtml(m.senderName || m.sender || 'me')} + ${escapeHtml(formatTime(m.at))} +
+ ${m.body ? `
${escapeHtml(m.body)}
` : ''} +
`).join(''); + thread.scrollTop = thread.scrollHeight; + }; + + function updateInputs() { + const networkId = BackOneNetworkSession.getNetworkId(); + const ok = !!networkId && !!selectedPeer; + input.disabled = !ok; + sendBtn.disabled = !ok; + renderHeader(); + } + + if (unsubChat) unsubChat(); + unsubChat = BackOneWebHubClient.subscribe('chat_message', (msg) => { + const networkId = BackOneNetworkSession.getNetworkId(); + if (!networkId || (msg.networkId && msg.networkId.toLowerCase() !== networkId)) return; + const senderId = (msg.senderId || '').toLowerCase(); + const peerId = selectedPeer?.peerId?.toLowerCase(); + if (!peerId || senderId !== peerId) { + if (senderId) { + getThread(senderId).push({ + sender: senderId, + senderName: msg.senderName || senderId, + body: msg.body || '', + at: msg.at || Date.now() + }); + } + return; + } + getThread(peerId).push({ + sender: senderId, + senderName: msg.senderName || senderId, + body: msg.body || '', + at: msg.at || Date.now() + }); + render(); + }); + + BackOnePeerPicker(container.querySelector('#secure-chat-peer-picker'), { + layout: 'sidebar', + showActionButton: false, + onSelect(peer) { + selectedPeer = peer; + updateInputs(); + render(); + }, + onNetworkChange() { + selectedPeer = null; + clearSessionMessages(); + updateInputs(); + render(); + } + }); + + BackOneNetworkSession.subscribe(() => { + updateInputs(); + render(); + }); + + sendBtn.onclick = () => { + const networkId = BackOneNetworkSession.getNetworkId(); + if (!selectedPeer || !networkId) return; + const body = input.value.trim(); + if (!body) return; + const now = Date.now(); + const name = selfName(); + BackOneWebHubClient.send({ + type: 'chat_message', + networkId, + targetId: selectedPeer.peerId, + senderName: name, + body + }); + getThread(selectedPeer.peerId).push({ + sender: 'me', + senderName: name, + body, + at: now + }); + input.value = ''; + render(); + }; + + updateInputs(); + render(); + } + }; +}; diff --git a/addons/updater/backone-updater b/addons/updater/backone-updater index 2ea7006..2b17b41 100755 --- a/addons/updater/backone-updater +++ b/addons/updater/backone-updater @@ -55,6 +55,9 @@ run_migrations() { 003_*) [ "$scope" = "all" ] || [ "$scope" = "core" ] || continue ;; + 004_*|005_*) + [ "$scope" = "all" ] || [ "$scope" = "core" ] || continue + ;; *) if [ "$scope" != "all" ] && [ "$scope" != "$ADDON_SCOPE" ]; then continue diff --git a/addons/updater/migrations/004_webrtc_ice_defaults.sh b/addons/updater/migrations/004_webrtc_ice_defaults.sh new file mode 100755 index 0000000..e1694a5 --- /dev/null +++ b/addons/updater/migrations/004_webrtc_ice_defaults.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -euo pipefail +BACKONE_HOME="${BACKONE_HOME:-/var/lib/backone}" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +if [ -x "${SCRIPT_DIR}/merge-local-conf.sh" ]; then + BACKONE_HOME="$BACKONE_HOME" bash "${SCRIPT_DIR}/merge-local-conf.sh" '{ + "settings": { + "icePreferOverlay": true, + "iceServers": [{"urls": "stun:stun.l.google.com:19302"}], + "iceFallbackTimeoutMs": 8000, + "webhubPort": 9994 + } + }' +fi diff --git a/addons/updater/migrations/005_enable_webhub_defaults.sh b/addons/updater/migrations/005_enable_webhub_defaults.sh new file mode 100755 index 0000000..3053353 --- /dev/null +++ b/addons/updater/migrations/005_enable_webhub_defaults.sh @@ -0,0 +1,44 @@ +#!/bin/bash +set -euo pipefail +BACKONE_HOME="${BACKONE_HOME:-/var/lib/backone}" +CONF="${BACKONE_HOME}/local.conf" + +python3 - "$CONF" <<'PY' +import json +import os +import sys + +path = sys.argv[1] +defaults = ["comms-core", "webhub", "voice", "video", "chat"] +current = {} +if os.path.isfile(path): + with open(path, encoding="utf-8") as f: + try: + current = json.load(f) + except json.JSONDecodeError: + current = {} +if not isinstance(current, dict): + current = {} +settings = current.setdefault("settings", {}) +if not isinstance(settings, dict): + settings = {} + current["settings"] = settings +enabled = settings.get("enabledAddons") +if enabled is None: + # No explicit list — all installed addons stay enabled. + sys.exit(0) +if not isinstance(enabled, list): + enabled = [] +merged = list(enabled) +for addon in defaults: + if addon not in merged: + merged.append(addon) +settings["enabledAddons"] = merged +os.makedirs(os.path.dirname(path), exist_ok=True) +with open(path, "w", encoding="utf-8") as f: + json.dump(current, f, indent=2) + f.write("\n") +PY + +chown backone:backone "$CONF" 2>/dev/null || true +chmod 644 "$CONF" 2>/dev/null || true diff --git a/addons/video/addon.json b/addons/video/addon.json index 77d85c4..18b8d9e 100644 --- a/addons/video/addon.json +++ b/addons/video/addon.json @@ -2,7 +2,7 @@ "id": "video", "name": "Video Call P2P", "version": "1.17.1", - "requires": ["comms-core", "voice"], + "requires": ["comms-core", "webhub", "voice"], "backoneCore": { "apis": [ "/app/api/peers", diff --git a/addons/video/frontend/index.js b/addons/video/frontend/index.js index 40dda4e..8e7b7dc 100644 --- a/addons/video/frontend/index.js +++ b/addons/video/frontend/index.js @@ -1,20 +1,13 @@ window.BackOneAddons = window.BackOneAddons || {}; window.BackOneAddons.video = function videoAddon() { - let pc = null; let picker = null; let selectedPeer = null; let inCall = false; - let localStream = null; let micEnabled = true; let camEnabled = true; let root = null; - function networkLabel() { - const n = BackOneNetworkSession.getActiveNetwork(); - return n ? (n.name || n.id) : BackOneNetworkSession.getNetworkId(); - } - function updateConnectionBar() { const bar = root?.querySelector('#video-conn-bar'); if (!bar || !window.BackOneCommLayout) return; @@ -53,19 +46,40 @@ window.BackOneAddons.video = function videoAddon() { updateConnectionBar(); } - function showPlaceholder(show) { - const ph = root?.querySelector('#video-placeholder'); - const remote = root?.querySelector('#video-remote'); - if (ph) ph.style.display = show ? 'block' : 'none'; - if (remote) remote.style.display = show ? 'none' : 'block'; + function updateCallLayout() { + const layout = root?.querySelector('.comm-layout'); + if (layout) layout.classList.toggle('video-in-call', inCall); } - function hangUp() { - if (pc) { pc.close(); pc = null; } - if (localStream) { - localStream.getTracks().forEach(t => t.stop()); - localStream = null; + function showPlaceholder(show) { + const stage = root?.querySelector('.video-stage'); + const remote = root?.querySelector('#video-remote'); + const phLabel = root?.querySelector('#video-ph-label'); + if (stage) stage.classList.toggle('has-remote', !show); + if (remote) remote.classList.toggle('active', !show); + if (phLabel && show) { + phLabel.textContent = inCall + ? 'Waiting for remote peer…' + : (selectedPeer ? 'Ready to call' : 'Select a peer to start a video call'); } + updateCallLayout(); + } + + function bindStreams() { + const local = root?.querySelector('#video-local'); + const remote = root?.querySelector('#video-remote'); + const localStream = BackOneWebRtcSession.getLocalStream(); + const remoteStream = BackOneWebRtcSession.getRemoteStream(); + if (local && localStream) local.srcObject = localStream; + if (remote && remoteStream && remoteStream.getTracks().length) { + remote.srcObject = remoteStream; + showPlaceholder(false); + root?.querySelector('#video-breadcrumb-state')?.textContent = 'Connected to'; + } + } + + async function hangUp() { + await BackOneWebRtcSession.hangUp(); const local = root?.querySelector('#video-local'); const remote = root?.querySelector('#video-remote'); if (local) local.srcObject = null; @@ -73,6 +87,8 @@ window.BackOneAddons.video = function videoAddon() { inCall = false; micEnabled = true; camEnabled = true; + const breadcrumbState = root?.querySelector('#video-breadcrumb-state'); + if (breadcrumbState) breadcrumbState.textContent = 'Select peer'; showPlaceholder(true); updateCallButtons(); window.BackOneRingingPopup?.dismiss(); @@ -99,20 +115,23 @@ window.BackOneAddons.video = function videoAddon() {
-
Video Call P2P > ${inCall ? 'Connected to' : 'Select peer'} —
+
Video Call P2P > Select peer —
-
-
Remote
-
-
👤
-
—
-
Remote Peer
+
+
+
+
👤
+
—
+
Select a peer to start a video call
+
+
-
+ You
+
Remote
@@ -133,6 +152,7 @@ window.BackOneAddons.video = function videoAddon() { selectedPeer = peer; const phPeer = root.querySelector('#video-ph-peer'); if (phPeer) phPeer.textContent = peer.peerId; + if (!inCall) showPlaceholder(true); updateCallButtons(); }, onNetworkChange() { @@ -142,45 +162,41 @@ window.BackOneAddons.video = function videoAddon() { }); BackOneNetworkSession.subscribe(() => updateCallButtons()); + BackOneWebRtcSession.subscribe((event) => { + if (event === 'call-started') { + inCall = true; + updateCallButtons(); + bindStreams(); + } + if (event === 'remote-track' || event === 'call-accepted') bindStreams(); + if (event === 'call-ended') { + inCall = false; + showPlaceholder(true); + updateCallButtons(); + } + }); callBtn.onclick = async () => { const peer = selectedPeer || picker.getSelectedPeer(); - const networkId = BackOneNetworkSession.getNetworkId(); - if (!peer || !networkId) return; + if (!peer || !BackOneNetworkSession.getNetworkId()) return; try { - pc = new RTCPeerConnection(); - pc.ontrack = (ev) => { - root.querySelector('#video-remote').srcObject = ev.streams[0]; - showPlaceholder(false); - }; - localStream = await navigator.mediaDevices.getUserMedia({ audio: true, video: true }); - root.querySelector('#video-local').srcObject = localStream; - localStream.getTracks().forEach(t => pc.addTrack(t, localStream)); - const offer = await pc.createOffer(); - await pc.setLocalDescription(offer); - await BackOneAuth.fetchWithAuth('/controller/comms/call', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - type: 'video', - networkId, - targetId: peer.peerId, - sdp: offer.sdp - }) - }); inCall = true; - root.querySelector('#video-breadcrumb-state').textContent = 'Connected to'; + root.querySelector('#video-breadcrumb-state').textContent = 'Connecting to'; + updateCallLayout(); updateCallButtons(); + await BackOneWebRtcSession.startOutgoingCall(peer, 'video'); + bindStreams(); } catch (err) { - hangUp(); + await hangUp(); alert(err.message || 'Call failed'); } }; - container.querySelector('#video-hangup').onclick = hangUp; - container.querySelector('#video-hangup-stage').onclick = hangUp; + container.querySelector('#video-hangup').onclick = () => hangUp(); + container.querySelector('#video-hangup-stage').onclick = () => hangUp(); container.querySelector('#video-mic').onclick = () => { + const localStream = BackOneWebRtcSession.getLocalStream(); if (!localStream) return; micEnabled = !micEnabled; localStream.getAudioTracks().forEach(t => { t.enabled = micEnabled; }); @@ -188,6 +204,7 @@ window.BackOneAddons.video = function videoAddon() { }; container.querySelector('#video-cam').onclick = () => { + const localStream = BackOneWebRtcSession.getLocalStream(); if (!localStream) return; camEnabled = !camEnabled; localStream.getVideoTracks().forEach(t => { t.enabled = camEnabled; }); @@ -199,7 +216,10 @@ window.BackOneAddons.video = function videoAddon() { if (stage.requestFullscreen) stage.requestFullscreen(); }; + const active = BackOneWebRtcSession.getActiveCall(); + inCall = !!active && active.callType === 'video'; showPlaceholder(true); + if (inCall) bindStreams(); updateCallButtons(); } }; diff --git a/addons/voice/addon.json b/addons/voice/addon.json index 912271d..77a0927 100644 --- a/addons/voice/addon.json +++ b/addons/voice/addon.json @@ -2,7 +2,7 @@ "id": "voice", "name": "Voice Call P2P", "version": "1.17.1", - "requires": ["comms-core"], + "requires": ["comms-core", "webhub"], "backoneCore": { "apis": [ "/app/api/peers", diff --git a/addons/voice/frontend/index.js b/addons/voice/frontend/index.js index b06003e..6e23e4c 100644 --- a/addons/voice/frontend/index.js +++ b/addons/voice/frontend/index.js @@ -1,13 +1,16 @@ window.BackOneAddons = window.BackOneAddons || {}; window.BackOneAddons.voice = function voiceAddon() { - let pc = null; let picker = null; let selectedPeer = null; let inCall = false; - let localStream = null; let micEnabled = true; let root = null; + let remoteAudio = null; + + function escapeHtml(s) { + return String(s).replace(/&/g, '&').replace(//g, '>'); + } function updateConnectionBar() { const bar = root?.querySelector('#voice-conn-bar'); @@ -34,6 +37,16 @@ window.BackOneAddons.voice = function voiceAddon() { } } + function updateActiveBanner() { + const banner = root?.querySelector('#voice-active-banner'); + const label = root?.querySelector('#voice-active-label'); + if (!banner) return; + banner.style.display = inCall && selectedPeer ? 'flex' : 'none'; + if (label && selectedPeer) { + label.textContent = `In call with ${selectedPeer.displayName || selectedPeer.peerId}`; + } + } + function updateCallButtons() { const callBtn = root?.querySelector('#voice-call'); if (callBtn) callBtn.disabled = !selectedPeer || !BackOneNetworkSession.getNetworkId() || inCall; @@ -45,16 +58,26 @@ window.BackOneAddons.voice = function voiceAddon() { } updateHeadStatus(); updateConnectionBar(); + updateActiveBanner(); } - function hangUp() { - if (pc) { pc.close(); pc = null; } - if (localStream) { - localStream.getTracks().forEach(t => t.stop()); - localStream = null; + function syncInCallState() { + const active = BackOneWebRtcSession.getActiveCall(); + inCall = !!active && (active.callType === 'voice' || !active.callType); + if (inCall && active.peer) { + selectedPeer = active.peer.peerId ? active.peer : selectedPeer; + root?.querySelector('#voice-breadcrumb-state')?.textContent = 'Connected to'; } + updateCallButtons(); + } + + async function hangUp() { + await BackOneWebRtcSession.hangUp(); inCall = false; micEnabled = true; + if (remoteAudio) remoteAudio.srcObject = null; + const breadcrumbState = root?.querySelector('#voice-breadcrumb-state'); + if (breadcrumbState) breadcrumbState.textContent = 'Select peer'; updateCallButtons(); window.BackOneRingingPopup?.dismiss(); } @@ -63,11 +86,11 @@ window.BackOneAddons.voice = function voiceAddon() { mount(container) { root = container; container.innerHTML = ` -
+