From fab44542bc3f675cb47f9e9d5f91f92b34190d37 Mon Sep 17 00:00:00 2001 From: ZDDC Date: Fri, 22 May 2026 10:59:24 -0500 Subject: [PATCH] fix(archive): normalize trailing slash in multi-project server listing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GET / Accept:application/json changed shape in the May-2026 reshape: it returns listing.FileInfo entries (directory names carry a trailing '/', and the array can include non-directory entries) instead of the legacy ProjectInfo array (bare names). archive.html's multi-project mode (?projects=A,B) intersected those server names against the projectFilter parsed from the URL — which is slash-free — so every listed project missed the intersection, projectFilter emptied, the "you don't have access" banner showed, and nothing scanned: empty projects dropdown, no parties/transmittal folders. Normalise serverNames (and the projectTitles keys) to bare directory names and filter the listing to is_dir entries before intersecting. The scan in source.js already uses the slash-free projectFilter directly, so this single normalization restores the whole flow. Verified headless against a 2-project fixture, old vs new binary: old -> projectFilter [], no-access warning, no parties rendered; new -> projectFilter [182246,197072], no warning, ACME/BETACO parties rendered. Reaches prod via the next zddc-server release (archive.html is //go:embed'd). Co-Authored-By: Claude Opus 4.7 (1M context) --- archive/js/app.js | 17 ++++++++++++++--- archive/js/source.js | 8 ++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/archive/js/app.js b/archive/js/app.js index 65b7728..4441fe5 100644 --- a/archive/js/app.js +++ b/archive/js/app.js @@ -138,11 +138,22 @@ var serverProjects = await resp.json(); if (Array.isArray(serverProjects) && serverProjects.length > 0 && serverProjects[0] && typeof serverProjects[0].name === 'string') { - serverNames = new Set(serverProjects.map(function(p) { return p.name; })); + // GET / Accept: application/json returns listing.FileInfo + // entries (not the legacy ProjectInfo shape): directory + // names carry a trailing "/", and the listing can include + // non-directory entries. Normalise to bare directory names + // so they match the slash-free projectFilter parsed from + // ?projects= (url-state.js). Without this, every URL-listed + // project misses the intersection below → "no access" + // banner + empty scan. + var bareName = function (p) { return p.name.replace(/\/+$/, ''); }; + var isProjectDir = function (p) { return p.is_dir === true || /\/$/.test(p.name); }; + var projectEntries = serverProjects.filter(isProjectDir); + serverNames = new Set(projectEntries.map(bareName)); var titles = {}; - serverProjects.forEach(function (p) { + projectEntries.forEach(function (p) { if (p && typeof p.title === 'string' && p.title) { - titles[p.name] = p.title; + titles[bareName(p)] = p.title; } }); window.app.projectTitles = titles; diff --git a/archive/js/source.js b/archive/js/source.js index 5767675..9feb94d 100644 --- a/archive/js/source.js +++ b/archive/js/source.js @@ -402,10 +402,10 @@ } async function scanHttpRoot(scanRootUrl, rootUrl, callbacks) { - // Mode 1 — multi-project (?projects= set). Skip listing scanRootUrl entirely: - // the zddc-server returns a ProjectInfo array there (not a Caddy fileInfo - // listing), so iterating it as if it were a directory listing wouldn't work. - // Project URLs are deterministic — go straight to each one. + // Mode 1 — multi-project (?projects= set). Skip listing scanRootUrl + // entirely: project URLs are deterministic, so go straight to each one + // (the names in projectFilter, slash-normalised in app.js against the + // server's root listing). Avoids depending on the root listing's shape. if (window.app.projectFilter && window.app.projectFilter.size > 0) { const tasks = []; for (const name of window.app.projectFilter) {