diff options
| author | Doan Luu Duc Tai <doan@luuduct.ai> | 2026-10-31 08:05:08 -0500 |
|---|---|---|
| committer | Doan Luu Duc Tai <doan@luuduct.ai> | 2026-10-31 08:05:08 -0500 |
| commit | fbb06ab09681079c01cc9b4eb13731ef17f4ab41 (patch) | |
| tree | 9d052f079aa6e1db4e8b89d7f76529cd66d30c38 /crates/chat-server/web | |
| parent | d809d86182ab802472ae7d5e74cab66ba7bfa397 (diff) | |
Show crypto download progress and centralize license links
Diffstat (limited to 'crates/chat-server/web')
| -rw-r--r-- | crates/chat-server/web/app.js | 54 | ||||
| -rw-r--r-- | crates/chat-server/web/index.html | 2 | ||||
| -rw-r--r-- | crates/chat-server/web/javascript.html | 2 |
3 files changed, 52 insertions, 6 deletions
diff --git a/crates/chat-server/web/app.js b/crates/chat-server/web/app.js index 3c97f66..4b0da63 100644 --- a/crates/chat-server/web/app.js +++ b/crates/chat-server/web/app.js @@ -356,12 +356,58 @@ element('lock').addEventListener('click', () => { if (session) api({ op: 'lock' window.addEventListener('pagehide', clearState); window.addEventListener('pageshow', event => { if (event.persisted) clearState(); }); +async function downloadCrypto() { + const controller = new AbortController(); + let timer; + let reader; + const refreshTimeout = () => { + clearTimeout(timer); + timer = setTimeout(() => controller.abort(), 45000); + }; + try { + refreshTimeout(); + const response = await fetch('/crypto.wasm', { cache: 'no-store', credentials: 'omit', signal: controller.signal }); + if (!response.ok) throw new Error(`Cryptographic module download failed (HTTP ${response.status}). Reload to try again.`); + const length = response.headers.get('content-encoding') ? 0 : Number(response.headers.get('content-length')); + const total = Number.isSafeInteger(length) && length > 0 ? length : 0; + const chunks = []; + let received = 0; + const progress = () => { + const loaded = (received / 1048576).toFixed(1); + if (total) { + const percent = Math.min(100, Math.floor(received * 100 / total)); + const filled = Math.floor(percent / 5); + status(`Downloading cryptographic module... [${'#'.repeat(filled)}${'-'.repeat(20 - filled)}] ${percent}% (${loaded} / ${(total / 1048576).toFixed(1)} MiB)`); + } else status(`Downloading cryptographic module... ${loaded} MiB received`); + }; + progress(); + reader = response.body.getReader(); + for (;;) { + const { done, value } = await reader.read(); + if (done) break; + refreshTimeout(); + chunks.push(value); + received += value.byteLength; + progress(); + } + if (!received) throw new Error('The cryptographic module download was empty. Reload to try again.'); + const bytes = new Uint8Array(received); + let offset = 0; + for (const chunk of chunks) { bytes.set(chunk, offset); offset += chunk.byteLength; } + return bytes.buffer; + } catch (error) { + if (controller.signal.aborted) throw new Error('Cryptographic module download stalled for 45 seconds. Check your connection and reload to try again.'); + throw error; + } finally { + clearTimeout(timer); + reader?.releaseLock(); + } +} + async function start() { - wasm = fetch('/crypto.wasm', { cache: 'no-store', credentials: 'omit' }).then(response => { - if (!response.ok) throw new Error('Build the WASI cryptographic module first'); - return response.arrayBuffer(); - }); + wasm = downloadCrypto(); await wasm; + status('Cryptographic module downloaded. Connecting to chat...'); config = await api({ op: 'config' }); if (config.origin !== location.origin) throw new Error('Server origin mismatch'); show('owner-entry'); diff --git a/crates/chat-server/web/index.html b/crates/chat-server/web/index.html index b2a4363..70f3ebb 100644 --- a/crates/chat-server/web/index.html +++ b/crates/chat-server/web/index.html @@ -100,7 +100,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. <noscript>This chat requires free JavaScript and WebAssembly for local cryptography. The main website remains JavaScript-free.</noscript> <footer class="source-footer" aria-label="Licensing and source"> <a class="project-license" href="https://www.gnu.org/licenses/agpl-3.0.html">[ LICENSE ]</a> -<a class="dependency-licenses" href="/THIRD-PARTY-LICENSES.html">[ THIRD PARTY LICENSES ]</a> +<a class="dependency-licenses" href="{{SITE_ORIGIN}}/THIRD-PARTY-LICENSES.html">[ THIRD PARTY LICENSES ]</a> <a href="/SOURCE.tar.gz">[ SOURCE CODE ]</a> </footer> </main> diff --git a/crates/chat-server/web/javascript.html b/crates/chat-server/web/javascript.html index 205a103..644e5a1 100644 --- a/crates/chat-server/web/javascript.html +++ b/crates/chat-server/web/javascript.html @@ -24,5 +24,5 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. <tr><td><a href="/wasi.mjs">wasi.mjs</a></td><td><a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0</a></td><td><a href="/SOURCE.tar.gz">Complete corresponding source</a></td></tr> </tbody></table></div> <p>The WebAssembly module is compiled from the Rust workspace and unmodified OpenSSL 3.6.5, under their respective free software licenses. The source archive includes first-party sources, Cargo dependencies, the OpenSSL source archive, build scripts, and notices. General-purpose Rust and WASI SDK tools are documented with verified download locations in CHAT-OPENSSL-MIGRATION.txt.</p> -<p><a href="/THIRD-PARTY-LICENSES.html">Third-party licenses</a> / <a href="/SOURCE.tar.gz">Source archive</a> / <a href="/">Return</a></p> +<p><a href="{{SITE_ORIGIN}}/THIRD-PARTY-LICENSES.html">Third-party licenses</a> / <a href="/SOURCE.tar.gz">Source archive</a> / <a href="/">Return</a></p> </main></body></html> |
