summaryrefslogtreecommitdiff
path: root/crates/chat-server/web
diff options
context:
space:
mode:
Diffstat (limited to 'crates/chat-server/web')
-rw-r--r--crates/chat-server/web/app.js54
-rw-r--r--crates/chat-server/web/index.html4
-rw-r--r--crates/chat-server/web/javascript.html2
3 files changed, 53 insertions, 7 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 79026b0..70f3ebb 100644
--- a/crates/chat-server/web/index.html
+++ b/crates/chat-server/web/index.html
@@ -41,7 +41,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
<div class="ascii-panel introduction">
<div class="ascii-bar" aria-hidden="true"><span>+</span><span class="ascii-rule">--------------------------------------------------------------------------------</span><span>+</span></div>
<div class="panel-body">
-<p class="build-status">EXPERIMENTAL / LOCAL TEST</p>
+<p class="build-status">EXPERIMENTAL</p>
<p>Keep your file and password. There is no account recovery.<br><a href="/LIMITATIONS.txt">Read the limitations</a> before using this experimental messenger.</p>
</div>
<div class="ascii-bar" aria-hidden="true"><span>+</span><span class="ascii-rule">--------------------------------------------------------------------------------</span><span>+</span></div>
@@ -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>