From 9f3c9abf5b096edea465a720175e4bae4295841e Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Fri, 27 Feb 2026 16:20:36 -0800 Subject: [PATCH] fix: canonicalize space URLs to subdomain pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rspace.online/{space}/{moduleId} now 301-redirects to {space}.rspace.online/{moduleId} — spaces are subdomains, not path segments. e.g. rspace.online/demo/rnotes → demo.rspace.online/rnotes. Landing pages (rspace.online/{moduleId}) unchanged. API sub-path rewrite for demo "Try Demo" unchanged. Co-Authored-By: Claude Opus 4.6 --- server/index.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/server/index.ts b/server/index.ts index 972a47e..00de104 100644 --- a/server/index.ts +++ b/server/index.ts @@ -1124,15 +1124,17 @@ const server = Bun.serve({ return app.fetch(rewrittenReq); } - // ── Bare-domain module routes: rspace.online/{moduleId}[/...] ── - // Exact module path → landing page; sub-paths rewrite to /demo/... + // ── Bare-domain routing: rspace.online/{...} ── if (!subdomain && hostClean.includes("rspace.online")) { const pathSegments = url.pathname.split("/").filter(Boolean); if (pathSegments.length >= 1) { const firstSegment = pathSegments[0]; const allModules = getAllModules(); + const knownModuleIds = new Set(allModules.map((m) => m.id)); const mod = allModules.find((m) => m.id === firstSegment); + if (mod) { + // rspace.online/{moduleId} → landing page if (pathSegments.length === 1) { // 1. Check for inline rich landing page if (mod.landingPage) { @@ -1157,12 +1159,23 @@ const server = Bun.serve({ }); return new Response(html, { headers: { "Content-Type": "text/html" } }); } - // Sub-paths → rewrite to /demo/{moduleId}/... + // rspace.online/{moduleId}/sub-path → rewrite to demo space internally const rewrittenPath = `/demo${url.pathname}`; const rewrittenUrl = new URL(rewrittenPath + url.search, `http://localhost:${PORT}`); const rewrittenReq = new Request(rewrittenUrl, req); return app.fetch(rewrittenReq); } + + // rspace.online/{space}/{...} → redirect to {space}.rspace.online/{...} + // (space is not a module ID — it's a space slug, canonicalize to subdomain) + if (!knownModuleIds.has(firstSegment) && pathSegments.length >= 2) { + const space = firstSegment; + const rest = "/" + pathSegments.slice(1).join("/"); + const baseDomain = hostClean.replace(/^www\./, ""); + return Response.redirect( + `${url.protocol}//${space}.${baseDomain}${rest}${url.search}`, 301 + ); + } } }