Deploy & ops
Production app: thermaltrace.dev.
pnpm ops:check # verify .env has core keys
pnpm test && pnpm typecheck && pnpm build
pnpm db:push # apply Supabase migrations
pnpm secrets:push # sync .env → Worker secrets
pnpm deploy # astro build + wrangler deployAfter deploy:
- Infra IDs vs brand — Cloudflare Worker name may stay
garage-temp; some API paths like/api/garage-temps/*are stable URLs. User-facing brand is ThermalTrace. - Set
SITE_URL/ORIGINWorker secrets tohttps://thermaltrace.dev - Confirm cron triggers (
0 * * * *and15,30,45 * * * *inwrangler.jsonc) are active - Enable Cloudflare Email Sending for the domain in
SMTP_MAIL_FROM(see below) - Point Stripe webhooks at
/api/stripe/webhookwithSTRIPE_WEBHOOK_SECRET - Optional:
node --env-file=.env scripts/rename-stripe-products.mjsto rename Stripe product labels
Keep display amounts aligned with live Stripe:
pnpm audit:stripe
# or open /dashboard/ops → Stripe display price auditOutbound email (Cloudflare Email)
wrangler.jsonc uses an unrestricted MAILER send_email binding so drip, trial, alert, and digest mail can reach user addresses. Enable Email Sending for the domain in SMTP_MAIL_FROM:
- Cloudflare Dashboard → Email → Email Sending, or
- CLI:
wrangler email sending enable <your-sending-domain>
If the binding is locked with destination_address, mail to other inboxes fails with email to … not allowed. Drip cron treats that as restricted (job stays success) until Email Sending is enabled. The contact form still targets SMTP_MAIL_TO.
Helpers: src/lib/mailer.ts (sendPlainEmail, sendMailerRaw).
GitHub Actions deploy
Astro inlines import.meta.env at build time. Deploy must use real production secrets — never CI placeholders — or auth breaks.
One-time (Cloudflare API token with Workers edit):
CLOUDFLARE_API_TOKEN=... pnpm setup:github-secretsThat syncs CLOUDFLARE_* plus build secrets from .env into GitHub Actions secrets. Pushes to main then run .github/workflows/deploy.yml. Without those secrets the workflow succeeds with a skip notice (use pnpm deploy locally).
Custom domain
The Worker binds thermaltrace.dev (and www → apex via middleware). Legacy hostnames listed in wrangler.jsonc redirect with 301. Set SITE_URL and ORIGIN to https://thermaltrace.dev, then pnpm secrets:push.
Public smoke (no auth)
pnpm smoke:public
pnpm ops:smoke # smoke + sitemap coverage + search-engine ping
pnpm ping:sitemaps # Google/Bing sitemap ping onlySubmit https://thermaltrace.dev/sitemap-index.xml in Google Search Console → Sitemaps.
Sitemaps are SSR routes (/sitemap-0.xml, /sitemap-index.xml) so they stay available on Cloudflare Workers even when static Asset uploads omit build-time XML.
Admin channel tests (SMS/push) run from Dashboard → Ops after sign-in.
Post-deploy smoke checklist
- [ ]
/system-status— healthy + recent cron runs - [ ]
/pricing— amounts match Stripe (monthly/annual toggle) - [ ]
/compare,/docs/api, case study CTAs →/pricing - [ ] Dashboard → Ops (admin) — email + channel smoke tests (SMS/push when configured)
- [ ] Enable NWS freeze alerts under Alerts after migration
- [ ] Save display prefs / alert settings (Actions) without full reload
- [ ] Optional:
OPS_DISCORD_WEBHOOK_URLfor failure Discord - [ ] Test alert → Share page webhook delivery log (Pro)
CI on every push to main: astro sync + typecheck, unit tests, build, Playwright smoke.