Skip to content

Serve the web client with nginx and proxy the API - #22

Merged
hyperloop-cornell merged 1 commit into
feat/ci-built-web-clientfrom
feat/nginx-frontend
Oct 1, 2026
Merged

hyperloop-cornell merged 1 commit into
feat/ci-built-web-clientfrom
feat/nginx-frontend

Conversation

@WesP10

@WesP10 WesP10 commented Oct 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • nginx replaces vite preview on port 4173. It serves the CI-built files from /var/www/hyperloop-gui and proxies /auth, /api, /ws, /hub and /health to cloud-services at 127.0.0.1:8080 (see deploy/nginx/hyperloop-gui.conf).
  • The browser now talks to a single origin. CORS no longer applies to the GUI, and port 8080 no longer has to be open to the internet. This fixes the ERR_CONNECTION_TIMED_OUT on view-only login at 98.93.47.19:4173.
  • The bundle is built with an empty API URL, so it calls the page origin, unless WEB_API_BASE_URL is set. This also works when gui.cornellhyperloop.com is proxied to :4173.
  • About 90 MB of RAM is freed on the instance (the Node preview process). EC2 no longer needs the web-client checkout or its node_modules.
  • Hashed /assets/* files are cached for a year, and index.html is revalidated on every load. gzip is on. Request bodies can be up to 25 MB, because the flash request carries the firmware base64-encoded.

What the first deploy does on EC2

  1. Installs nginx if it is missing. Only in that case, it also removes the stock site on :80.
  2. Unpacks the build into /var/www/hyperloop-gui, installs the site config and runs nginx -t.
  3. Disables and stops the old frontend (vite preview) service, then starts or reloads nginx. Expect a second or two of downtime on this first deploy only.

These are left in place and can be deleted by hand later: /home/ubuntu/web-client and /etc/systemd/system/frontend.service.

Notes

  • Hubs are unchanged. They can keep using port 8080 or api.cornellhyperloop.com, or connect through nginx at ws://<host>:4173/hub.
  • Client IPs: uvicorn trusts X-Forwarded-For from 127.0.0.1 by default, so the backend still sees each browser's real IP and the per-IP login limits keep working. Leave TRUST_PROXY_HEADERS=false.
  • After this deploys, the GUI no longer needs the security group rule for port 8080.

Stack

This PR is based on #21 (feat/ci-built-web-client). Merge it into that branch first, then merge #21 into main, so there is a single deploy. CI test jobs only run on PRs into main, and this PR doesn't change any service code.

Test plan

  • nginx -t passes in an nginx:stable container.
  • End-to-end test in Docker: nginx with this config served a same-origin build. The real cloud-services ran in a container sharing nginx's network namespace, so 127.0.0.1:8080 resolves as it does on EC2.
    • / returns 200 with Cache-Control: no-cache, /devices returns the app shell, and a missing asset returns 404.
    • A hashed asset returns 200, gzipped, with immutable caching.
    • POST /auth/login-viewer returns 200, /auth/me reports the viewer role, /api/hubs returns 200, and a viewer restart command returns 403.
    • A 5 MB flash body reaches the backend (403 from the backend, not 413 from nginx).
    • The /ws/client and /hub websocket upgrades return 101.
    • The backend log shows the client's IP, not 127.0.0.1.
  • actionlint passes, and shellcheck is clean on the remote script apart from one info note on an existing line.
  • After merge: the deploy succeeds, systemctl is-active nginx reports active, curl -s localhost:4173/health responds, and view-only login works from the browser at http://98.93.47.19:4173.

🤖 Generated with Claude Code

…rigin

`vite preview` is not meant for production and kept a ~90 MB Node process plus
a full web-client checkout and node_modules on the instance. nginx now serves
the CI-built files on port 4173 and proxies /auth, /api, /ws, /hub and /health
to cloud-services, so the browser makes same-origin requests: no CORS, and
port 8080 no longer has to be open to the internet.

The bundle is now built with an empty API URL (the page origin) unless the
WEB_API_BASE_URL repository variable is set. The first deploy installs nginx
if it is missing and disables the old `frontend` service.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Copilot AI balanced review requested due to automatic review settings October 1, 2026 21:00
@WesP10 WesP10 mentioned this pull request Oct 1, 2026
2 of 3 tasks

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@hyperloop-cornell

Copy link
Copy Markdown
Owner

lgtm

@hyperloop-cornell
hyperloop-cornell merged commit 3aa1905 into feat/ci-built-web-client Oct 1, 2026
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.

3 participants