Tenant Backup + Restore

Self-service request, AES-256 encryption, R2 upload, 14-day retention

5 นาที·อัปเดต 2026-05-24

Tenant ส่ง backup request

ลูกค้าเปิด /dashboard/settings/backup → กด ขอ backup:

  • ระบบสร้าง BackupJob row (status: PENDING)
  • Owner-only (สิทธิ์ TENANT_OWNER)
  • Rate limit: 1 in-flight job ต่อ tenant

Backup worker

/api/cron/backup-process รันทุก 1 นาที (Vercel cron):

  1. Claim atomicallyupdateMany PENDING → RUNNING (กัน 2 worker ทำ job เดียวกัน)
  2. Export tenant data — stream NDJSON ทุก tenant-scoped table (ใช้ cursor pagination 500 rows/page)
  3. Encrypt — AES-256-GCM streaming (IV + ciphertext + auth tag)
  4. Upload — multipart to R2/S3 (tenant-backups/{tenantId}/{jobId}.bin.enc)
  5. 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:

  1. Customer support ติดต่อทีม engineering
  2. Engineer download backup file + decrypt
  3. ใช้ migration script restore ทับ tenant (ระวัง: ทับข้อมูลปัจจุบันทั้งหมด)
  4. ทดสอบ → 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 ติดตั้งแล้ว

บทความที่เกี่ยวข้อง