3.4 KiB
3.4 KiB
Phase 9 — Cutover Runbook
Pre-flight checklist (before cutting traffic)
Run from the project root on your local machine.
bash db/01-load-schema.sh— schema + RLS loaded on new server (14 tables)SUPABASE_DB_URL="..." bash db/02-dump-and-restore.sh— all public data restoredSUPABASE_DB_URL="..." bash db/03-migrate-passwords.sh— passwords migrated, 0 NULL rowsSUPABASE_URL="..." SUPABASE_SERVICE_ROLE_KEY="..." bash db/04-migrate-evidence.sh— evidence files on serverSUPABASE_DB_URL="..." bash db/05-verify.sh— row counts match, embeddings present/var/www/ims/.envpopulated on server (seedocs/new-server-setup.mdsection I)bash deploy.sh— Next.js standalone build deployed,systemctl status imsshows Active- nginx config live at
ims.setia.com.my(seedocs/new-server-setup.mdsections E–F) - TLS cert issued (
sudo certbot --nginx -d ims.setia.com.my) curl -I https://ims.setia.com.my/returns HTTP/2 200- Cron jobs registered (see
docs/vps-cron.md) - Daily backup cron registered (see
docs/new-server-setup.mdsection K)
DNS cutover
Point ims.setia.com.my A record to the new server's public IP.
TTL: set to 60s before cutover, restore to 3600s after.
Smoke tests (run immediately after cutover)
Test as each role. All must pass before telling users to switch.
Reporter role
- Log in with a migrated user's real password — succeeds
- Submit new incident with photo evidence — reference number generated, evidence stored
- Open own incident detail — evidence renders (HMAC-signed URL serves file)
Supervisor role
- Log in
- View site incidents list — all historical incidents present
- Triage an incident — triage saved, audit_log row written with correct
changed_by
HSE role
- Log in
- Run AI triage suggestion — response received (Anthropic key working)
- Run Similar Incidents — embeddings returning results
- Open dashboard — stats render, JKKP8 export downloads CSV
- AI risk flags panel loads
Admin role
- Log in
- Create new user — user created, no Supabase call
- Delete user — user removed (auth + profile)
CAPA owner role
- Log in
- View assigned CAPAs
- Mark a CAPA in-progress — status updates
Password reset flow
- Request reset for own email — Brevo email arrives
- Complete reset — new password accepted
Cron (manual trigger)
- CAPA escalation endpoint returns
{"ok":true} - Effectiveness recheck endpoint returns
{"ok":true}
RLS spot-check (security)
# As app_user role with a reporter's user_id set:
sudo -u postgres psql -d ims
SET app.user_id = '<reporter-A-uuid>';
SELECT * FROM incidents WHERE reported_by != '<reporter-A-uuid>';
-- Expected: 0 rows (RLS blocks cross-user read)
Post-cutover
- Monitor
/var/log/ims-cron.logfor first cron run (08:00 MYT next day) - Monitor
sudo journalctl -u ims -ffor runtime errors - After 48h stable: take snapshot of new server DB (
pg_dump ims > /var/lib/ims/backups/pre-supabase-decommission.sql) - Decommission Supabase project only after snapshot confirmed
Rollback plan
If cutover fails within the first hour:
- Repoint DNS A record back to the old VPS IP (
64.176.82.100) - Supabase project remains live throughout — no data risk
- Investigate failure before re-attempting