fix(deploy): อัปโหลด/เปิดไฟล์ใน Docker ไม่ได้ — 3 จุดรวมกัน

- app/uploads/[name]/route.ts: Next production (next start/standalone) อ่านรายชื่อ
  public/ เก็บใน Set ครั้งเดียวตอน startup เท่านั้น ไฟล์ที่ uploadFile เขียน
  ตอน runtime GET /uploads/* แล้ว 404 (dev ไม่ผ่าน branch นี้ = local ผ่าน
  Docker พัง) → เสิร์ฟผ่าน route handler แทน: path guard lib/server-pdf +
  whitelist ext + content-type จาก utils/file-kind + cache immutable
- drizzle/0001: bookmarks.user_name (N6) + backfill ตาม PLAN-BOOKMARK.md —
  image ใช้ migrate.mjs จากไฟล์ SQL ไม่ใช่ drizzle push ของ dev ทำให้ schema
  ตกรุ่นจนหน้า annotate 500 ทั้งหน้า; แก้ schema.ts ครั้งต่อไปต้อง drizzle-kit
  generate ทุกครั้ง (กติกาใหม่ใน .claude/rules/data-layer.md)
- docker/web.Dockerfile: mkdir public/uploads + chown nextjs — volume ที่
  compose mount ทับได้ ownership จาก image (ไม่งั้น root เป็นเจ้าของ = EACCES)
  + .dockerignore กัน public/uploads ของเครื่อง dev ติดไปใน image
- docker-compose.test.yml: compose ทดสอบบนเครื่อง (port 6704 + volume uploads)

verify บน Docker จริง: migration apply บน DB เดิมได้, annotate 500→200,
Playwright อัปโหลด JPG+PDF→เปิด viewer ได้ 7/7, tsc ผ่าน, vitest 120/120

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Your Name 2026-09-16 15:57:06 +07:00
parent 21b9958600
commit a4cac15e42
9 changed files with 571 additions and 1 deletions

View file

@ -0,0 +1,11 @@
ALTER TABLE "bookmarks" ADD COLUMN IF NOT EXISTS "user_name" text DEFAULT '' NOT NULL;--> statement-breakpoint
-- N6 backfill ชื่อผู้บันทึกของ bookmark เดิมจาก annotations ตามแผน PLAN-BOOKMARK.md D2
-- (เดิมรันมือครั้งเดียวบน dev — ฝั่ง deployment ใช้ migrate.mjs จึงต้องมาในไฟล์นี้;
-- WHERE user_name = '' ทำให้รันซ้ำปลอดภัย แถวที่หาชื่อไม่ได้คง '' → UI แสดง "ไม่ทราบชื่อ")
UPDATE "bookmarks" b
SET user_name = COALESCE((
SELECT a.user_name FROM annotations a
WHERE a.user_id = b.user_id AND a.user_name <> ''
ORDER BY a.created_at DESC LIMIT 1
), '')
WHERE b.user_name = '';