UAT Deploy Runbook
Overview
The OdontoX UAT environment lives atuat.odontox.io and is deployed from
the perf/neon-tanstack branch. The Cloudflare Worker (odonto-uat) handles
the API at uat.odontox.io/api/*; Cloudflare Pages serves the rest of the UI
under the same hostname. The UAT database is a Neon branch off prod —
schema and data are real prod copies, so all outbound communication
(email, WhatsApp, SMS, Stripe charges) is hard-disabled via the
UAT_DISABLE_OUTBOUND=true environment variable to ensure no real patient is
contacted or charged from UAT.
Architecture (single subdomain, path-routed)
| Path | Handled by |
|---|---|
https://uat.odontox.io/api/* | Worker odonto-uat |
https://uat.odontox.io/* | Cloudflare Pages (UI) |
api., id., go., portal.,
tenants.) into a single host. The UI helper subdomain-utils.ts detects
uat.odontox.io and keeps cross-host helpers on the same origin. Public
share links and asset URLs likewise stay path-rooted.
Resources already created
| Resource | UAT value |
|---|---|
| Worker name | odonto-uat |
KV namespace OTT_STORE | fad4a0a6b8d9449bb6cceac8723524b2 |
| R2 bucket | odontox-files-uat |
| Rate limiter namespace | 1002 (prod uses 1001) |
| Durable Object migration | v1-clinic-hub-uat |
| Cron schedule | mirrors prod (0 4, 0 9, 0 14, 0 16) |
| Database (Neon branch) | ep-super-sun-a1y850mh-pooler.ap-southeast-1 |
Secrets to set (run from server/)
DATABASE_URL is already set. Run each line below — values come from a
secure source (1Password / prod wrangler), NOT this file.
DNS (Cloudflare DNS, proxied)
The singleuat.odontox.io record is shared by Pages + the Worker route.
Pages will give you a *.pages.dev target after the project is created.
| Type | Name | Target | Proxy |
|---|---|---|---|
| CNAME | uat | <your-pages-project>.pages.dev | yes |
uat.odontox.io/api/* short-circuits before Pages.
Cloudflare Pages project
- Cloudflare dashboard → Workers & Pages → Create application → Pages → Connect to Git.
- Repo:
odontoX. Production branch:perf/neon-tanstack. - Project name:
odontox-uat-ui. - Build command:
cd ui && pnpm install && pnpm build(UI build script isvite build && node postbuild.js.) - Build output directory:
ui/dist. - Environment variables (Production branch):
VITE_API_URL— leave empty (api-url.ts auto-detects UAT host).VITE_ASSET_URL— leave empty.
- Custom domain → Set up custom domain →
uat.odontox.io.
Worker deploy
Verification checklist
-
curl https://uat.odontox.io/api/v1/healthreturns 200. - Login flow on
https://uat.odontox.io/auth/loginsucceeds against the UAT Neon branch DB (not prod). - Patients / Appointments / Billing modules load data; cross-check at least one record vs. the UAT branch DB to confirm it’s UAT, not prod.
- Mutations (create patient, schedule appointment) write only to the UAT DB — re-read to confirm; the prod DB row count must NOT change.
- Worker logs show
[UAT] outbound email disabled, skipping ...lines and zero send events appear in the Zepto dashboard during testing. - Stripe dashboard (test + live) shows zero new charges/customers from
UAT activity. Worker logs show
[UAT] outbound Stripe ... disabled. - WhatsApp Business Manager shows zero outbound messages from the UAT
session. Worker logs show
[UAT] outbound WhatsApp disabled. - Cron handlers fire on schedule (wait at least 24 h, then
wrangler tail --env uat | grep cron). - On login/logout,
clearAllQueryCachesclears React Query localStorage (verify via DevTools → Application → Local Storage). - Cross-tenant cache isolation: log in as a user with multiple clinics, switch clinics, confirm data does not bleed between caches.
Rollback
UAT is non-customer-facing. To roll back, redeploy the previous Worker version withwrangler rollback --env uat and revert the Pages deployment
in the dashboard.
