From 076e20d7e63a59279a7098e38552cc7fe2e068d2 Mon Sep 17 00:00:00 2001 From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Date: Tue, 30 Jun 2026 11:43:36 -0400 Subject: [PATCH] feat(ui): derive Vercel proxy backend from per-project env var (#75) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generate the /dashboard/api/* proxy destination at build time from LANGGRAPH_BACKEND_URL via the Vercel Build Output API, instead of hardcoding it in ui/vercel.json. This lets the dev and main branches stay byte-identical (required by the fast-forward-only prod promotion) while each Vercel project proxies to its own LangGraph backend purely via its own env var — so both the dev and prod dashboards can be git-linked. - ui/vercel.json: drop the hardcoded rewrites; buildCommand -> bun run vercel-build - ui/package.json: add vercel-build = vite build && node scripts/build-vercel-output.mjs - ui/scripts/build-vercel-output.mjs: emit .vercel/output/config.json with [ proxy /dashboard/api/* -> $LANGGRAPH_BACKEND_URL, filesystem, SPA fallback ]; throws if LANGGRAPH_BACKEND_URL is unset so a misconfig fails the build. Keeps the app same-origin (no CORS/auth change); preserves the _shell.html SPA fallback. Set LANGGRAPH_BACKEND_URL per Vercel project (All Environments). --- ui/package.json | 1 + ui/scripts/build-vercel-output.mjs | 47 ++++++++++++++++++++++++++++++ ui/vercel.json | 13 +-------- 3 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 ui/scripts/build-vercel-output.mjs diff --git a/ui/package.json b/ui/package.json index 1af93dc2..130f02be 100644 --- a/ui/package.json +++ b/ui/package.json @@ -5,6 +5,7 @@ "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", diff --git a/ui/scripts/build-vercel-output.mjs b/ui/scripts/build-vercel-output.mjs new file mode 100644 index 00000000..0bb2c1fb --- /dev/null +++ b/ui/scripts/build-vercel-output.mjs @@ -0,0 +1,47 @@ +// 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})`) diff --git a/ui/vercel.json b/ui/vercel.json index 7d11d9bc..46980c33 100644 --- a/ui/vercel.json +++ b/ui/vercel.json @@ -1,15 +1,4 @@ { "framework": null, - "buildCommand": "bun run build", - "outputDirectory": ".output/public", - "rewrites": [ - { - "source": "/dashboard/api/:path*", - "destination": "https://open-swe-v3-f2834ffc0df05a46a10262a9690e8490.us.langgraph.app/dashboard/api/:path*" - }, - { - "source": "/(.*)", - "destination": "/_shell.html" - } - ] + "buildCommand": "bun run vercel-build" }