From 5b7ee71eb99e88fecfa0c421c1f6b785e6e7e0ff Mon Sep 17 00:00:00 2001 From: Jeff Emmett Date: Sat, 28 Feb 2026 04:38:42 +0000 Subject: [PATCH] fix: case-insensitive module routing for mixed-case URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit rTrips, rFunds, rVote etc. now resolve correctly — normalizes module ID to lowercase in subdomain routing, bare-domain routing, and 404 fallback checks. Co-Authored-By: Claude Opus 4.6 --- server/index.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/server/index.ts b/server/index.ts index a3504f9..26d74e4 100644 --- a/server/index.ts +++ b/server/index.ts @@ -1123,9 +1123,14 @@ const server = Bun.serve({ return app.fetch(req); } + // Normalize module ID to lowercase (rTrips → rtrips) + const normalizedPath = "/" + pathSegments.map((seg, i) => + i === 0 ? seg.toLowerCase() : seg + ).join("/"); + // Rewrite: /{moduleId}/... → /{space}/{moduleId}/... // e.g. demo.rspace.online/vote/api/polls → /demo/vote/api/polls - const rewrittenPath = `/${subdomain}${url.pathname}`; + const rewrittenPath = `/${subdomain}${normalizedPath}`; const rewrittenUrl = new URL(rewrittenPath + url.search, `http://localhost:${PORT}`); const rewrittenReq = new Request(rewrittenUrl, req); return app.fetch(rewrittenReq); @@ -1135,7 +1140,7 @@ const server = Bun.serve({ if (!subdomain && hostClean.includes("rspace.online")) { const pathSegments = url.pathname.split("/").filter(Boolean); if (pathSegments.length >= 1) { - const firstSegment = pathSegments[0]; + const firstSegment = pathSegments[0].toLowerCase(); const allModules = getAllModules(); const knownModuleIds = new Set(allModules.map((m) => m.id)); const mod = allModules.find((m) => m.id === firstSegment); @@ -1167,7 +1172,8 @@ const server = Bun.serve({ return new Response(html, { headers: { "Content-Type": "text/html" } }); } // rspace.online/{moduleId}/sub-path → rewrite to demo space internally - const rewrittenPath = `/demo${url.pathname}`; + const normalizedPath = "/" + [firstSegment, ...pathSegments.slice(1)].join("/"); + const rewrittenPath = `/demo${normalizedPath}`; const rewrittenUrl = new URL(rewrittenPath + url.search, `http://localhost:${PORT}`); const rewrittenReq = new Request(rewrittenUrl, req); return app.fetch(rewrittenReq); @@ -1195,7 +1201,7 @@ const server = Bun.serve({ const parts = url.pathname.split("/").filter(Boolean); // Check if this is under a known module — if so, the module's 404 is authoritative const knownModuleIds = getAllModules().map((m) => m.id); - const isModulePath = parts.length >= 2 && knownModuleIds.includes(parts[1]); + const isModulePath = parts.length >= 2 && knownModuleIds.includes(parts[1].toLowerCase()); if (!isModulePath && parts.length >= 1 && !parts[0].includes(".")) { // Not a module path — could be a canvas SPA route, try fallback