Skip to content

Conversation

@yingca1
Copy link

@yingca1 yingca1 commented Nov 22, 2025

  • Introduced APP_BASE_PATH in .env.example for deployment path configuration.
  • Updated server/index.js to handle dynamic base path for routes, WebSocket connections, and static assets.
  • Modified React components to utilize BASE_URL for asset paths and router basename.
  • Enhanced WebSocket connection handling to respect the base path.
  • Adjusted API utility functions to prepend BASE_URL to relative paths.

Summary by CodeRabbit

  • New Features

    • App and client now support deployment under a custom base path so routes, assets, WebSocket connections, and injected runtime values work correctly when hosted in a sub-directory.
  • Bug Fixes

    • Improved connection handling to avoid attempting WebSocket connections when required credentials are missing and to ensure asset/HTML responses resolve correctly under a base path.
  • Chores

    • Added a deployment configuration variable in the environment to specify the application base path.

✏️ Tip: You can customize this high-level summary in your review settings.

- Introduced APP_BASE_PATH in .env.example for deployment path configuration.
- Updated server/index.js to handle dynamic base path for routes, WebSocket connections, and static assets.
- Modified React components to utilize BASE_URL for asset paths and router basename.
- Enhanced WebSocket connection handling to respect the base path.
- Adjusted API utility functions to prepend BASE_URL to relative paths.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 22, 2025

Walkthrough

Adds configurable APP_BASE_PATH support: server mounts routes under a router at that base path and injects it into served HTML/manifest; client reads window.APP_BASE_PATH (BASE_URL/ROUTER_BASENAME) to prefix API calls, WebSocket URLs, router basename, and asset imports.

Changes

Cohort / File(s) Change Summary
Environment Configuration
\.env.example
Adds a DEPLOYMENT CONFIGURATION section documenting APP_BASE_PATH and usage guidance for root vs sub-directory deployments.
Server base-path routing & HTML injection
server/index.js
Introduces APP_BASE_PATH, refactors to use an expressApp and a mounted router at the base path, updates WebSocket setup to be base-path-aware, rewrites served manifest.json/*.html/dist/index.html to inject APP_BASE_PATH and adjust asset/script URLs, and adjusts caching headers for HTML vs static assets.
Client base discovery & fetch/WebSocket wiring
src/utils/api.js, src/utils/websocket.js
Adds BASE_URL and ROUTER_BASENAME (from window.__APP_BASE_PATH__) exports; authenticatedFetch now merges headers, omits Content-Type for FormData, and prepends BASE_URL to relative URLs. WebSocket URL construction now uses BASE_URL and avoids connecting when required token is missing.
Router basename usage
src/App.jsx
Imports ROUTER_BASENAME and sets Router basename={ROUTER_BASENAME} to enable client routing under the configured base path.
Asset path updates in components
src/components/ClaudeLogo.jsx, src/components/CursorLogo.jsx
Replace hardcoded icon paths with ${BASE_URL}/icons/... by importing BASE_URL from src/utils/api.js; remove explicit React import (JSX runtime).

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant Server
    participant expressApp as expressApp
    participant router as Router (mounted)
    participant Injector as HTML/Manifest Injector

    Browser->>Server: GET /myapp/ (or /myapp/index.html)
    Server->>expressApp: Accept request
    expressApp->>router: Forward to router mounted at APP_BASE_PATH (/myapp)
    router->>Injector: Request index/manifest
    Injector->>Injector: Rewrite asset URLs, inject window.__APP_BASE_PATH__ = '/myapp'
    Injector-->>Browser: Serve HTML/manifest with injected base path
    Browser->>Browser: Initialize React app, read window.__APP_BASE_PATH__
    Browser->>Server: API request to `${BASE_URL}/user` (via authenticatedFetch)
Loading
sequenceDiagram
    participant App as React App
    participant API as api.js
    participant WS as websocket.js
    participant Server as Backend (HTTP/WS)

    App->>API: Read ROUTER_BASENAME / BASE_URL
    App->>App: Router basename={ROUTER_BASENAME}
    App->>API: authenticatedFetch('/status')
    API->>API: Prepend BASE_URL -> `${BASE_URL}/status`
    API->>Server: HTTP request to `${BASE_URL}/status`
    App->>WS: useWebSocket()
    WS->>Server: Connect to `${BASE_URL}/ws` (with token if required)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay special attention to server/index.js (router mounting, HTML/manifest rewriting, WebSocket path handling, caching headers).
  • Review src/utils/api.js header merge and relative-URL detection to ensure no double-prefixing or incorrect absolute-URL handling.
  • Confirm window.__APP_BASE_PATH__ injection timing vs client initialization to avoid race conditions.

Possibly related PRs

  • Feature/edit diff #216 — Modifies src/utils/websocket.js and server/index.js; likely related changes to WebSocket URL handling and base-path wiring.

Poem

🐰
I hopped along the filesystem trail,
tucked base paths under my tail,
injected whispers in the page,
so apps can roam from stage to stage.
A little hop, a stable path — deploy without fail!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 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 support for sub-directory deployments with configurable base paths across server configuration, routing, WebSocket handling, and client-side asset resolution.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 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: 0

🧹 Nitpick comments (4)
server/index.js (3)

166-178: Base-path aware app/router setup is solid; consider central base-path normalization

Splitting expressApp from the router and mounting the router at APP_BASE_PATH with shared middleware on expressApp is a good way to support sub-directory deployments. Using expressApp.locals.wss is also the correct choice so req.app.locals.wss continues to work from within routes.

You compute a trimmed base path (APP_BASE_PATH.endsWith('/') ? ...) in several places (manifest, HTML, WebSocket, SPA index). To avoid drift and edge cases (e.g. values without a leading slash, or accidental //), consider a small helper like:

function normalizeBasePath(raw = '/') {
  if (!raw) return '';
  let p = raw.trim();
  if (!p.startsWith('/')) p = `/${p}`;
  if (p !== '/' && p.endsWith('/')) p = p.slice(0, -1);
  return p === '/' ? '' : p;
}

and then reuse const basePath = normalizeBasePath(APP_BASE_PATH); everywhere, including the expressApp.use(APP_BASE_PATH, router) call (which could then use the normalized form).

Also applies to: 221-225, 1531-1533


274-292: HTML/manifest base-path injection works; tighten script injection and index.html handling

The manifest and HTML rewriting plus the SPA catch-all give you consistent base-path behavior in production. A couple of small robustness tweaks:

  1. Escape APP_BASE_PATH safely in the injected script

Instead of interpolating directly into JavaScript:

const configScript = `<script>window.__APP_BASE_PATH__='${basePath}';</script>`;

prefer a JSON-safe form:

const configScript = `<script>window.__APP_BASE_PATH__=${JSON.stringify(basePath)};</script>`;

This avoids breakage if APP_BASE_PATH ever contains quotes or other special characters.

  1. Ensure /index.html also benefits from injection

Right now the SPA handler is on app.get('*') and reads ../dist/index.html, but /index.html under the base path can still be served directly by express.static('../dist') without your rewrites. If users or tooling hit /your-base/index.html directly, asset URLs may not be base-prefixed.

You can avoid that by, for example:

  • Adding an explicit app.get('/index.html', ...) handler (inside the router) that delegates to the same SPA logic, before the express.static('../dist', ...) middleware; or
  • Setting index: false on the public static as well and relying solely on the SPA handler for all HTML.

Also applies to: 295-319, 1488-1528


754-762: WebSocket path routing matches APP_BASE_PATH; optionally normalize trailing slashes

Deriving shellPath and chatPath from APP_BASE_PATH so they become /<base>/shell and /<base>/ws is consistent with the rest of the base-path design and should work as long as the reverse proxy preserves the prefix.

As a small hardening improvement, you could normalize pathname to strip trailing slashes before comparison so clients connecting to /ws/ or /shell/ still work:

const urlObj = new URL(url, 'http://localhost');
let pathname = urlObj.pathname.replace(/\/+$/, '') || '/';

and then compare against normalized shellPath / chatPath.

.env.example (1)

43-51: APP_BASE_PATH documentation is clear; consider clarifying normalization rules

The deployment section makes it obvious how to set APP_BASE_PATH for root vs sub-directory installs. To reduce misconfiguration, you might add a brief note that:

  • The value should start with / (e.g. /claudeui, not claudeui), and
  • A trailing slash is optional and normalized internally.

This would align expectations with the server’s base-path handling.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a72a26 and 407d0af.

📒 Files selected for processing (7)
  • .env.example (1 hunks)
  • server/index.js (5 hunks)
  • src/App.jsx (2 hunks)
  • src/components/ClaudeLogo.jsx (1 hunks)
  • src/components/CursorLogo.jsx (1 hunks)
  • src/utils/api.js (4 hunks)
  • src/utils/websocket.js (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
src/App.jsx (1)
src/utils/api.js (2)
  • ROUTER_BASENAME (7-7)
  • ROUTER_BASENAME (7-7)
src/utils/api.js (3)
server/index.js (3)
  • token (203-204)
  • url (202-202)
  • url (747-747)
src/contexts/AuthContext.jsx (1)
  • token (27-27)
server/middleware/auth.js (1)
  • token (40-40)
src/utils/websocket.js (2)
src/utils/api.js (3)
  • BASE_URL (5-5)
  • BASE_URL (5-5)
  • token (11-11)
server/index.js (1)
  • token (203-204)
src/components/ClaudeLogo.jsx (1)
src/utils/api.js (2)
  • BASE_URL (5-5)
  • BASE_URL (5-5)
src/components/CursorLogo.jsx (1)
src/utils/api.js (2)
  • BASE_URL (5-5)
  • BASE_URL (5-5)
server/index.js (2)
server/routes/user.js (1)
  • router (9-9)
server/routes/agent.js (1)
  • router (13-13)
🔇 Additional comments (11)
src/App.jsx (1)

39-39: Router basename wiring correctly ties client routing to the base path

Using ROUTER_BASENAME for the BrowserRouter basename, derived from window.__APP_BASE_PATH__, is the right way to keep client-side URLs aligned with APP_BASE_PATH while still allowing simple navigate('/session/:id') calls.

Since ROUTER_BASENAME is BASE_URL || '/', please verify:

  • Deep links to /session/:id work both when deploying at root and under a sub-path.
  • In development (no __APP_BASE_PATH__ injection), basename="/" behaves as expected with your existing routes.

Also applies to: 955-960

src/components/ClaudeLogo.jsx (1)

1-5: ClaudeLogo now respects BASE_URL for sub-path deployments

Using ${BASE_URL}/icons/claude-ai-icon.svg ensures the icon resolves correctly both at the root and under a configured base path, without changing the component’s public API.

src/components/CursorLogo.jsx (1)

1-5: CursorLogo BASE_URL usage is consistent and base-path aware

Mirroring ClaudeLogo, building the src as ${BASE_URL}/icons/cursor.svg keeps the cursor icon working under both root and sub-directory deployments without altering the component’s interface.

src/utils/websocket.js (3)

2-2: LGTM: BASE_URL import is correct.

The import properly retrieves the BASE_URL constant for constructing base-path-aware WebSocket URLs.


30-34: LGTM: Platform mode WebSocket URL correctly incorporates BASE_URL.

The WebSocket URL construction properly prepends the base path while maintaining protocol detection and proxy compatibility.


36-44: LGTM: OSS mode WebSocket connection properly handles BASE_URL and authentication.

The implementation correctly:

  • Guards against missing tokens with early return
  • Incorporates BASE_URL into the WebSocket path
  • URL-encodes the token parameter
  • Maintains consistency with server-side token extraction logic
src/utils/api.js (5)

3-7: LGTM: BASE_URL and ROUTER_BASENAME exports are well-implemented.

The implementation correctly:

  • Reads the server-injected base path from window global
  • Provides appropriate fallbacks for root path deployment
  • Exports both variants for different use cases (API calls vs routing)

17-20: LGTM: FormData Content-Type handling is correct.

Properly removes the Content-Type header for FormData requests to allow the browser to set the correct multipart boundary automatically.


22-29: LGTM: Header merging and Authorization logic is well-structured.

The implementation correctly:

  • Merges default headers with provided headers
  • Adds Authorization header after merge, ensuring token-based auth takes precedence
  • Only applies Authorization in OSS mode when token exists

37-40: LGTM: Fetch call correctly uses computed URL and headers.

The fetch invocation properly applies the base-path-aware URL and merged headers object.


31-35: BASE_URL trailing slash concern is already addressed.

The server-side code normalizes APP_BASE_PATH before injecting it as window.__APP_BASE_PATH__ (server/index.js:1504–1509). The normalization removes any trailing slash, ensuring that BASE_URL on the client never contains a trailing slash. Therefore, the URL construction in lines 31–35 is safe and will not produce double slashes. The suggested client-side normalization in the review comment is unnecessary.

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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/components/Shell.jsx (1)

61-74: skipAuth is undefined here and will break non‑platform WebSocket connections

In the non‑platform branch, this line:

const queryParams = (token && !skipAuth) ? `?token=${encodeURIComponent(token)}` : '';

references skipAuth, which is not defined anywhere in this file or in the surrounding scope. At runtime this will throw a ReferenceError the first time the non‑platform path is executed, preventing the shell from connecting.

Unless skipAuth is intentionally provided as a global elsewhere (which is brittle), this should either be removed from the condition or properly sourced (e.g., from props/env or an import).

If the intent is simply “append token if present”, a minimal safe fix is:

-        const queryParams = (token && !skipAuth) ? `?token=${encodeURIComponent(token)}` : '';
-        wsUrl = `${protocol}//${window.location.host}${BASE_URL}/shell${queryParams}`;
+        const queryParams = token ? `?token=${encodeURIComponent(token)}` : '';
+        wsUrl = `${protocol}//${window.location.host}${BASE_URL}/shell${queryParams}`;

This keeps behavior compatible with the server’s token handling (token from query params or Authorization header) while avoiding the undefined variable. Based on relevant code snippets, this matches how the server currently retrieves the token.

🧹 Nitpick comments (2)
src/components/Shell.jsx (2)

63-63: Using BASE_URL in the platform WebSocket URL fits the base‑path design

The platform branch now builds:

wsUrl = `${protocol}//${window.location.host}${BASE_URL}/shell`;

which makes the shell endpoint respect the configured base path, consistent with the rest of the app’s routing. This aligns with the PR goal of sub‑directory deployments.

One small future‑proofing idea (optional): if APP_BASE_PATH can be configured with or without a trailing slash, consider normalizing BASE_URL once in src/utils/api.js to avoid accidental double slashes in URLs.


302-302: Silent clipboard error handling is acceptable but could log in dev

Swallowing errors from navigator.clipboard.readText() is reasonable to avoid noisy UX when permissions are denied, but the completely empty catch makes debugging harder if something else goes wrong.

Optionally, you could log only in development, e.g.:

-        }).catch(() => { });
+        }).catch((err) => {
+          if (import.meta.env.DEV) {
+            console.warn('[Shell] Clipboard paste failed:', err);
+          }
+        });

This keeps production quiet while still giving you visibility during development.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 407d0af and bf251a6.

📒 Files selected for processing (1)
  • src/components/Shell.jsx (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/components/Shell.jsx (4)
src/utils/api.js (3)
  • BASE_URL (5-5)
  • BASE_URL (5-5)
  • token (11-11)
server/index.js (1)
  • token (203-204)
src/contexts/AuthContext.jsx (1)
  • token (27-27)
server/middleware/auth.js (1)
  • token (40-40)
🔇 Additional comments (1)
src/components/Shell.jsx (1)

8-8: BASE_URL import for WebSocket path looks appropriate

Importing BASE_URL here keeps the shell WebSocket endpoint aligned with the app’s configured base path; this is consistent with how BASE_URL is exposed from src/utils/api.js. No changes needed.

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.

1 participant