ZDDC/zddc/internal/handler
ZDDC 5e4d4fefb3 feat(zddc): serve a .zip as a virtual directory (zipfs + dispatch intercept)
zddc-server can now browse into a .zip file without the client
downloading the whole archive:
  - GET …/Foo.zip/                → JSON listing of the zip's members
                                     (Accept: application/json), or the
                                     browse SPA (HTML) — same content
                                     negotiation as ServeDirectory/.archive
  - GET …/Foo.zip/sub/doc.pdf     → extracts and streams that one member
                                     (Range / ETag / conditional GET via
                                     http.ServeContent)
  - GET …/Foo.zip                 → unchanged: the raw .zip download
  - PUT/DELETE/POST …/Foo.zip/…   → 405 (zip access is read-only)

New internal/zipfs package reconstructs directory levels from the zip's
flat central directory (synthesising intermediate dirs with no explicit
"<dir>/" entry, mirroring what browse does client-side with JSZip) and
drops zip-slip-unsafe entries ("..", absolute, backslash). New
handler.ServeZip wraps it. The dispatcher gets splitZipPath + an
intercept placed before the file-API branch (so a write to a path under
a .zip is refused, not silently mkdir'd); ACL is the chain of the
directory CONTAINING the zip — a zip carries no .zddc of its own, same
as the .archive virtual surface. The os.Stat-per-segment walk is gated
by a cheap ".zip/" substring check so ordinary requests are unaffected.

Also fixes two pre-existing dispatch-test failures uncovered along the
way: a non-existent top-level "*.html" URL was 302'ing to its slash
form (because the bare "*" project glob makes every first-level segment
"declared") — the cascade-declared no-slash block now requires a
directory-shaped URL (trailing slash, or no file extension); and the
stale TestDispatchSlashRouting expectation that archive/<party>/mdl/
302s to mdl/table.html was updated to match the intended behaviour
(the default-MDL virtual fallback shows the browse listing there; only
a real on-disk tables: + *.table.yaml triggers the bounce).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 12:17:47 -05:00
..
archivehandler.go refactor(zddc-server): demote routing-shape redirects from 301 to 302 2026-05-10 14:37:02 -05:00
archivehandler_test.go feat(archive): canonicalize deep .archive URLs + permissions follow the file 2026-05-07 06:28:07 -05:00
authcheck.go feat(zddc-server): /.auth/admin forward_auth endpoint 2026-05-01 21:08:39 -05:00
authcheck_test.go feat(zddc-server): /.auth/admin forward_auth endpoint 2026-05-01 21:08:39 -05:00
cors.go feat(server): authenticated CRUD + verb-based RBAC with WORM archive folders 2026-05-05 15:58:04 -05:00
cors_test.go feat(zddc-server): admin debug page + X-Auth-Request-Email default + hidden-segment guard 2026-04-28 14:02:06 -05:00
default-mdl.form.yaml feat(mdl): default columns mirror tracking-number components + customizable 2026-05-09 11:09:31 -05:00
default-mdl.table.yaml feat(mdl): default columns mirror tracking-number components + customizable 2026-05-09 11:09:31 -05:00
directory.go feat(zddc): dir_tool key — make the slash/no-slash routing convention configurable 2026-05-12 11:46:55 -05:00
directory_test.go fix(zddc-server): mdl slash form serves browse; .zddc viewable at every depth 2026-05-11 12:45:16 -05:00
fileapi.go chore(zddc): migrate mkdir auto-own hook to the cascade, drop dead predicates 2026-05-12 10:42:49 -05:00
fileapi_test.go refactor(zddc): worm: is a list of principals, not a {principal: verbs} map 2026-05-12 09:40:15 -05:00
formhandler.go refactor(tables): in-dir convention + unified table+form HTML bundle 2026-05-09 09:15:26 -05:00
formhandler_test.go refactor(tables): in-dir convention + unified table+form HTML bundle 2026-05-09 09:15:26 -05:00
logring.go feat(zddc-server): user profile page replaces /.admin/ 2026-04-29 16:32:02 -05:00
logring_test.go feat(zddc-server): admin debug page + X-Auth-Request-Email default + hidden-segment guard 2026-04-28 14:02:06 -05:00
middleware.go feat(server): case-insensitive URL canonicalization at dispatch 2026-05-09 09:09:47 -05:00
middleware_test.go feat(server): self-issued bearer tokens + --no-auth flag 2026-05-08 07:40:28 -05:00
profilehandler.go feat(handler): expose inherit fence in /.profile/effective-policy 2026-05-07 11:02:33 -05:00
profilehandler_test.go feat(handler): expose inherit fence in /.profile/effective-policy 2026-05-07 11:02:33 -05:00
profilepage.go feat: lockstep release infra + cascade/.archive fixes + profile perf + page redesign 2026-05-01 20:11:38 -05:00
profileprojects.go feat(zddc-server): user profile page replaces /.admin/ 2026-04-29 16:32:02 -05:00
projecthandler.go refactor(zddc-server): demote routing-shape redirects from 301 to 302 2026-05-10 14:37:02 -05:00
projecthandler_test.go refactor(landing): project landing is now a single-file SPA, not server-rendered 2026-05-10 07:57:30 -05:00
projectshandler.go feat(server): reference Rego, parity test, decision cache, listing ETags 2026-05-04 17:46:24 -05:00
projectshandler_test.go feat(zddc-server): user profile page replaces /.admin/ 2026-04-29 16:32:02 -05:00
reviewinghandler.go Round of UX fixes: tool strip removed, MDL routing, browse markdown layout, reviewing depth-2 2026-05-11 12:30:34 -05:00
reviewinghandler_test.go Round of UX fixes: tool strip removed, MDL routing, browse markdown layout, reviewing depth-2 2026-05-11 12:30:34 -05:00
static.go Initial commit 2026-04-27 11:05:47 -05:00
tablehandler.go chore(zddc): remove dead canonical-folder predicates 2026-05-11 16:01:43 -05:00
tablehandler_test.go refactor(tables): in-dir convention + unified table+form HTML bundle 2026-05-09 09:15:26 -05:00
tables.html feat(zddc): incoming/ is a controlled drop zone — project_team read-only, doc controller QCs 2026-05-12 10:29:44 -05:00
tokenhandler.go feat(server): self-issued bearer tokens + --no-auth flag 2026-05-08 07:40:28 -05:00
tokenhandler_test.go feat(server): self-issued bearer tokens + --no-auth flag 2026-05-08 07:40:28 -05:00
zddc_assets.go feat(zddc-server): user profile page replaces /.admin/ 2026-04-29 16:32:02 -05:00
zddceditor.go feat(handler): per-directory <dir>/.zddc.html editor URL 2026-05-07 11:37:36 -05:00
zddcfile.go fix(zddc-server): mdl slash form serves browse; .zddc viewable at every depth 2026-05-11 12:45:16 -05:00
zddcfile_test.go fix(zddc-server): mdl slash form serves browse; .zddc viewable at every depth 2026-05-11 12:45:16 -05:00
zddchandler.go feat(zddc-server): apps section in .zddc editor 2026-05-01 15:25:42 -05:00
zddchandler_test.go feat(archive): periodic rescan + admin reindex endpoint 2026-05-06 08:50:51 -05:00
ziphandler.go feat(zddc): serve a .zip as a virtual directory (zipfs + dispatch intercept) 2026-05-12 12:17:47 -05:00
ziphandler_test.go feat(zddc): serve a .zip as a virtual directory (zipfs + dispatch intercept) 2026-05-12 12:17:47 -05:00