Skip to content

fix: detect router basename for path-prefixed deployments#465

Open
JohnGenri wants to merge 3 commits intositeboon:mainfrom
JohnGenri:fix/router-basename-prefix
Open

fix: detect router basename for path-prefixed deployments#465
JohnGenri wants to merge 3 commits intositeboon:mainfrom
JohnGenri:fix/router-basename-prefix

Conversation

@JohnGenri
Copy link

@JohnGenri JohnGenri commented Mar 1, 2026

Summary

Fix blank-page routing failures when CloudCLI UI is deployed behind a reverse proxy under a path prefix such as /ai.

Closes #464.

Problem

src/App.tsx currently uses window.__ROUTER_BASENAME__ || '', but that global is not initialized by the app.

As a result, path-prefixed deployments can successfully rewrite HTML, assets, and API calls, while React Router still assumes it is mounted at /.

That breaks routed views after login and direct links like:

  • /ai/session/:sessionId

Solution

Keep the explicit window.__ROUTER_BASENAME__ override when present, but otherwise derive the basename from runtime URLs that already reflect the deployed prefix:

  • manifest href
  • module script src
  • icon href

This makes prefixed deployments work without requiring an additional server-side bootstrap script.

Validation

Built locally with Vite after the change and verified that the generated app bundle was updated.

Summary by CodeRabbit

  • Bug Fixes
    • Routing now dynamically determines the app base path: it honors an explicit override when present, otherwise inspects page-provided hints (manifest, script and icon links) and ignores invalid values. If nothing reliable is found, an empty base is used. This improves routing reliability across varied hosting setups and prevents incorrect base-path resolution at runtime.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 1, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9b51b426-aa8a-451a-a84b-37883378f16a

📥 Commits

Reviewing files that changed from the base of the PR and between 111431c and 055c932.

📒 Files selected for processing (1)
  • src/App.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/App.tsx

📝 Walkthrough

Walkthrough

Adds a module-level helper detectRouterBasename() in src/App.tsx that derives the Router basename at runtime (honors window.__ROUTER_BASENAME__ override; otherwise inspects manifest, module script, and icon hrefs). App uses this result to set <Router basename={routerBasename}>.

Changes

Cohort / File(s) Summary
Router Basename Detection
src/App.tsx
Introduce detectRouterBasename() to compute Router basename by honoring window.__ROUTER_BASENAME__ or inferring from document hints (manifest href, module script src, favicon/apple-icon/mask-icon hrefs); replace `basename={window.ROUTER_BASENAME

Sequence Diagram(s)

sequenceDiagram
    participant App as App Component
    participant Doc as document (DOM)
    participant Window as window/global
    participant Router as React Router

    App->>Window: call detectRouterBasename()
    detectRouterBasename->>Window: check window.__ROUTER_BASENAME__
    alt override present
        detectRouterBasename-->>App: return trimmed override
    else override absent
        detectRouterBasename->>Doc: read manifest href, module script src, icon hrefs
        Doc-->>detectRouterBasename: list of candidate URLs
        detectRouterBasename->>detectRouterBasename: normalize, pick longest valid path
        detectRouterBasename-->>App: return inferred basename (or empty)
    end
    App->>Router: mount with basename (routerBasename)
    Router-->>App: routes mounted using computed basename
Loading

Poem

🐰 I sniff the manifest and icons with delight,
I trim a slash and set the path just right,
From script and link I find the way,
No more blank pages after the relay,
Hoppity hop — the Router beams bright!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding router basename detection for path-prefixed deployments, which is the core problem and solution.
Linked Issues check ✅ Passed The PR fully addresses #464 requirements: detects router basename from runtime hints, preserves explicit overrides, ignores cross-origin resources, and selects the longest normalized prefix.
Out of Scope Changes check ✅ Passed All changes are scoped to client-side router basename detection in src/App.tsx; no unrelated modifications or server-side changes are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/App.tsx`:
- Line 30: The URL resolution is using window.location.origin which mis-resolves
relative hints; update the code that sets pathname (the expression new
URL(candidate, window.location.origin).pathname) to resolve against
document.baseURI instead (new URL(candidate, document.baseURI).pathname) so
relative paths and deployed prefixes are preserved; modify the line that
computes pathname in App.tsx accordingly.
- Around line 22-34: The loop over candidatePaths currently returns on the first
successful match which can pick a shorter or empty basename; instead iterate all
candidatePaths, collect each normalized prefix from the regex match (use the
same regex and normalization: match[1] ? match[1].replace(/\/+$/, '') : ''),
skip empty prefixes when preferring non-empty, then after the loop return the
longest non-empty normalized prefix (or '' if none); update the code around
candidatePaths, pathname, match to compute and return that strongest detected
prefix rather than returning immediately on first match.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e22f42 and e1a7d8f.

📒 Files selected for processing (1)
  • src/App.tsx

@JohnGenri
Copy link
Author

Validation note from the environment where this was reproduced:

  • Deployment mode: reverse proxy under /ai
  • Before this patch: login/setup screen rendered, but the post-login routed UI could go blank and direct links like /ai/session/<id> failed
  • After this patch: the router picks up the effective prefix and the routed UI works correctly under /ai

So the fix is not theoretical; it resolves the concrete path-prefixed deployment failure that triggered issue #464.

@JohnGenri
Copy link
Author

Addressed both CodeRabbit findings in a follow-up commit:

  • switched URL resolution from window.location.origin to document.baseURI || window.location.href
  • scan all runtime path hints and keep the longest detected normalized prefix instead of returning the first match

This makes basename detection more robust for path-prefixed deployments where some hints may resolve to root while others preserve the actual prefix.

@JohnGenri JohnGenri closed this Mar 7, 2026
@JohnGenri JohnGenri force-pushed the fix/router-basename-prefix branch from f2b6c35 to 3950c0e Compare March 7, 2026 06:21
@JohnGenri JohnGenri reopened this Mar 7, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/App.tsx`:
- Around line 21-31: The current basename detection in App.tsx (iterating
candidatePaths and using pathname.match) only recognizes favicon.svg/png; update
the regex used in the match (the expression assigned to match in the loop) to
also accept favicon with any extension and other icon filenames so the prefix is
detected for favicon.ico or other icon names (e.g. change the part
`favicon\.(?:svg|png)$` to something like `favicon(?:\.[^\/]+)?$` or otherwise
include a broader `icon`/favicon filename pattern) so detectedBasename is
correctly set for more icon filenames.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6eb853f9-51b5-4f50-bd15-0885f9f4805c

📥 Commits

Reviewing files that changed from the base of the PR and between f2b6c35 and e63fa7d.

📒 Files selected for processing (1)
  • src/App.tsx

@JohnGenri
Copy link
Author

Validation note after refreshing this branch onto current main:

  • Issue #464 is still reproducible in a real path-prefixed deployment behind nginx under /ai.
  • The HTML shell, assets, API, and login flow work there, but the routed app view can go blank after login, and direct URLs like /ai/session/<id> fail because the client router still ends up with basename="".
  • This branch is now rebased onto the current upstream main and still fixes that specific routing failure by deriving the effective basename from runtime deployment hints when window.__ROUTER_BASENAME__ is not initialized.

So this is still an active bug, and the PR remains intentionally scoped to the client-side router basename detection only.

@JohnGenri
Copy link
Author

Addressed the remaining CodeRabbit edge case in 111431c.

  • broadened icon hint detection beyond favicon.svg|png to cover favicon.ico, apple-touch-icon, mask-icon, and other icon-style filenames
  • scan all relevant icon link tags instead of a single rel="icon"
  • normalize trailing asset-style directories so icon URLs like /ai/assets/icons/icon-192.png still resolve to basename /ai

The branch remains intentionally scoped to runtime basename detection for issue #464.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/App.tsx (1)

40-41: ⚠️ Potential issue | 🟠 Major

Ignore cross-origin asset hints before deriving the basename.

At Line 40, any absolute manifest/script/icon URL can influence the detected basename. If those assets are hosted on a CDN, this will infer the CDN path instead of the app’s path prefix and break routing on the current origin.

Proposed fix
-      const pathname = new URL(candidate.value, document.baseURI || window.location.href).pathname;
+      const candidateUrl = new URL(candidate.value, document.baseURI || window.location.href);
+      if (candidateUrl.origin !== window.location.origin) {
+        continue;
+      }
+      const pathname = candidateUrl.pathname;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/App.tsx` around lines 40 - 41, The code is deriving a basename from
candidate.value regardless of origin, causing CDN/absolute asset URLs to leak
into the app basename; before using new URL(candidate.value, document.baseURI ||
window.location.href).pathname, construct the URL and compare url.origin to
window.location.origin (or new URL(document.baseURI ||
window.location.href).origin), and only use url.pathname (then normalize via
pathname.replace(/\/+$/, '')) if the origins match; if they differ, skip this
candidate (or fall back to using window.location.pathname) so CDN/third‑party
absolute URLs do not influence basename detection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/App.tsx`:
- Around line 54-57: The current cleanup loop that strips trailing folder names
from match[1] (using segments = match[1].split('/').filter(Boolean) and the
while loop checking segments.pop()) can remove the only segment (e.g., "/icons")
and collapse the prefix to root; change the loop condition to avoid popping when
there's only one segment left (e.g., while (segments.length > 1 &&
['assets','static','icons','images'].includes(segments[segments.length - 1])))
so the basename is preserved for single-segment mounts; update any logic that
builds the final prefix from segments to rely on this adjusted condition.

---

Duplicate comments:
In `@src/App.tsx`:
- Around line 40-41: The code is deriving a basename from candidate.value
regardless of origin, causing CDN/absolute asset URLs to leak into the app
basename; before using new URL(candidate.value, document.baseURI ||
window.location.href).pathname, construct the URL and compare url.origin to
window.location.origin (or new URL(document.baseURI ||
window.location.href).origin), and only use url.pathname (then normalize via
pathname.replace(/\/+$/, '')) if the origins match; if they differ, skip this
candidate (or fall back to using window.location.pathname) so CDN/third‑party
absolute URLs do not influence basename detection.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 67000551-4e71-46f0-b6e8-e1cffbcfdf1a

📥 Commits

Reviewing files that changed from the base of the PR and between e63fa7d and 111431c.

📒 Files selected for processing (1)
  • src/App.tsx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Blank page in path-prefixed deployments because Router basename is never initialized

1 participant