Skip to content

feat: support serving WebUI under a configurable URL sub-path#113

Open
tittuvarghese wants to merge 2 commits into
Project-HAMi:mainfrom
tittuvarghese:feat/base-serving-path
Open

feat: support serving WebUI under a configurable URL sub-path#113
tittuvarghese wants to merge 2 commits into
Project-HAMi:mainfrom
tittuvarghese:feat/base-serving-path

Conversation

@tittuvarghese

@tittuvarghese tittuvarghese commented Jul 15, 2026

Copy link
Copy Markdown

What this does

Makes the HAMi WebUI serveable under an arbitrary URL sub-path behind a reverse
proxy (e.g. https://host/gpu-ui/) in addition to the site root (/), without
requiring a frontend image rebuild per deployment. The base path is configurable
at deploy time. The design follows Grafana's serve_from_sub_path and ArgoCD's
server.rootpath.

How it works

The base path is resolved at request time (never baked into the JS bundle),
from — in precedence order:

  1. the X-Forwarded-Prefix request header (set by a path-stripping proxy), then
  2. the HAMI_WEBUI_BASE_PATH environment variable, then
  3. the default / (root, unchanged historical behavior).

On serving index.html, the BFF injects <base href="{basePath}"> and a
window.__BASE_PATH__ global. The SPA reads that global for its router history
base, its axios baseURL, and its socket.io path.

Both proxy modes are supported:

  • path-stripping proxy that sets X-Forwarded-Prefix (leave the chart at its
    default), and
  • non-stripping ingress that passes the full prefixed path through (set the
    base path so the BFF strips its own prefix internally).

The Go API server is untouched — it is always reached via the /api/vgpu proxy
and remains path-agnostic.

Changes

BFF (NestJS, src/)

  • utils/base-path.ts (new): normalize (leading/trailing slash, sanitized),
    resolve (header > env), and inject the <base> tag + global into index.html.
  • main.ts: strip the configured prefix up-front so static assets, the /api
    proxy, and the SPA fallback keep matching root-relative paths; serve static
    with index/redirect disabled so requests fall through to the injecting
    controller; allow PORT override for local runs.
  • app.controller.ts: render index.html with the resolved base path (template
    cached).

Frontend (Vue, packages/web/)

  • index.html: drop the hard-coded <base href="/">; make favicon/entry refs
    relative.
  • utils/base-path.js (new, getBasePath) used for the router history base, the
    axios baseURL, and the socket.io path.

Helm chart (charts/hami-webui/)

  • New basePath value (default "/") wired to HAMI_WEBUI_BASE_PATH. Ingress
    default and base path both remain /, so existing installs are unaffected.

Docs

  • docs/installation/sub-path.md: configuration, both proxy modes, and a manual
    test plan. Chart README.md section + values row; root README link.

Testing

  • nest build, ESLint (BFF + frontend), and the full Jest suite (incl. new
    src/utils/base-path.spec.ts) pass.
  • Verified a real Vite build against the running BFF in all three modes — root
    /, non-stripping /gpu-ui/, and header-based stripping: index injection,
    hashless deep-link refresh, static assets, favicon, /api/vgpu proxy match,
    /health_check, and header-XSS sanitization all behave correctly.
  • helm template renders HAMI_WEBUI_BASE_PATH as / (default) and /gpu-ui/
    (override).

Root-serving behavior is unchanged; default installs are unaffected.

Summary by CodeRabbit

  • New Features

    • Added support for serving the WebUI under a configurable URL sub-path.
    • Added runtime base-path handling for page navigation, API requests, WebSocket connections, and static assets.
    • Added Helm configuration for setting the WebUI base path.
    • Added support for configurable server ports.
  • Documentation

    • Added setup guidance and reverse-proxy examples for sub-path deployments.
    • Expanded Helm chart configuration documentation.
  • Bug Fixes

    • Improved deep-link refresh behavior when the WebUI is hosted under a sub-path.
    • Added safeguards for sanitized base-path injection.
  • Tests

    • Added automated coverage for base-path normalization, resolution, and HTML injection.

Make the WebUI serveable under an arbitrary reverse-proxy prefix
(e.g. https://host/gpu-ui/) in addition to the site root, without
rebuilding the frontend image per deployment. Modelled on Grafana's
serve_from_sub_path and ArgoCD's server.rootpath.

The base path is resolved at request time from, in precedence order:
the X-Forwarded-Prefix header, the HAMI_WEBUI_BASE_PATH env var, then
default "/". This works for both a path-stripping proxy (sets the
header) and an ingress that passes the full prefixed path through.

BFF (NestJS):
- Add utils/base-path.ts: normalize (leading/trailing slash,
  sanitized), resolve (header > env), and inject <base href> +
  window.__BASE_PATH__ into index.html at send time.
- main.ts: strip the configured prefix up-front so static assets, the
  /api proxy, and the SPA fallback keep matching root-relative paths;
  serve static with index/redirect disabled so requests fall through
  to the injecting controller; allow PORT override.
- app.controller.ts: render index.html with the resolved base path.

Frontend (Vue):
- index.html: drop the hard-coded <base href="/">; make favicon and
  entry refs relative.
- Add utils/base-path.js (getBasePath) and use it for the router
  history base, the axios baseURL, and the socket.io path.

Helm chart:
- Add basePath value (default "/") wired to HAMI_WEBUI_BASE_PATH.

Docs:
- Add docs/installation/sub-path.md (config, both proxy modes, manual
  test plan); document the chart value and X-Forwarded-Prefix behavior.

Root-serving behavior is unchanged and default installs are unaffected.

Signed-off-by: tittu.varghese <tittu.varghese@npci.org.in>
@hami-robot

hami-robot Bot commented Jul 15, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: tittuvarghese

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@hami-robot

hami-robot Bot commented Jul 15, 2026

Copy link
Copy Markdown

Welcome @tittuvarghese! It looks like this is your first PR to Project-HAMi/HAMi-WebUI 🎉

@hami-robot hami-robot Bot added the size/L label Jul 15, 2026
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@tittuvarghese, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 66edc703-a90f-437a-ba87-9957559e7f95

📥 Commits

Reviewing files that changed from the base of the PR and between 24829c7 and d9b6f88.

📒 Files selected for processing (1)
  • src/app.controller.ts
📝 Walkthrough

Walkthrough

HAMi-WebUI now supports runtime URL base paths. The BFF resolves and injects the path, the frontend applies it to routing and network clients, and the Helm chart and documentation describe configuration and proxy modes.

Changes

Base-path serving

Layer / File(s) Summary
BFF base-path resolution and serving
src/utils/base-path.ts, src/app.controller.ts, src/main.ts, src/utils/base-path.spec.ts
Base paths are normalized, resolved from headers or environment, stripped from requests, injected into cached HTML, and covered by unit tests.
Frontend runtime path integration
packages/web/index.html, packages/web/src/utils/base-path.js, packages/web/src/router/index.js, packages/web/src/utils/request.js, packages/web/src/components/Socket/index.js
Runtime base paths are applied to relative assets, Vue Router history, Axios requests, and Socket.IO connections.
Helm configuration and installation guidance
charts/hami-webui/*, docs/installation/sub-path.md, README.md
Helm exposes basePath, deployment wiring sets HAMI_WEBUI_BASE_PATH, and documentation describes proxy modes, configuration, and test procedures.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant ReverseProxy
  participant FrontendBFF
  participant VueRouter
  participant Axios
  participant SocketIO
  Browser->>ReverseProxy: Request WebUI under a URL prefix
  ReverseProxy->>FrontendBFF: Forward request and optional X-Forwarded-Prefix
  FrontendBFF-->>Browser: HTML with resolved runtime base path
  Browser->>VueRouter: Initialize history with base path
  Browser->>Axios: Send API requests using base path
  Browser->>SocketIO: Connect using prefixed socket.io path
Loading

Possibly related PRs

Poem

I’m a bunny beneath /gpu-ui/,
Hopping through routes fresh and new.
HTML paths now point just right,
Sockets follow through the night.
Helm values guide the way—
Base paths bloom in code today!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: configurable sub-path serving for the WebUI.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app.controller.ts`:
- Around line 24-28: Update the index method to set the response Vary header to
X-Forwarded-Prefix before sending the dynamically rendered HTML, while
preserving the existing resolveBasePath and renderIndex flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a4fba224-70f1-4089-b083-5e793be1b42b

📥 Commits

Reviewing files that changed from the base of the PR and between c59f776 and 24829c7.

📒 Files selected for processing (14)
  • README.md
  • charts/hami-webui/README.md
  • charts/hami-webui/templates/deployment.yaml
  • charts/hami-webui/values.yaml
  • docs/installation/sub-path.md
  • packages/web/index.html
  • packages/web/src/components/Socket/index.js
  • packages/web/src/router/index.js
  • packages/web/src/utils/base-path.js
  • packages/web/src/utils/request.js
  • src/app.controller.ts
  • src/main.ts
  • src/utils/base-path.spec.ts
  • src/utils/base-path.ts

Comment thread src/app.controller.ts
The index.html body varies by the X-Forwarded-Prefix header (it drives
the injected <base href> and window.__BASE_PATH__). Without a matching
Vary header, a CDN or intermediate proxy could cache one prefix's
response and serve it to users on a different prefix — a cache-poisoning
vector that breaks routing / causes a denial of service. Advertise the
header so caches key responses per-prefix.

Signed-off-by: tittu.varghese <tittu.varghese@npci.org.in>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant