From 5b65be24839bf2d089913f696733508c1fbd4c57 Mon Sep 17 00:00:00 2001 From: Local Dev Date: Sat, 29 Aug 2026 13:27:42 +0200 Subject: [PATCH] gateway: auto-index directory paths for s3 records MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit silentmode.bch/tools/ was returning the Sia NoSuchKey XML because the gateway only auto-appended index.html for the root path (/), not for sub-directory paths (/tools/, /apps/, etc). Trailing-slash URLs now resolve to /index.html — matches Ariadne's bnsd.js which already had the same rewrite on the client side. Verified: /bns/silentmode.bch/tools/ returns 200 with the tools page (was 404 NoSuchKey). Fixes the user-reported "download link on silentmode.bch/tools/#theseus shows an XML NoSuchKey error" — the browser was requesting /tools/ (trailing slash from the URL path), gateway looked up bucket key bns/silentmode/tools/ which doesn't exist, forwarded the S3 error. Deployed on the VPS (/opt/bns-gateway/) with the in-place edit path that keeps the same-dir import shape there (repo has ../lib/, VPS has ./resolver-web.js — the two file layouts intentionally differ). --- src/gateway/public-gateway.mjs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/gateway/public-gateway.mjs b/src/gateway/public-gateway.mjs index 34aec4d..28661b8 100644 --- a/src/gateway/public-gateway.mjs +++ b/src/gateway/public-gateway.mjs @@ -171,7 +171,14 @@ async function serve(res, name, reqPath, baseHref) { case "s3": { const sub = subFolder(name); let base = r.s3; if (base.endsWith("/") && sub) base += sub + "/"; - const key = base.endsWith("/") ? base + (reqPath === "/" ? "index.html" : reqPath.slice(1)) : base; + // Auto-index directory paths. S3 has no dir listing, so `/tools/` + // would 404 (NoSuchKey) without this — matches how nginx handles + // `try_files … /index.html` and how Ariadne's bnsd.js rewrites the + // same URLs client-side. + const rel = reqPath === "/" ? "index.html" + : 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}`); const buf = Buffer.from(await up.arrayBuffer()); let ct = up.headers.get("content-type"); if (!ct || ct === "application/octet-stream") ct = guess(key);