feat(docver): add document versioning API (docx+pdf in MinIO)

Stand-alone API group at /api/documents/{docId}/versions — one document
per docId (unique), each version stores .docx + optional .pdf as MinIO
objects with uploader userId/userName/createdAt (no auth, like
api/template; callers identify themselves per docs/PLAN-DOCVER.md).

- POST versions (multipart): validates fields/magic bytes/100MB cap,
  uploads to MinIO first, cleans up objects if the DB insert fails;
  returns {hasPdf, versionId, versionNumber} with server-authoritative
  numbering (tx: upsert + SELECT FOR UPDATE, safe under parallel calls)
- GET versions: DocumentVersionDTO[] newest-first (404 for unknown docId)
- GET versions/{versionId}/download-link?format=docx|pdf: presigned URL
  (docId+versionId matched together; pdf-missing = 404)
- tables documents/document_versions + migration drizzle/0005
- docker-compose.yml: judes-note-db + judes-note-minio (quay.io image,
  host ports 19000/19001, POSTGRES_DB ecourt-note, pinned volume names)
- lib/openapi.ts: documents tag + paths for Swagger UI
- middleware: exempt api/documents from login redirect
- unit tests tests/lib/document-versioning.test.ts (23 cases)

wordaddin untouched — swap the mock later via NEXT_PUBLIC_ADDIN_API_BASE_URL.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Suphonchai Phoonsawat 2026-10-20 10:52:35 +07:00
parent 5917c0532c
commit 765946f0d5
19 changed files with 2711 additions and 185 deletions

View file

@ -23,7 +23,9 @@ export function middleware(req: NextRequest) {
// ยกเว้น: หน้า login เอง, API/SSE/export (อยู่ใต้ api/note/**), API template
// (api/template/** — เปิดกว้างให้ทางศาล/ผู้ทดสอบภายนอกยิงได้เลย ไม่แตะ DB
// ไม่มีข้อมูลรับ), หน้า Swagger UI (api/swagger — document ของทุกกลุ่ม),
// ไม่มีข้อมูลรับ), API เอกสาร versioning (api/documents/** — กลุ่ม docver
// stand-alone ของ wordaddin: ไม่มี session ผู้เรียกบอก userId/userName เอง —
// docs/PLAN-DOCVER.md), หน้า Swagger UI (api/swagger — document ของทุกกลุ่ม),
// taskpane ของ wordaddin (wordaddin/** — รันใน webview ของ Word ไม่มี cookie
// oacCase และมี auth ของตัวเองแยกจากแอป note), ไฟล์อัปโหลด, Next internal,
// และ static ใน public/note/ (pdf.worker, samples, asset Swagger UI)
@ -35,6 +37,6 @@ export function middleware(req: NextRequest) {
// ยังถูกคุมอยู่ดีเพราะ root page ส่งต่อให้ /note ซึ่ง middleware จับตามปกติ
export const config = {
matcher: [
"/((?!note/login|api/note|api/template|api/swagger|wordaddin|note/uploads|note/samples|note/swagger-ui|note/pdf\\.worker\\.min\\.mjs|_next|fonts|favicon\\.ico).+)",
"/((?!note/login|api/note|api/template|api/documents|api/swagger|wordaddin|note/uploads|note/samples|note/swagger-ui|note/pdf\\.worker\\.min\\.mjs|_next|fonts|favicon\\.ico).+)",
],
};