Skip to content

F146: URL-based site routing (proxy-rewrite core)#1

Merged
cbroberg merged 2 commits into
mainfrom
feat/f146-url-routing
Jun 1, 2026
Merged

F146: URL-based site routing (proxy-rewrite core)#1
cbroberg merged 2 commits into
mainfrom
feat/f146-url-routing

Conversation

@cbroberg

Copy link
Copy Markdown
Contributor

F146 — URL-Based Site Routing

Active site can now live in the URL: `/admin/{slug}/...`. Solves the daily parallel-tab cookie tug-of-war, makes bookmarks stable, and makes shared admin links land on the right site.

Approach: proxy-rewrite (NOT a 25-folder move)

`proxy.ts` detects `/admin/{slug}/...` where `{slug}` is a known registry site, injects `cms-active-*` cookies on the forwarded request, and `NextResponse.rewrite()`s back to `/admin/...` so the existing route tree renders unchanged. Browser keeps the pretty slug URL. Far lower risk than physically moving 25 route folders + a 22-file link codemod — those links keep working via the cookie fallback and can migrate incrementally via the new `siteAdminPath()` helper.

What's in this PR

  • `lib/site-slug-routing.ts` — reserved-segment set + `parseSiteSlugPath()` + `siteAdminPath()` (13 unit tests, all green)
  • `proxy.ts` — slug resolution + cookie injection + rewrite; strips any existing `cms-active-*` before injecting so the URL slug can't lose to a duplicate same-named cookie (found via browser verification — applied to both the slug path and the `?site=` API path)
  • `switch-context.ts` — `switchSite()` navigates to `/admin/{slug}`

Verified against :3010 (HTTPS)

Case Result
`/admin/trail/settings` `<title>trail-landing</title>` ✅
`/admin/sanneandersen/settings` `<title>Sanne Andersen</title>` ✅
URL slug vs conflicting cookie (3 cross-site cases) URL wins ✅
No slug → cookie fallback works ✅
Unknown slug falls through to legacy 307 redirect ✅
Reserved segment (`/admin/settings`) unaffected ✅

Deferred (follow-up, documented in plan-doc)

  • Incremental link codemod (`href="/admin/..."` → `siteAdminPath()`) — not required for correctness; cookie fallback covers it
  • Site-slug-equals-collection-name ambiguity: current rule = first segment is always the site; collections only at `/admin/{site}/content/{collection}`

🤖 Generated with Claude Code

cbroberg and others added 2 commits May 31, 2026 20:10
Active site can now live in the URL: /admin/{slug}/... resolves the site,
injects cms-active-* cookies on the forwarded request, and rewrites back to
/admin/... so the existing route tree renders unchanged. Browser keeps the
pretty slug URL. Solves the parallel-tab cookie tug-of-war: two tabs on
different /admin/{slug} URLs render their own site regardless of the shared
cookie.

Approach: proxy-rewrite, NOT a 25-folder move — far lower risk, no link
codemod required up front (cookie fallback keeps existing /admin/... links
working). New lib/site-slug-routing.ts owns the reserved-segment set + slug
parsing + siteAdminPath() helper for incremental link migration.

Critical fix found via browser verification: must STRIP existing cms-active-*
from the cookie header before injecting, else a duplicate same-named cookie
shadows the URL slug (parser keeps the first/stale one) and the URL loses to
the cookie. Applied to both the slug path and the ?site= API path.

Verified against :3010 (HTTPS): /admin/trail/settings → <title>Trail</title>,
/admin/sanneandersen/settings → Sanne Andersen, and URL wins over a
conflicting cookie in all 3 cross-site cases; no-slug still falls back to
cookie. 13 unit tests for parse/build/round-trip. Unknown slug falls through
to legacy collection redirect (307); reserved segments unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
switchSite() now hard-navigates to siteAdminPath(destination, siteId) so a
switch lands on the slug URL (/admin/{slug}) instead of bare /admin. The
cookie is still set as fallback for cookie-only callers. Combined with the
proxy rewrite, switching to a site now produces a stable, bookmarkable,
tab-independent URL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cbroberg cbroberg merged commit 9e19ffc into main Jun 1, 2026
6 of 7 checks passed
@cbroberg cbroberg deleted the feat/f146-url-routing branch June 1, 2026 10:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant