diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-11 04:13:53 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-11 04:13:53 +0200 |
| commit | e23e33adeaf8ee7439187d4451c856b37816a51f (patch) | |
| tree | a41eef1fba34cdd642d25576395b4ad484a748ad /packages/meshbay-hub/src/meshbay_hub/static/webrtc-test.html | |
| parent | 60c4570e72e36c2a9720593c8baec74ee2ab52d6 (diff) | |
| download | meshbay-e23e33adeaf8ee7439187d4451c856b37816a51f.tar.gz | |
feat: Phase 9 — Web client SPA with WebRTC P2P transport
Complete browser-based client: Preact SPA with login, group file browser,
encrypted download, video playback, group chat, i18n, and dark/light theme.
Browser connects P2P to nodes behind residential NAT via WebRTC DataChannel
(aiortc). Hub handles signaling only — all data flows E2E.
Performance: pipelined downloads (8-chunk sliding window), binary msgpack
wire format (no base64), redundant I/O elimination. Large file downloads
stream to disk via File System Access API (showSaveFilePicker).
Validated on SFR + Orange residential NATs, Chrome + Firefox, IPv4/IPv6.
132 tests passing. Deployed to meshbay.org + Orange node.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/webrtc-test.html')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/webrtc-test.html | 19 |
1 files changed, 13 insertions, 6 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/webrtc-test.html b/packages/meshbay-hub/src/meshbay_hub/static/webrtc-test.html index 0d5750b..46003a7 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/webrtc-test.html +++ b/packages/meshbay-hub/src/meshbay_hub/static/webrtc-test.html @@ -68,7 +68,7 @@ <div id="log"></div> </div> -<script src="/transport.js"></script> +<script src="/transport.js?v=2"></script> <script> const HUB_URL = window.location.origin; const params = new URLSearchParams(window.location.search); @@ -76,13 +76,14 @@ let accessToken = null; let jwtToken = null; let transport = null; let fileIndex = null; +let connecting = false; // Pre-fill from URL params if (params.get('user')) document.getElementById('username').value = params.get('user'); if (params.get('pass')) document.getElementById('password').value = params.get('pass'); if (params.get('node')) document.getElementById('node-id').value = params.get('node'); if (params.get('group')) document.getElementById('group-id').value = params.get('group'); -if (params.get('file')) document.getElementById('file-id').value = params.get('file'); +if (params.get('file')) document.getElementById('file-id').value = params.get('file').replace(/\s+/g, ''); // Auto-run if all params provided if (params.get('auto')) { @@ -143,10 +144,14 @@ async function doLogin() { } async function doConnect() { + if (connecting) { logMsg('warn', 'Connect already in progress'); return; } const nodeId = document.getElementById('node-id').value; const groupId = document.getElementById('group-id').value; if (!nodeId) { logMsg('warn', 'Enter a node ID'); return; } + connecting = true; + if (transport) { transport.close(); transport = null; } + logMsg('info', `Connecting to node ${nodeId.substring(0, 8)}... via WebRTC`); setStep('step-connect', 'active'); @@ -164,7 +169,7 @@ async function doConnect() { logMsg('ok', `WebRTC connected in ${elapsed}ms`); logMsg('ok', ` MNP handshake_ack — node_pk: ${ack.node_pk?.substring(0, 16)}...`); - logMsg('ok', ` DataChannel: open, ordered, reliable`); + logMsg('ok', ` DataChannel state: ${transport._channel?.readyState}`); setStep('step-connect', 'done'); document.getElementById('connect-status').innerHTML = '<span class="badge">P2P OK</span>'; document.getElementById('btn-index').disabled = false; @@ -173,11 +178,13 @@ async function doConnect() { logMsg('err', `Connection FAILED: ${e.message}`); setStep('step-connect', 'fail'); document.getElementById('connect-status').innerHTML = '<span class="badge fail">FAIL</span>'; + } finally { + connecting = false; } } async function doFetchIndex() { - logMsg('info', 'Fetching Mesh Group Index via DataChannel...'); + logMsg('info', `Fetching Mesh Group Index... (channel: ${transport?._channel?.readyState})`); try { const t0 = performance.now(); const indexBytes = await transport.fetchIndex(); @@ -210,7 +217,7 @@ async function doFetchIndex() { } async function doFetchChunk() { - let fileId = document.getElementById('file-id').value.trim(); + let fileId = document.getElementById('file-id').value.replace(/\s+/g, ''); if (!fileId) { logMsg('warn', 'Enter a file_id (blake3 hex hash from node indexer log)'); @@ -219,7 +226,7 @@ async function doFetchChunk() { return; } - logMsg('info', `Fetching chunk 0 of file ${fileId.substring(0, 16)}... via DataChannel...`); + logMsg('info', `Fetching chunk 0 of ${fileId.substring(0, 16)}... (channel: ${transport?._channel?.readyState})`); try { const t0 = performance.now(); |