From 85098bf98922db68c977253b331c92b5f39e71fa Mon Sep 17 00:00:00 2001 From: Local Dev Date: Sat, 24 Oct 2026 00:57:38 +0300 Subject: [PATCH] prometheus: submit page shows what the server actually returned MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'Unexpected token <' error landed the user in a dead-end — the message named the failure (JSON.parse choked on an HTML body) but hid the body itself. Now the submit path reads the response as text first, tries JSON.parse, and on failure surfaces the HTTP status, the content type, and the first 180 chars of the body with tags stripped. That tells a publisher 'Caddy returned 413, max body size X' instead of 'Network error'. Pure diagnostic — no change to the success path. --- service/pages.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) 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;