feat: add configurable base path support for deploying under a sub-path#5
feat: add configurable base path support for deploying under a sub-path#5agrogov wants to merge 1 commit into
Conversation
Adds support for deploying the Vexa Dashboard under a configurable sub-path (e.g. `/vexa`) via the `NEXT_PUBLIC_BASE_PATH` environment variable. This is required for reverse-proxy setups where the app is served at a non-root URL. The implementation introduces a shared `base-path.ts` utility library with `withBasePath()` and `stripBasePath()` helpers, then updates all API fetch calls, static asset references, and client-side route matching across the entire application to be base-path aware. The Next.js config is updated to set `basePath` and `assetPrefix` conditionally, and the Dockerfile accepts the base path as a build argument.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 290ff6e250
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const isDirectMode = healthStatus?.authMode === "direct"; | ||
| const isGoogleAuthEnabled = healthStatus?.checks.googleOAuth.configured === true; | ||
| const isEmailAuthEnabled = !isGoogleAuthEnabled && (healthStatus?.authMode === "magic-link" || healthStatus?.authMode === "direct"); | ||
| const isAzureAdAuthEnabled = healthStatus?.checks.azureAdOAuth.configured === true; |
There was a problem hiding this comment.
Guard Azure health checks before reading configured flag
The login page now dereferences healthStatus?.checks.azureAdOAuth.configured, but /api/health still returns a checks object without azureAdOAuth (it only includes smtp, googleOAuth, adminApi, and vexaApi). In the normal success path this throws Cannot read properties of undefined during render, crashing the login screen and blocking all sign-in flows. Either make this access optional (azureAdOAuth?.configured) or update the health endpoint payload to always include that field.
Useful? React with 👍 / 👎.
| @@ -0,0 +1,23 @@ | |||
| const rawBasePath = process.env.NEXT_PUBLIC_BASE_PATH || ""; | |||
| const normalizedBasePath = rawBasePath.replace(/\/$/, ""); | |||
There was a problem hiding this comment.
Normalize base path before composing client URLs
base-path.ts only removes one trailing slash and never trims or enforces a leading slash, unlike next.config.ts which does. If NEXT_PUBLIC_BASE_PATH is provided as vexa (or with surrounding whitespace), withBasePath() produces relative URLs like vexa/api/..., so API calls and asset links resolve against the current route instead of the app root. This breaks sub-path deployments for non-canonical env values; reuse the same normalization rules as next.config.ts.
Useful? React with 👍 / 👎.
|
Hey! This repo has been archived — the dashboard has moved to the monorepo at https://github.com/Vexa-ai/vexa under We're pulling your changes into the monorepo now to preserve your commits. Future PRs should be submitted to https://github.com/Vexa-ai/vexa. Thanks for the contribution! |
Adds support for deploying the Vexa Dashboard under a configurable sub-path (e.g.
/vexa) via theNEXT_PUBLIC_BASE_PATHenvironment variable. This is required for reverse-proxy setups where the app is served at a non-root URL.The implementation introduces a shared
base-path.tsutility library withwithBasePath()andstripBasePath()helpers, then updates all API fetch calls, static asset references, and client-side route matching across the entire application to be base-path aware. The Next.js config is updated to setbasePathandassetPrefixconditionally, and the Dockerfile accepts the base path as a build argument.Changes
Core Infrastructure
src/lib/base-path.ts(NEW)NEXT_PUBLIC_BASE_PATHenv var and normalizes it (strips trailing slash)basePath— the raw normalized base path stringwithBasePath(path)— prepends the base path to a given route or asset pathstripBasePath(path)— removes the base path prefix from a pathname (for route matching)next.config.tsnormalizeBasePath()helper to sanitize the env var (trim, ensure leading slash)basePathandassetPrefixin the Next.js config whenNEXT_PUBLIC_BASE_PATHis setDockerfileNEXT_PUBLIC_BASE_PATHas a build-timeARGin the builder stageENVso it's available duringnpm run buildbuild_image.sh(NEW)--build-arg NEXT_PUBLIC_BASE_PATH=/vexato the build commandAPI Fetch Calls —
withBasePath()Appliedsrc/lib/api.ts/api/vexa/...) withwithBasePath()src/lib/admin-api.ts/api/vexa/...admin endpoints) withwithBasePath()src/stores/auth-store.ts/api/auth/send-magic-link,/api/auth/login,/api/auth/logout,/api/auth/me,/api/auth/oauth-callbacksrc/stores/admin-auth-store.ts/api/auth/admin-verify,/api/auth/admin-logoutsrc/hooks/use-runtime-config.ts/api/configfetchsrc/hooks/use-live-transcripts.ts/api/configfetchsrc/hooks/use-vexa-websocket.ts/api/configfetchsrc/app/settings/page.tsx/api/configand/api/ai/configfetchessrc/app/auth/verify/page.tsx/api/auth/verifyfetchsrc/app/login/page.tsx/api/healthfetch, CSRF token fetch, and NextAuth sign-in POST pathsnext-auth/reactsignIn()with a manualsignInWithProvider()that uses base-path-aware URLssrc/components/admin/admin-guard.tsx/api/auth/admin-verifyfetchsrc/components/ai/ai-chat-panel.tsx/api/ai/configfetch and/api/ai/chatstreaming endpointsrc/components/mcp/mcp-config-button.tsx/api/configfetch and MCP icon asset pathStatic Asset Paths —
withBasePath()Appliedsrc/app/layout.tsx/icons/vexadark.svg)src/app/page.tsxsrc/app/meetings/[id]/page.tsxbasePathexport for building shareable URLs:${window.location.origin}${basePath || ""}src/components/ui/logo.tsx/icons/vexalight.svg,/icons/vexadark.svg)src/components/meetings/meeting-card.tsxsrc/components/transcript/transcript-viewer.tsxbasePathexport for building shareable transcript URLsRoute Matching —
stripBasePath()Appliedsrc/components/auth/auth-provider.tsxpathnamebefore checking against protected/public route patternssrc/components/layout/app-layout.tsxpathnamebefore highlighting active navigation itemsNextAuth Base-Path Awareness
src/app/api/auth/[...nextauth]/route.tsgetAppBasePath()to extract the app base path fromNEXTAUTH_URLbuildAppPath()to construct base-path-prefixed routespages.signInandpages.errorto usebuildAppPath("/login")so NextAuth redirects to the correct URL under a sub-pathOther
src/stores/meetings-store.tswithBasePath()where applicableEnvironment Variables
NEXT_PUBLIC_BASE_PATH/vexa). Leave empty for root deployment. Passed as a build-time arg for Docker and available at runtime via Next.js public env.Test Plan
/) whenNEXT_PUBLIC_BASE_PATHis unset (no regression)NEXT_PUBLIC_BASE_PATH=/vexaand verify the app loads at/vexa/vexa/loginnot/login)--build-arg NEXT_PUBLIC_BASE_PATH=/vexaand verify it worksbuild_image.shproduces a working image