Skip to content

deployment

Kadyapam edited this page May 24, 2026 · 1 revision

Deployment

How the travel SPA gets to production at https://travel.mestumre.dev.

The SPA is a Vite-built static bundle served from Cloudflare Pages. It talks to the NoETL gateway on a separate origin (https://gateway.mestumre.dev). The gateway and the NoETL server run on GKE.

The pieces

Surface Hosting Source
SPA static bundle Cloudflare Pages dist/ (Vite build output)
Container image (alt. for self-host) Docker / OCI Dockerfile + nginx.conf
Cloudflare Workers / Pages config Cloudflare wrangler.toml
Build script Local / CI scripts/build_container.sh

Build

The SPA build runs Vite with TypeScript and produces a static bundle:

npm ci
npm run contracts       # regenerate src/contracts/widgets.ts from JSON schemas
npm run type-check      # tsc --noEmit
npm run build           # tsc --noEmit && vite build

The output lands in dist/. The package's build:cf script runs the full sequence in one command for Cloudflare Pages.

Build-time environment

The SPA reads these VITE_* env vars at build time. The full list is in .env.example.

Var What
VITE_NOETL_API_BASE_URL Direct NoETL API base, used only in guest mode (local dev).
VITE_GATEWAY_BASE_URL Production gateway URL (e.g. https://gateway.mestumre.dev).
VITE_ALLOW_GUEST true to bypass auth in local dev. Production builds set this false.
VITE_AUTH0_DOMAIN Auth0 tenant (e.g. mestumre-development.us.auth0.com).
VITE_AUTH0_CLIENT_ID Auth0 SPA client ID (public value).
VITE_GOOGLE_MAPS_KEY Restricted Google Maps embed key.

None of these are secrets. The Auth0 domain and client ID are public; the Google Maps key is restricted by referrer; the gateway URL is intentionally a public endpoint.

Cloudflare Pages deployment

The travel SPA is deployed via Wrangler:

npm run deploy:cf
# expands to: npx wrangler pages deploy dist --project-name travel --branch main

Wrangler picks up wrangler.toml. The main branch is the production deployment; other branches map to preview URLs.

Cloudflare Pages sits behind Cloudflare DNS for travel.mestumre.dev. Access protection (Cloudflare Access) can be added in front; the gateway-session model works equally well with or without it.

Container deployment (alternative)

For self-hosting or container-based platforms, the SPA also builds as a Docker image with nginx serving the bundle:

TAG="$(date -u +%Y%m%d-%H%M%S)" ./scripts/build_container.sh

The build script:

  1. Runs npm ci && npm run build inside a build stage.
  2. Copies dist/ into an nginx-alpine image.
  3. Tags the result with the date-stamp.

The container exposes nginx on port 80; nginx.conf declares the SPA's history-API fallback so /route/anything falls back to /index.html.

Where the gateway and NoETL run

The SPA's backend is on a separate cluster:

  • Gateway: GKE deployment in the gateway namespace, fronted by a Cloudflare Tunnel (cloudflare/noetl-gke-gateway-tunnel).
  • NoETL server + worker pool: GKE deployment in the noetl namespace, exposed only inside the cluster.
  • NATS: GKE deployment in the nats namespace.
  • Postgres: Cloud SQL with PgBouncer in the postgres namespace.

For the deploy mechanism on the backend side, see the GKE Helm install page on the ops wiki.

DNS and Auth0

The production setup:

  • travel.mestumre.dev → Cloudflare Pages (SPA).
  • gateway.mestumre.dev → Cloudflare Tunnel → GKE gateway service.
  • Auth0 application: Allowed Callback URLs, Allowed Logout URLs, Allowed Web Origins, and Allowed Origins (CORS) all include https://travel.mestumre.dev.
  • Auth0 application's application_type is Single Page Application (no client secret used in the browser flow; the gateway has the client secret server-side).

When forking, replace mestumre.dev everywhere with your domain and update the Auth0 application URLs to match.

Local development

cp .env.example .env.local
# edit .env.local with your local NoETL / gateway URLs
npm install
npm run dev

Set VITE_ALLOW_GUEST=true in .env.local to skip Auth0 during local iteration. The SPA will hit a local NoETL at the VITE_NOETL_API_BASE_URL you configured.

For a quick local NoETL, follow the kind install path.

Validation before shipping

Required checks (all passing in CI):

npm test             # vitest unit suite
npm run smoke:widgets # widget envelope round-trip harness
npm run type-check    # tsc --noEmit
npm run lint          # tsc --noEmit (same; redundant alias)
npm run build         # full build

The CI workflow under .github/workflows/ gates merges on these.

Related

Clone this wiki locally