diff --git a/service/pages.js b/service/pages.js index a40e797..8620a0b 100644 --- a/service/pages.js +++ b/service/pages.js @@ -606,6 +606,8 @@ export function submit() { }; const $ = (id) => document.getElementById(id); + const esc = (s) => String(s).replace(/[&<>"']/g, (c) => ( + {'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); const SUBS = { 1: 'the binary (up to 2 GiB — free below 100 MiB)', 2: 'name, publisher name, category, description', @@ -713,7 +715,21 @@ export function submit() { let json; try { const r = await fetch('/api/upload', { method: 'POST', body }); - json = await r.json(); + // Read as text first — if the server returned an HTML error page + // (Caddy 413/502, a cached old vhost, etc.) the JSON.parse below + // throws "Unexpected token '<'" and the user has no idea what + // actually came back. Show them the HTTP status and the first + // chunk of the body instead. + const text = await r.text(); + try { + json = JSON.parse(text); + } catch (parseErr) { + const preview = text.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 180); + status.innerHTML = '⚠️ Server returned non-JSON — HTTP ' + r.status + + ' (' + (r.headers.get('content-type') || 'no content-type') + ')' + + (preview ? '. Snippet: ' + esc(preview) + '' : ''); + return; + } } catch (err) { status.innerHTML = '⚠️ Network error — ' + String(err?.message || err); return;