diff --git a/apps/studio/router.tsx b/apps/studio/router.tsx index 5c1804b3035..8e31e6add7e 100644 --- a/apps/studio/router.tsx +++ b/apps/studio/router.tsx @@ -23,6 +23,10 @@ export function getRouter() { context, scrollRestoration: true, defaultPreload: 'intent', + // Inlined via Vite's `define` at build time; stays undefined (= app at `/`) + // unless NEXT_PUBLIC_BASE_PATH is set. Must agree with Vite `base` and + // Nitro `baseURL` — see vite.config.ts. + basepath: process.env.NEXT_PUBLIC_BASE_PATH || undefined, }) setupRouterSsrQueryIntegration({ router, queryClient: context.queryClient }) diff --git a/apps/studio/routes/index.tsx b/apps/studio/routes/index.tsx index d58928d9ed0..c23073c5fad 100644 --- a/apps/studio/routes/index.tsx +++ b/apps/studio/routes/index.tsx @@ -1,9 +1,28 @@ -import { createFileRoute } from '@tanstack/react-router' +import { createFileRoute, redirect } from '@tanstack/react-router' +import { IS_PLATFORM } from '@/lib/constants' + +// `/` is never rendered — it always redirects. Mirrors the Next.js +// `redirects()` rules in next.config.ts: platform sends users to `/org` +// (or `/new/new-project` when deep-linked with `?next=new-project`), +// self-hosted sends them straight to `/project/default`. +// +// Kept as a route (rather than a Nitro `routeRule`) so the redirect works +// in both `vite dev` and prod — `routeRules` only fire under Nitro — and +// so the query-param branch has somewhere to run. export const Route = createFileRoute('/')({ - component: RouteComponent, + validateSearch: (search: Record) => ({ + next: typeof search.next === 'string' ? search.next : undefined, + }), + beforeLoad: ({ search }) => { + // `href` instead of `to` because these targets aren't in the TanStack + // routeTree yet — they're still on the Next.js pages side during the + // migration. Swap to `to` once `/org`, `/new/new-project`, and + // `/project/default` are migrated. + if (IS_PLATFORM) { + if (search.next === 'new-project') throw redirect({ href: '/new/new-project' }) + throw redirect({ href: '/org' }) + } + throw redirect({ href: '/project/default' }) + }, }) - -function RouteComponent() { - return
Hello "/"!
-} diff --git a/apps/studio/vite.config.ts b/apps/studio/vite.config.ts index 7484d0388db..08bf6f1dd11 100644 --- a/apps/studio/vite.config.ts +++ b/apps/studio/vite.config.ts @@ -88,6 +88,16 @@ export default defineConfig(({ mode }) => { .map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)]) ) + // Mirror Next's `basePath` via NEXT_PUBLIC_BASE_PATH. Unlike Next, TanStack + // Start has no single knob — the prefix has to be declared in three places + // (see BASE_PATH_REDIRECT_GUIDE.md): + // - Vite `base` — bakes the prefix into asset URLs in the built bundle + // - Nitro `baseURL` — mounts the production server at that path + // - Router `basepath` — makes client-side navigation match the prefix + // The router layer is set in router.tsx off the same env var (inlined via + // `define` above). Leaving the var empty keeps the app at `/` as today. + const basePath = env.NEXT_PUBLIC_BASE_PATH || undefined + return { server: { port: 3000, @@ -95,6 +105,7 @@ export default defineConfig(({ mode }) => { resolve: { tsconfigPaths: true, }, + ...(basePath && { base: basePath }), define: publicEnvDefines, ssr: { // `lodash` is CJS; its named-export interop fails in Node ESM unless bundled. @@ -131,6 +142,11 @@ export default defineConfig(({ mode }) => { // `modules/index.js` via `package.json#exports`) out of the picture. exportConditions: ['module'], noExternals: ['tslib'], + // See the `basePath` comment above — Nitro's own mount point needs the + // same prefix as Vite/router. `routeRules` paths are matched AFTER + // baseURL is stripped, so if we add redirects later they should be + // written relative to the base. + ...(basePath && { baseURL: basePath }), }), viteReact(), ],