mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 05:43:14 +00:00
fix(ui): drive Vercel dashboard-API proxy through Nitro routeRules (#76)
The Vercel deploy 404'd at `/` because PR #75's `vercel-build` script (`scripts/build-vercel-output.mjs`) ran `rm -rf .vercel/output` and rebuilt it from `.output/public`. On Vercel CI, Nitro's Vercel preset auto-activates (from the VERCEL env var) and emits the Build Output API layout to `.vercel/output` itself during `vite build`; the script then clobbered that correct output with a static-only config that could not resolve the SPA `_shell.html` fallback or the server functions, so production returned `404: NOT_FOUND` even though the build was READY. Stop fighting Nitro and drive the proxy through it instead: - Remove `scripts/build-vercel-output.mjs` and the `vercel-build` script; restore the plain `vite build` for both local and Vercel builds. - Add an env-driven Nitro `routeRules` proxy in `vite.config.ts`. Nitro's Vercel preset compiles a plain external-URL `proxy` rule into a CDN-level rewrite in `.vercel/output/config.json` at build time, reading LANGGRAPH_BACKEND_URL (the per-project Vercel env var). Proxy, not redirect, so the osw_session cookie stays first-party (same-origin). The build fails loudly if LANGGRAPH_BACKEND_URL is missing on Vercel; the rule is omitted for plain local/off-Vercel builds (which use the E2E_HARNESS mock proxy). - `vercel.json`: `buildCommand` back to `bun run build`; drop the `.output/public` outputDirectory so Vercel serves Nitro's `.vercel/output`. Validated with `LANGGRAPH_BACKEND_URL=... NITRO_PRESET=vercel bun run build`: the generated `.vercel/output/config.json` contains the `/dashboard/api/(.*)` -> backend rewrite ahead of `handle: filesystem` and the SPA catch-all, `_shell.html` and the 699 hashed assets are emitted, and typecheck passes. Supersedes the broken approach in #75.
This commit is contained in:
parent
076e20d7e6
commit
c75e76c05e
4 changed files with 34 additions and 50 deletions
|
|
@ -5,7 +5,6 @@
|
|||
"scripts": {
|
||||
"dev": "vite dev --port 3000",
|
||||
"build": "vite build",
|
||||
"vercel-build": "vite build && node scripts/build-vercel-output.mjs",
|
||||
"preview": "vite preview",
|
||||
"test": "vitest run",
|
||||
"lint": "eslint",
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
// Repackage the static Vite/TanStack-Start build (.output/public) into the Vercel
|
||||
// Build Output API layout (.vercel/output), generating the /dashboard/api/* proxy
|
||||
// destination from a per-project env var.
|
||||
//
|
||||
// Why this exists: prod deploys via a fast-forward-only promotion, so the `dev` and
|
||||
// `main` branches are byte-identical and a committed vercel.json CANNOT differ between
|
||||
// them. The app is same-origin only (no backend CORS), so the per-environment backend
|
||||
// must be the PROXY DESTINATION, supplied per Vercel project as LANGGRAPH_BACKEND_URL.
|
||||
// Vercel reads vercel.json BEFORE the build and never interpolates env into it, so the
|
||||
// routes are emitted here (read AFTER the build) where process.env is available.
|
||||
import { cp, mkdir, writeFile, rm } from "node:fs/promises"
|
||||
|
||||
const backend = process.env.LANGGRAPH_BACKEND_URL
|
||||
if (!backend) {
|
||||
throw new Error(
|
||||
"LANGGRAPH_BACKEND_URL is required at build time (set it per Vercel project: " +
|
||||
"dev project -> dev langgraph URL, prod project -> prod langgraph URL).",
|
||||
)
|
||||
}
|
||||
const base = backend.replace(/\/$/, "")
|
||||
|
||||
await rm(".vercel/output", { recursive: true, force: true })
|
||||
await mkdir(".vercel/output/static", { recursive: true })
|
||||
await cp(".output/public", ".vercel/output/static", { recursive: true })
|
||||
|
||||
await writeFile(
|
||||
".vercel/output/config.json",
|
||||
JSON.stringify(
|
||||
{
|
||||
version: 3,
|
||||
routes: [
|
||||
// Same-origin proxy to this project's backend (proxy, not redirect, so the
|
||||
// osw_session cookie stays first-party on the Vercel origin).
|
||||
{ src: "^/dashboard/api/(.*)$", dest: `${base}/dashboard/api/$1` },
|
||||
// Serve hashed assets, the PWA manifest/service worker, and _shell.html itself
|
||||
// BEFORE the SPA catch-all, or static files get swallowed by the fallback.
|
||||
{ handle: "filesystem" },
|
||||
// SPA fallback (replaces the old vercel.json /(.*) -> /_shell.html rewrite).
|
||||
{ src: "^/.*$", dest: "/_shell.html" },
|
||||
],
|
||||
},
|
||||
null,
|
||||
2,
|
||||
) + "\n",
|
||||
)
|
||||
|
||||
console.log(`[build-vercel-output] wrote .vercel/output/config.json (backend: ${base})`)
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"$schema": "https://openapi.vercel.sh/vercel.json",
|
||||
"framework": null,
|
||||
"buildCommand": "bun run vercel-build"
|
||||
"buildCommand": "bun run build"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,13 +74,44 @@ function mockHarnessProxy(): Plugin | null {
|
|||
}
|
||||
}
|
||||
|
||||
// Same-origin proxy for the dashboard API. Vercel reads vercel.json BEFORE the build
|
||||
// and never interpolates env into it, and prod promotes by fast-forward (so `dev` and
|
||||
// `main` ship a byte-identical vercel.json that cannot encode a per-project backend).
|
||||
// So the per-project backend is driven through Nitro instead: this `routeRules` proxy
|
||||
// is compiled by Nitro's Vercel preset into a CDN-level rewrite in
|
||||
// `.vercel/output/config.json` at build time, reading LANGGRAPH_BACKEND_URL from the
|
||||
// per-project Vercel env var. Proxy (not redirect) keeps the osw_session cookie
|
||||
// first-party on the Vercel origin. The `**` wildcard maps 1:1 to the destination.
|
||||
function backendProxyRouteRules():
|
||||
| Record<string, { proxy: string }>
|
||||
| undefined {
|
||||
const backend = process.env.LANGGRAPH_BACKEND_URL?.replace(/\/$/, "")
|
||||
// Nitro's Vercel preset auto-activates from the VERCEL env var; NITRO_PRESET=vercel
|
||||
// forces it locally. On Vercel the backend is mandatory — fail loudly rather than
|
||||
// ship a build whose API calls 404, which is exactly how the previous approach broke.
|
||||
const onVercel =
|
||||
!!process.env.VERCEL || process.env.NITRO_PRESET === "vercel"
|
||||
if (!backend) {
|
||||
if (onVercel) {
|
||||
throw new Error(
|
||||
"LANGGRAPH_BACKEND_URL is required for Vercel builds (set it per Vercel " +
|
||||
"project: dev project -> dev langgraph URL, prod project -> prod langgraph URL).",
|
||||
)
|
||||
}
|
||||
// Off Vercel with no backend configured (plain local `vite build`, tests): omit the
|
||||
// proxy. Local dev gets its API via the E2E_HARNESS mock proxy above instead.
|
||||
return undefined
|
||||
}
|
||||
return { "/dashboard/api/**": { proxy: `${backend}/dashboard/api/**` } }
|
||||
}
|
||||
|
||||
const config = defineConfig({
|
||||
optimizeDeps: { include: ["workbox-window"] },
|
||||
worker: { format: "es" },
|
||||
plugins: [
|
||||
mockHarnessProxy(),
|
||||
devtools(),
|
||||
nitro(),
|
||||
nitro({ routeRules: backendProxyRouteRules() }),
|
||||
viteTsConfigPaths({
|
||||
projects: ["./tsconfig.json"],
|
||||
}),
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue