เพิ่มทางเข้าเอกสารสำหรับ e-Court ที่ไม่มี URL ให้ดึง — POST /api/note/files/stage (x-api-key DOCVER_API_KEY) ส่งเนื้อไฟล์ PDF/รูปเป็น base64 ระบบเก็บใน RAM staging (TTL sliding 8 ชม. + LRU + cap 100MB/ไฟล์ 256MB รวม — ห้ามเขียน disk/DB/MinIO แม้แต่ไบต์เดียวตามโจทย์) ส่วน annotation/bookmark เก็บถาวรตามระบบเดิม: - lib/staged-docs.ts: Map บน globalThis + sentinel "staged:<uuid>.<ext>" ใน pdf_url (ext จาก magic bytes — ไม่ต้อง migration) + hasStagedDoc แยกจาก getStagedDoc - lib/ecourt-fid.ts: external_id เก็บ HMAC-SHA256(pepper, fid) แทน fid ดิบ (env FID_HASH_PEPPER fail-closed) + แปลง row เก่าด้วย npm run migrate:ecourt-fid - route stage: อ่าน body แบบ stream นับไบต์ + idle 60 วิ/total 10 นาที = 408 + concurrency default 1 = 429 + base64 strict + magic bytes กำหนด ext เอง - registerStagedFile: ลำดับกัน race 23505 — stage ใต้ id ก่อน insert โดนชนแล้ว re-stage ใต้ id ผู้ชนะ + try/finally คืน RAM ผู้แพ้ทุกทางออก - ทางอ่าน 4 จุด: /api/note/pdf + export route (410 gate ไทย), resolvePublicPdfPath guard PdfPathError, PDFViewer โหลดผ่าน route + objectURL (gate ครบทุกชนิดไฟล์) - session route (D5): ลิงก์ fid-only ตัดสิน staging ตั้งแต่แลก token — มี bytes ได้ fileId / หมดอายุ fileError "พ้นช่วงจัดเตรียม" / token ออกทุกกรณี - Swagger UI: endpoint ใหม่ + คู่มือ retry (408/429/5xx ยิงซ้ำได้ stage ซ้ำปลอดภัย) - MockStageForm บน /note/dev ทดสอบ 3 ขั้นในหน้าเดียว (ต้องถือ API key จริง) Verify ครบตาม docs/PLAN-STAGED.md: tsc สะอาด + vitest 59 เคสใหม่ผ่าน + E2E browser จริง (textLayer ไทยเรียงถูก / export ฝัง annotation / restart ได้ fileError ตั้งแต่ bootstrap / RAM 100MB: peak WS +580MB commit จริง +92MB) — ผ่าน code review รอบ 7 แก้ครบทุก finding Co-Authored-By: Claude Code <noreply@anthropic.com>
59 lines
2.1 KiB
JSON
59 lines
2.1 KiB
JSON
{
|
|
"name": "judes-note",
|
|
"version": "0.1.0",
|
|
"private": true,
|
|
"scripts": {
|
|
"dev": "npm run db:prepare && next dev",
|
|
"dev:server": "next dev",
|
|
"build:worker": "node scripts/build-worker.mjs",
|
|
"prebuild": "npm run build:worker",
|
|
"build": "next build",
|
|
"test": "vitest run",
|
|
"start": "next start",
|
|
"seed-samples": "node --env-file=.env scripts/seed-samples.mjs",
|
|
"migrate:ecourt-fid": "node --env-file=.env scripts/migrate-ecourt-fid-hash.mjs",
|
|
"db:prepare": "docker compose up -d --wait && node --env-file=.env scripts/migrate.mjs && node --env-file=.env scripts/seed-samples.mjs",
|
|
"db:reset": "docker compose down && docker volume rm judes-note-db-data judes-note-minio-data && npm run db:prepare",
|
|
"addin:install": "node scripts/install-wordaddin.mjs",
|
|
"postinstall": "node scripts/patch-pdf-worker.mjs && node scripts/copy-swagger-ui.mjs && node scripts/fetch-office-js.mjs",
|
|
"dev:https": "next dev --experimental-https",
|
|
"dev:https:cert": "next dev --experimental-https --experimental-https-key certs/localhost.key --experimental-https-cert certs/localhost.crt"
|
|
},
|
|
"dependencies": {
|
|
"@pdf-lib/fontkit": "^1.1.1",
|
|
"@xmldom/xmldom": "^0.9.12",
|
|
"docx-templates": "^4.15.0",
|
|
"drizzle-orm": "^0.45.2",
|
|
"lucide-react": "^1.42.0",
|
|
"minio": "^8.0.7",
|
|
"next": "^14.2.35",
|
|
"pdf-lib": "^1.17.1",
|
|
"pdfjs-dist": "^4.10.38",
|
|
"piscina": "^5.3.2",
|
|
"pizzip": "^3.3.0",
|
|
"pngjs": "^7.0.0",
|
|
"postgres": "^3.4.9",
|
|
"react": "^18.3.1",
|
|
"react-dom": "^18.3.1",
|
|
"swagger-ui-dist": "^5.33.1",
|
|
"utif2": "^4.1.0",
|
|
"xpath": "^0.0.34"
|
|
},
|
|
"devDependencies": {
|
|
"@types/node": "^26.6.5",
|
|
"@types/office-js": "^1.0.612",
|
|
"@types/pngjs": "^6.0.5",
|
|
"@types/react": "^18.3.31",
|
|
"@types/react-dom": "^18.3.7",
|
|
"autoprefixer": "^10.5.5",
|
|
"drizzle-kit": "^0.31.10",
|
|
"esbuild": "^0.28.2",
|
|
"jsdom": "^29.1.1",
|
|
"office-addin-debugging": "^7.0.1",
|
|
"office-addin-dev-settings": "^4.0.1",
|
|
"postcss": "^8.5.28",
|
|
"tailwindcss": "^3.4.19",
|
|
"typescript": "^5.9.3",
|
|
"vitest": "^5.0.0"
|
|
}
|
|
}
|