Tenant Backup + Restore
Self-service request, AES-256 encryption, R2 upload, 14-day retention
Tenant ส่ง backup request
ลูกค้าเปิด /dashboard/settings/backup → กด ขอ backup:
- ระบบสร้าง
BackupJobrow (status: PENDING) - Owner-only (สิทธิ์ TENANT_OWNER)
- Rate limit: 1 in-flight job ต่อ tenant
Backup worker
/api/cron/backup-process รันทุก 1 นาที (Vercel cron):
- Claim atomically —
updateManyPENDING → RUNNING (กัน 2 worker ทำ job เดียวกัน) - Export tenant data — stream NDJSON ทุก tenant-scoped table (ใช้ cursor pagination 500 rows/page)
- Encrypt — AES-256-GCM streaming (IV + ciphertext + auth tag)
- Upload — multipart to R2/S3 (
tenant-backups/{tenantId}/{jobId}.bin.enc) - Mark COMPLETED + เซต
expiresAt= +14 วัน
ใช้เวลา ~30 วินาที ถึง 5 นาที (ตามขนาด tenant)
Download
Settings → Backup ฝั่ง tenant:
- Job เห็น status: PENDING → RUNNING → COMPLETED
- เมื่อ COMPLETED → ปุ่ม Download โผล่ขึ้น
- Click → signed URL valid 1 ชม. (re-sign ทุกครั้งที่ refresh)
ไฟล์ที่ได้: .bin.enc — encrypted blob. ต้องใช้ BACKUP_ENCRYPTION_KEY ของ HexaHealth decrypt
Decryption (ฝั่งลูกค้า)
ลูกค้าที่ต้องการ restore ข้อมูล (เช่น migrate ไประบบอื่น):
- ติดต่อ HexaHealth support ขอ decrypt
- หรือ HexaHealth ส่ง JSON ที่ decrypt แล้วผ่าน secure channel
- ทำเอง not supported (key อยู่กับ HexaHealth เพื่อ security)
Future: customer-managed encryption key สำหรับ ENTERPRISE — ลูกค้าถือ key เอง
ขนาดไฟล์ที่คาดได้
| Tenant size | Backup size | |---|---| | STARTER (100 patient/เดือน × 12 เดือน) | 5-20 MB | | GROWTH (1k patient/เดือน × 12 เดือน) | 50-200 MB | | PRO (full year, multi-branch) | 500 MB - 2 GB | | ENTERPRISE | 2-20 GB |
X-ray + lab images ไม่ใส่ใน backup (เพราะใหญ่เกิน). เก็บแยกใน R2
Retention
/api/cron/backup-cleanup รันทุกวัน 03:00 ICT:
- หา BackupJob ที่
expiresAt < now+ มี fileUrl - ลบไฟล์ R2
- เซต fileUrl = null + errorMessage = "Backup file expired"
- เก็บ row ไว้สำหรับ audit (ดูประวัติได้)
Restore
ไม่ self-service — ต้องติดต่อ HexaHealth ขอ restore:
- Customer support ติดต่อทีม engineering
- Engineer download backup file + decrypt
- ใช้ migration script restore ทับ tenant (ระวัง: ทับข้อมูลปัจจุบันทั้งหมด)
- ทดสอบ → confirm กับลูกค้า
ใช้สำหรับ:
- Disaster recovery (data corruption)
- Tenant ตัดสินใจ rollback ไป snapshot
- Legal hold
Audit
ทุก backup operation เก็บใน AuditLog:
- ใครขอ (
tenant.backup.request) - Worker run + status (
tenant.backup.complete/tenant.backup.fail) - Cleanup (
tenant.backup.expired) - Restore (manual entry by engineer)
Setup
ใน .env:
BACKUP_ENCRYPTION_KEY=<64-hex-chars>(generate:openssl rand -hex 32)CRON_SECRET(สำหรับ cron auth)AWS_BUCKET_NAME+AWS_ENDPOINT(R2) + access keys
โดน @aws-sdk/client-s3 ติดตั้งแล้ว