Skip to content

Deployment & handover

This is the guide for taking over or contributing to NanaSelect — running it, deploying it, and knowing where things live. It's written for a new contributor arriving cold (hello, Aziz 👋).

How to read it: do the Start here section first — that's everything you need to run the app and understand the shape of it. The Deploy and Add a reviewer sections are the day-to-day operations. Everything under Going deeper is ops depth you can safely ignore until you need it. Don't try to read it all at once.


Start here (your first hour)

1. What NanaSelect is

NanaSelect is a standalone product-selection app that guides architects, builders, and homeowners to the right-sized NanaWall system, then hands off into the nanawall.com configurator. It runs on its own and augments the legacy nanawall.com (Drupal) site — it owns its own product database and talks to Drupal over JSON. For the full "why", read the product vision; for the "how it fits together", the data access reference.

Stack: SvelteKit · Cloudflare Workers · D1 (SQLite at the edge, via Drizzle) · Tailwind · Vitest.

2. Run it locally

You don't need a Cloudflare account to run NanaSelect locally — Wrangler runs D1 on your machine.

sh
pnpm install
dev-up            # migrate -> seed -> serve -> health-check, in one step

Then open http://localhost:6650. Locally the admin signs you in automatically (a dev bypass badge), so /admin just works. If you'd rather run the steps yourself, the README quick start lists the underlying pnpm commands.

Next, walk the Set up your first track tutorial — it's the fastest way to understand what the app actually does.

3. Where things live

src/routes/          UI pages + server endpoints (guided flow, comparison, admin)
src/lib/server/db/   Drizzle schema + client — the single source of truth
drizzle/             D1 migrations (generated by drizzle-kit)
seed/                CSV -> D1 ingestion (see seed/README.md)
docs/                This documentation site

The hosting model

NanaSelect deploys to Cloudflare:

  • The app is a single Worker named nanaselect (see wrangler.jsonc), serving the SvelteKit build.
  • Data lives in a D1 database, nanaselect-db, bound to the Worker as DB.
  • The /admin area is gated at the edge by Cloudflare Access (Zero Trust) — see admin access for the full model.
  • This docs site is a separate deploy (Cloudflare Pages), published by CI — see Building & publishing the docs site.

The app deploy is manual today (there is no app CI/CD pipeline — only the docs site auto-deploys). That keeps you in control of when production changes.


Deploy the app

One-time setup:

sh
wrangler login                       # authenticate to the Cloudflare account
# The D1 database already exists (id is in wrangler.jsonc). If you ever need a
# fresh one: wrangler d1 create nanaselect-db, then put the id in wrangler.jsonc.

Each deploy:

sh
pnpm db:apply:remote    # apply any new migrations to the remote D1 (first time / after schema changes)
pnpm seed:remote        # load/refresh the seed catalogue on remote D1 (when data changes)
pnpm run deploy         # deploy gate -> build -> wrangler deploy

pnpm run deploy runs the deploy gate (#1058, security review F-D1) before anything ships: it refuses to publish a wrangler.jsonc whose vars leave CF_ACCESS_TEAM_DOMAIN/CF_ACCESS_AUD empty or contain ADMIN_DEV_BYPASS in any form — the config combination that would open /admin without Access. Run it alone with pnpm run deploy:gate; a drift-guard spec (deploy-gate.spec.ts) also fails the suite if the committed config ever regresses. Deploying with a raw wrangler deploy skips the gate — don't.

There's also a small companion Worker for session cleanup — deploy it with pnpm run deploy:purge when its code changes.

Production hostname

Production must be served from a .nanawall.com subdomain for consent (Iubenda/GTM) to work — see admin access and the consent model. The public app + docs hostnames and their shared access allowlist are tracked in #560; until they're set up, treat deploys as staging.


Add a reviewer (grant access)

Two things gate who can do what:

  1. Reaching /admin at all — the person's email must be allowed by the Cloudflare Access policy in the Zero Trust dashboard (add their email to the Access application in front of /admin*).
  2. What they can do once in — their email must have a role in the admin_users table (data_editor / rules_editor / admin). Add them in seed/sql/010-admin-users.sql (or insert directly); emails are stored lower-cased.

Full step-by-step (including the one-time Zero Trust application + AUD/team-domain setup) is in admin access. For the POC review host (nanaselect.iwpi.com) — where the whole site, not just /admin, is behind the allowlist — invite/removal steps are in POC access.


Where to get help


Going deeper (ops — read when you need it)

You can skip this on day one.

  • Migrations. Schema is defined in src/lib/server/db/schema.ts; generate a migration with pnpm db:generate, apply locally with pnpm db:apply:local, remotely with pnpm db:apply:remote. See audit & validation for how edits are validated.
  • Seed data. pnpm seed:local / pnpm seed:remote build seed.sql from the CSVs, load it, then apply the catalog fragments through the shared runner — the fragments rebuild what seed.sql's attribute REPLACE cascades away, so both steps are required. Seed covers all lifecycle states; new features ship seed data in the same change.
  • Reset local data. pnpm db:reset:local wipes and rebuilds the local D1.
  • Consent & retention. Iubenda (via GTM) is the consent source of truth; the IUBENDA_* and SESSION_TTL_DAYS vars in wrangler.jsonc drive the 2-tier consent + retention model. The session-purge Worker enforces retention.
  • Drupal sync. DRUPAL_BASE_URL points at nanawall.com; the app ingests Drupal media/content — see the corpus refresh how-to.

Keep this guide honest: the commands above mirror package.json and wrangler.jsonc. If you change a script or the hosting model, update this page in the same change.