Files
alla-allaos-fullstack/plans/task-i.md
phaichayon 0a484e0b45 task-i
2026-06-16 17:01:29 +07:00

8.4 KiB

Task I: Storage Abstraction + Approved Artifact Lifecycle

ALLA OS CRM vNext

คุณคือ Senior Full-stack Engineer

ให้ implement storage abstraction และ approved artifact lifecycle สำหรับ Quotation PDF โดยต่อยอดจาก Task H และ H.1


Must Read

docs/implementation/technical-debt.md
docs/adr/0008-attachment-storage-strategy.md
docs/adr/0009-approved-document-storage-lifecycle.md

src/features/foundation/pdf-generator/**
src/features/foundation/document-template/**
src/features/crm/quotations/document/**
src/features/crm/quotations/**
src/app/api/crm/quotations/[id]/approved-pdf/route.ts
src/db/schema.ts

Goal

ทำให้ PDF storage พร้อม production มากขึ้น

จากเดิม:

public/generated/quotations/<organizationId>/

ไปเป็น:

Storage Provider Interface
Local Provider for dev
S3 / MinIO compatible provider for production
Secure download route
Immutable approved artifact lifecycle

Scope I1: Storage Provider Interface

สร้าง foundation module:

src/features/foundation/storage/**

Files:

types.ts
service.ts
server/local-provider.ts
server/s3-provider.ts
server/provider.ts

Interface:

export interface StorageProvider {
  putObject(input: PutObjectInput): Promise<StoredObject>
  getObject(input: GetObjectInput): Promise<StoredObjectStream>
  deleteObject(input: DeleteObjectInput): Promise<void>
  objectExists(input: ObjectExistsInput): Promise<boolean>
  getSignedUrl?(input: GetSignedUrlInput): Promise<string>
}

Types:

PutObjectInput
GetObjectInput
DeleteObjectInput
ObjectExistsInput
GetSignedUrlInput
StoredObject
StoredObjectStream

Rules:

  • service ต้อง server-only
  • client ห้ามเข้าถึง provider โดยตรง
  • storage key ต้อง organization-scoped
  • ห้ามใช้ raw public path เป็น source of truth

Scope I2: Storage Configuration

รองรับ env:

STORAGE_PROVIDER=local | s3

LOCAL_STORAGE_ROOT=storage

S3_ENDPOINT=
S3_REGION=
S3_BUCKET=
S3_ACCESS_KEY_ID=
S3_SECRET_ACCESS_KEY=
S3_FORCE_PATH_STYLE=true

Rules:

  • local ใช้ได้ใน dev
  • s3 provider ต้อง compatible กับ MinIO/S3
  • ถ้า config ไม่ครบ ให้ throw error ชัดเจน
  • อย่า hardcode bucket/path

Scope I3: Artifact Schema

เพิ่ม table ถ้ายังไม่มี:

crm_document_artifacts

Fields:

id
organizationId

entityType
entityId

documentType
artifactType

templateVersionId

storageProvider
storageKey
fileName
contentType
fileSize
checksum

status

generatedBy
generatedAt

lockedAt
lockedBy

voidedAt
voidedBy
voidReason

metadata

createdAt
updatedAt
deletedAt

artifactType:

preview
approved_pdf
export

status:

active
locked
voided
deleted

Rules:

  • approved_pdf ต้อง lock หลัง generate
  • approved_pdf ห้าม overwrite
  • ถ้าต้อง regenerate ให้สร้าง artifact ใหม่หรือ void artifact เดิมก่อนตาม policy
  • organizationId required

Scope I4: Checksum

เพิ่ม utility:

src/features/foundation/storage/server/checksum.ts

รองรับ:

sha256(buffer)

ใช้บันทึก checksum ของ PDF artifact


Scope I5: Approved PDF Storage Refactor

ปรับ generateApprovedQuotationPdf() จากการเขียน:

public/generated/...

เป็น:

StorageProvider.putObject()
crm_document_artifacts insert
quotation.approvedPdfUrl update เป็น secure reference

Recommended approvedPdfUrl:

artifact:<artifactId>

หรือถ้ามี field ใหม่:

approvedArtifactId

ถ้าต้องเพิ่ม field ใน quotation:

approvedArtifactId

ให้เพิ่มแบบระวัง

Rules:

  • approved PDF generate ได้เฉพาะ quotation.status = approved
  • ถ้ามี locked approved artifact อยู่แล้ว ให้ไม่ overwrite
  • ถ้าต้อง regenerate ให้ return error พร้อมข้อความชัดเจน
  • create attachment metadata ให้ชี้ storageKey/artifactId
  • audit generate approved PDF

Scope I6: Secure Download Route

ปรับ:

GET /api/crm/quotations/[id]/approved-pdf

ให้:

  • อ่าน artifact จาก database
  • validate organizationId
  • validate permission
  • ดึงไฟล์ผ่าน StorageProvider.getObject()
  • stream PDF กลับ
  • ไม่ใช้ public file path ตรง ๆ

เพิ่ม route generic ได้ถ้าเหมาะสม:

GET /api/crm/document-artifacts/[id]/download

Rules:

  • ห้าม expose storageKey ถ้าไม่จำเป็น
  • ห้ามให้ user เดา URL แล้วโหลดได้
  • permission guard ต้องอยู่ server route
  • approved PDF download ควร audit ได้ ถ้าทำไม่ใหญ่เกินไป

Scope I7: Artifact Lifecycle

Implement service:

src/features/foundation/document-artifact/server/service.ts

Functions:

createArtifact()
getArtifact()
getActiveArtifactForEntity()
lockArtifact()
voidArtifact()
deleteArtifactMetadataOnly()
downloadArtifact()

Rules:

  • locked artifact ห้ามแก้ storageKey/fileSize/checksum
  • voided artifact ยังเก็บ metadata
  • delete จริงยังไม่ต้องลบ object storage ถ้าไม่จำเป็น
  • approved PDF lifecycle ต้อง immutable-first

Scope I8: UI Integration

Quotation detail:

  • View Approved PDF ใช้ secure download route

  • ถ้ามี approved artifact แสดง:

    • fileName
    • generatedAt
    • generatedBy
    • checksum short
    • locked badge
  • ถ้ายังไม่มี artifact แต่มี old approvedPdfUrl จาก Task H ให้แสดง migration warning


Scope I9: Migration Compatibility

ถ้ามี approvedPdfUrl เดิมแบบ:

/generated/...

ให้ไม่ลบทิ้งทันที

เพิ่ม compatibility:

  • ถ้า approvedPdfUrl เป็น artifact reference → ใช้ artifact
  • ถ้าเป็น legacy local path → แสดง legacy warning
  • future migration script ค่อย migrate legacy file

Scope I10: Documentation

Update:

docs/implementation/technical-debt.md
docs/adr/0009-approved-document-storage-lifecycle.md

เพิ่ม:

Storage provider abstraction
Local vs S3/MinIO policy
Immutable artifact policy
Legacy public/generated migration
Download security model

Permissions

เพิ่มถ้ายังไม่มี:

crm.document_artifact.read
crm.document_artifact.download
crm.document_artifact.void

Audit

Audit required:

crm_document_artifact

Actions:

create
lock
void
download

ถ้า download audit ทำให้ระบบหนัก ให้ทำ optional toggle ได้


ห้ามทำ

Report
Dashboard KPI
Notification
Template Designer
Mapping Editor
Full object-storage admin UI
Bulk migration of legacy files

Output

หลังทำเสร็จ สรุป:

  1. Files Added
  2. Files Modified
  3. Storage Provider Added
  4. Artifact Schema Added
  5. Approved PDF Refactor
  6. Secure Download Added
  7. UI Updated
  8. Permissions Added
  9. Audit Integration
  10. Compatibility Notes
  11. Remaining Risks
  12. Task J Readiness

Definition of Done

Task I ผ่านเมื่อ:

✔ storage provider interface พร้อม

✔ local provider พร้อม

✔ s3/minio provider skeleton พร้อม

✔ approved PDF ไม่เขียนลง public/generated เป็น source of truth แล้ว

✔ approved artifact ถูกบันทึกใน crm_document_artifacts

✔ approved artifact locked/immutable

✔ approved PDF download ผ่าน secure route

✔ permission guard download/generate ครบ

✔ checksum ถูกบันทึก

✔ quotation UI แสดง artifact metadata

✔ legacy approvedPdfUrl compatibility มี

✔ technical debt / ADR updated

✔ ยังไม่ทำ report/dashboard/notification