diff --git a/apps/web/next.config.ts b/apps/web/next.config.ts index 911e3e8c4..a9fdad9df 100644 --- a/apps/web/next.config.ts +++ b/apps/web/next.config.ts @@ -176,16 +176,14 @@ const nextConfig: NextConfig = { source: "/c/:token", destination: `${apiBase}/c/:token`, }, - // Hosted dashboard builds use basePath="/app". Public share links must - // remain top-level no-auth URLs, so /s/* is rewritten into the app route - // without changing the browser URL. - ...(APP_BASE_PATH - ? [{ - source: "/s/:path*", - destination: `${APP_BASE_PATH}/s/:path*`, - basePath: false as const, - }] - : []), + // /s/* public share links: NOT rewritten here. A dashboard self-rewrite of + // a top-level path into the basePath is rejected by Next.js ("rewrites urls + // outside of the basePath. Please use a destination that starts with + // http(s)://") and broke hosted (basePath="/app") builds. It was also + // ineffective: on the hosted host the apex URL `/s/*` reaches the landing, + // not this dashboard, so the apex is where `/s/*` must be rewritten into + // `/app/s/*` (mirroring how `/app/*` is served). OSS (no basePath) serves + // /s/[token] directly with no rewrite. ]; }, async headers() { diff --git a/apps/web/tests/approval-batch-share-public.test.ts b/apps/web/tests/approval-batch-share-public.test.ts index e97bb7b17..6054c53ca 100644 --- a/apps/web/tests/approval-batch-share-public.test.ts +++ b/apps/web/tests/approval-batch-share-public.test.ts @@ -139,11 +139,16 @@ describe("public approval batch share route", () => { expect(fetchMock).not.toHaveBeenCalled(); }); - it("keeps minted /s links top-level when the hosted app uses basePath /app", () => { + it("does NOT self-rewrite /s/* in the dashboard config (apex handles it)", () => { + // A dashboard self-rewrite of top-level /s/* into the basePath is rejected by + // Next.js ("rewrites urls outside of the basePath") and broke hosted + // (basePath="/app") builds. It was also ineffective: the hosted /s/* URL + // reaches the landing apex, not this dashboard, so the apex rewrites + // /s/* -> /app/s/* (mirroring /app/*). OSS (no basePath) serves /s/[token] + // directly. So next.config must NOT contain the /s/ rewrite. const config = readFileSync(join(process.cwd(), "next.config.ts"), "utf-8"); - expect(config).toContain('source: "/s/:path*"'); - expect(config).toContain("destination: `${APP_BASE_PATH}/s/:path*`"); - expect(config).toContain("basePath: false"); + expect(config).not.toContain('source: "/s/:path*"'); + expect(config).not.toContain("destination: `${APP_BASE_PATH}/s/:path*`"); }); it("does not call API_BASE directly from the logged-out share card", () => {