summaryrefslogtreecommitdiff
path: root/crates/chat-server/web/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'crates/chat-server/web/app.js')
-rw-r--r--crates/chat-server/web/app.js54
1 files changed, 50 insertions, 4 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');