# Deploying the website to Cloudflare Pages

> Day-to-day flow (staging, embed checks, tag releases, CI) lives in
> **`docs/STAGE-TEST-RELEASE.md`** — this file covers the underlying build
> and deploy mechanics.

The site is a **Next.js multi-page static export**: `npm run build:static`
produces `out/`, a folder of plain static files with no server component.
Cloudflare Pages serves that folder directly. (This replaces the old
S3/Jenkins pipeline from the Create-React-App era — no bucket, no CloudFront,
no Jenkins step needed.)

## 1. Build

On any machine with Node 20+:

```bash
npm ci
BIG_API_SERVER_URL=...  \
BIG_CLOUD_API_URL=...    \
BIG_API_KEY=...          \
BIG_RDC_SERVER_HOST=...  \
BIG_STRIPE_PK=...        \
npm run build:static
```

Output: **`out/`** — the complete site, including the routing files Pages
reads (`_redirects`, see §3).

## 2. Env vars (public, baked at build time)

These five are read at build time and written into `out/config.json`, which
the client reads. They are all **public** (the old React app baked the same
values into its JS bundle) — safe in a client-visible file. Values come from
`Server_Keys` (ask Brandon):

| var | what |
| --- | --- |
| `BIG_API_SERVER_URL` | main Bigscreen API base URL |
| `BIG_CLOUD_API_URL` | cloud API base URL |
| `BIG_API_KEY` | public API key (Bearer) |
| `BIG_RDC_SERVER_HOST` | remote-desktop config host |
| `BIG_STRIPE_PK` | Stripe **publishable** key |

If unset, `config.json` points at a local mock — the marketing site still
works fully, but account/orders/purchase pages can't reach the real API.
Fine for a test deploy; **must be set for the production build**.

## 3. Routing (handled automatically)

Nothing to configure by hand:

- **Clean URLs** (`/about/`, `/account/login/`) — every page is exported as
  `<path>/index.html`; Pages serves directory indexes natively.
- **Root** — `/` serves a tiny built-in redirect to `/en`.
- **Dynamic URL patterns** (`/account/reset/:token`, `/purchase/:id/:url`,
  `/browser/:activityId`, …) — the build writes `out/_redirects` with a
  `200` rewrite per pattern; Pages applies it automatically. These URLs are
  load-bearing: the API's transactional emails, the iOS app clip, and the
  eyetracking client hard-code them. The host-agnostic list also lands in
  `out/_routing-spec.json` for reference.

New routes only ever get added in `scripts/routes.mjs` — both files are
generated from it.

## 4. Deploy

One-time: `npx wrangler login` (opens the browser; use the Bigscreen
Cloudflare account). Then:

```bash
npm run deploy        # build:static + wrangler pages deploy
```

`wrangler.toml` carries the project name (`bigscreen-website`) and output
dir, so no arguments are needed. The first deploy creates the Pages project
and puts the site on `bigscreen-website.pages.dev` — **no DNS is touched**.

For CI, set `CLOUDFLARE_API_TOKEN` (scope: *Cloudflare Pages: Edit* only —
such a token cannot touch DNS) and `CLOUDFLARE_ACCOUNT_ID` instead of
`wrangler login`.

## 5. Custom domains — the careful part

All of Bigscreen's DNS (email, website, VR app) lives in this Cloudflare
account. The safe order:

1. Verify everything on `*.pages.dev` first (zero DNS risk).
2. Attach a **test subdomain** (e.g. `new.bigscreenvr.com`) via the Pages
   project → Custom domains. This only *adds* a CNAME; existing records are
   untouched.
3. Pointing the apex `bigscreenvr.com` / `www` at Pages changes an existing
   record — coordinate that cutover, export the zone (DNS → Records →
   Export) beforehand, and note the old record's value + proxy status for
   rollback.

Never touch: nameservers, MX/TXT records (company email), unfamiliar
A/CNAME records (VR app APIs), or the zone's SSL/TLS mode.

## 6. Verifying a build locally

```bash
npm run build:static   # -> out/
npm run preview        # plain static server on :3001 (no _redirects rules)
npm run preview:pages  # wrangler pages dev — full Pages emulation, applies
                       # _redirects, so dynamic URLs are testable locally
```

`npm run local` remains the day-to-day dev server (with the mock API); it is
unaffected by any of the above.
