eCourt-note/docker/web.Dockerfile

50 lines
3.6 KiB
Text
Raw Normal View History

# ─── Stage 1: ติดตั้ง dependencies (npm ci รวม postinstall คัดลอก pdf.worker) ───
FROM node:24-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
# postinstall เขียน public/pdf.worker.min.mjs — ต้องมีโฟลเดอร์ public/ ก่อน ไม่งั้น npm ci ล้ม
RUN mkdir -p public && npm ci
# ─── Stage 2: build Next.js (output: standalone) ───
FROM node:24-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# ประกันว่า pdf.worker.min.mjs ตรงกับเวอร์ชัน pdfjs-dist ใน node_modules เสมอ
# (postinstall รันใน stage deps ซึ่งยังไม่มี public/ ของ repo)
RUN node -e "require('fs').copyFileSync('node_modules/pdfjs-dist/build/pdf.worker.min.mjs','public/pdf.worker.min.mjs')"
ENV NEXT_TELEMETRY_DISABLED=1
# db/index.ts throw ถ้าไม่มี DATABASE_URL ตอน next build เก็บ page data —
# ใส่ค่า dummy (postgres.js เชื่อมแบบ lazy ไม่มี query ตอน build) ตัวจริงฉีดตอน docker run
ENV DATABASE_URL=postgresql://placeholder:placeholder@127.0.0.1:5432/placeholder
RUN npm run build
# ─── Stage 3: runtime ตัวเบา (แค่ standalone + public/) ───
FROM node:24-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production \
NEXT_TELEMETRY_DISABLED=1 \
PORT=3000 \
HOSTNAME=0.0.0.0
RUN addgroup -S nodejs && adduser -S nextjs -G nodejs
# Next 14.2 รวม public/ (pdf.worker, ฟอนต์ Sarabun, samples PDF) ไว้ใน standalone แล้ว
# แต่ไม่รวม .next/static (JS chunks) — ต้องคัดลอกเพิ่ม ไม่งั้นหน้าเว็บไม่มี JS
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# migration: ไฟล์ SQL (drizzle-kit generate) + runner — รันตอน container start
COPY --from=builder --chown=nextjs:nodejs /app/drizzle ./drizzle
COPY --from=builder --chown=nextjs:nodejs /app/scripts ./scripts
# postgres.js โดน Next บีบ bundle เข้า server chunks — standalone node_modules ไม่มี package นี้
# แต่ migrate.mjs import มันตรง ๆ → คัดลอกจาก stage deps (v3 ไม่มี dependency ย่อย โฟลเดอร์เดียวพอ)
COPY --from=deps --chown=nextjs:nodejs /app/node_modules/postgres ./node_modules/postgres
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>
2026-09-16 15:57:06 +07:00
# สร้างโฟลเดอร์ uploads ให้ user nextjs เป็นเจ้าของ — named volume ที่ compose mount ทับ
# (เก็บไฟล์อัปโหลดข้าม redeploy) จะได้ ownership จาก image ตอน init:
# ไม่มี dir นี้ใน image = volume ถูกสร้างโดย root แล้ว action เขียนไฟล์ไม่ได้ (EACCES)
RUN mkdir -p public/uploads && chown nextjs:nodejs public/uploads
USER nextjs
EXPOSE 3000
# DATABASE_URL ฉีดตอนรันผ่าน env ของ container (อย่าฝังใน image)
# migrate สร้าง database ถ้ายังไม่มี + apply ไฟล์ที่ยังไม่ได้ apply (idempotent)
# seed-samples sync ตาราง files กับ public/samples/ ที่ bake อยู่ใน image (idempotent)
CMD ["sh", "-c", "node scripts/migrate.mjs && node scripts/seed-samples.mjs && exec node server.js"]