Skip to content

Deploy & runbook

Practical operations for whoever ships and maintains the platform. Product managers can skim this to understand how changes go live and how the center is kept safe.

How a change goes live

There is no automatic deploy pipeline — shipping is a deliberate, manual command.

# From the Cloudflare project directory:
npm run deploy      # builds the web app, then deploys the Worker

This single command:

  1. Builds the React app.
  2. Bundles it as the Worker's static assets.
  3. Deploys the Worker to kategeneral.long-cao.dev.

Because web and API ship together, there is never a version mismatch between the two.

Node version

The deploy tooling needs a recent Node.js (22+). If the deploy errors on an old Node, switch versions first.

Before you deploy (checklist)

  • [ ] Secrets are set in production — wrangler secret list shows JWT_SECRET, AZURE_SPEECH_KEY, OCR_SECRET.
  • [ ] The end-to-end role/permission suite passes against production.
  • [ ] Any database change is a safe, additive migration (see below) — never the destructive reset.

Database migrations

Schema changes are applied as idempotent migration files (safe to run more than once):

npm run db:migrate:remote     # applies the additive migration set to production

The one command never to run against production

A separate script rebuilds the database from scratch — it drops every table and reseeds demo data. It is deliberately renamed to a RESET…DANGER name with a countdown warning so it can't be run by accident. It must never be used on the live center's data.

Backups & recovery

  • D1 Time Travel provides point-in-time recovery — the database can be restored to an earlier moment. This is the safety net against a bad bulk operation.
  • Practically: if a wrong mass update happens, restore to just before it rather than trying to undo by hand.
  • Test content (mock tests, demo data) is reproducible from seed scripts, so it can always be regenerated.

Monitoring & logs

  • Live logs: wrangler tail streams the Worker's logs in real time.
  • Observability is enabled on the Worker (request/error visibility in the Cloudflare dashboard).
  • AI budget: the manager watches monthly AI usage vs. caps on the Reports → AI usage panel; the caps themselves fail closed, so an overrun is prevented, not just observed.

Health checks

  • GET /health and GET /api/health return a simple OK (and confirm the database and storage bindings are reachable). They do not leak internal error details.

Routine operational tasks

Task How
Add a manager/teacher/student Admin creates the account in the app (no SQL needed)
Onboard a class of students Enroll them into a class → a tuition charge is raised automatically from the class fee
Block a departed account Admin toggles the user's status — effective immediately
Regenerate a demo/mock test Run the corresponding seed script
Investigate a stuck grading Grading is idempotent; re-triggering is safe. Check logs via wrangler tail

Incident quick-reference

  • "Everyone is logged out / auth failing" → check JWT_SECRET is still set (the app fails closed without it).
  • "Audio won't play" → media is signed-URL only; a link may have expired (they're short-lived by design). Reload the page to get a fresh URL.
  • "AI stopped grading" → likely the monthly cap; check the AI usage panel. New essays go to the teacher queue in the meantime.
  • "A bad bulk edit hit the DB" → restore via D1 Time Travel to just before it.