From 3e6eb1d39297e36a815901ba1677cc8b870c7b20 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Wed, 16 Sep 2026 18:39:11 +0200 Subject: [PATCH] feat(argus): SPA fallback for missing paths under BNS domains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a request to /bns// misses in Sia AND the path has no file extension (looks like a client-side route, not an asset), serve the domain's own index.html instead. Same shape as nginx's try_files … /index.html — lets any hosted site use the History API for pretty URLs. Extension-having requests (missing .png / .js / .json / …) still 404 as they should — those should never be routed by a client-side router and swallowing them would hide real bugs. Deployed to silentmode 2026-09-16: scp Argus/src/gateway/public-gateway.mjs silentmode:/tmp/public-gateway.mjs sudo cp … /opt/bns-gateway/public-gateway.mjs sudo systemctl restart bns-gateway Companion Deviant commit ("widescreen swap + URL routing") uses this to serve /bns/potidaea.asm/katalogos etc. from the same index.html. --- src/gateway/public-gateway.mjs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/gateway/public-gateway.mjs b/src/gateway/public-gateway.mjs index 92736a1..a5627e6 100644 --- a/src/gateway/public-gateway.mjs +++ b/src/gateway/public-gateway.mjs @@ -234,7 +234,17 @@ async function serve(res, name, reqPath, baseHref) { : reqPath.endsWith("/") ? reqPath.slice(1) + "index.html" : reqPath.slice(1); const key = base.endsWith("/") ? base + rel : base; - const up = await aws.fetch(`${S3_ENDPOINT}/${key}`); + let up = await aws.fetch(`${S3_ENDPOINT}/${key}`); + // SPA fallback — if the extension-less path 404s, serve the domain's + // own index.html so client-side routers (History API) can pick up + // /katalogos, /anthemus, /artemision, etc. under any BNS mount. + // We only fall back when the requested path has no file extension — + // requests for missing images / json / js still 404 as they should. + if (up.status === 404 && !/\.[a-z0-9]+$/i.test(reqPath) && reqPath !== "/") { + const fallbackKey = (base.endsWith("/") ? base : base + "/") + "index.html"; + const fb = await aws.fetch(`${S3_ENDPOINT}/${fallbackKey}`); + if (fb.ok) { up = fb; } + } const buf = Buffer.from(await up.arrayBuffer()); let ct = up.headers.get("content-type"); if (!ct || ct === "application/octet-stream") ct = guess(key); if (ct.includes("text/html")) { res.writeHead(up.status, { "content-type": ct }); return res.end(injectBase(buf.toString("utf8"), baseHref)); }