Dockerfile
Rama main · réplica de lectura del repositorio original.
# syntax=docker/dockerfile:1.7
# ── Stage 1: 安裝相依套件 ──
FROM node:24-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
# ── Stage 2: 建置 Next.js standalone ──
FROM node:24-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
# Build-time module analysis may load lib/db.ts; direct generated secrets to a throwaway path
# so VAPID/JWT/API keys never get embedded in the standalone image. Runner sets the volume path below.
ENV ENV_PATH=/tmp/assetpilot-build-env/.env
RUN npm run build
# ── Stage 3: 執行階段 ──
FROM node:24-alpine AS runner
WORKDIR /app
# 修補 base image 落後的 OS 套件(如 OpenSSL libcrypto3/libssl3 CVE)。
# Trivy 設 ignore-unfixed=true,僅會擋「上游已有修補」的弱點,故升級到 alpine repo 最新修補版即可通過掃描。
# CI 用 cache-from/to: type=gha 快取 layer;若不加 cache-bust,只要 base image digest
# 沒變,這一層會被快取命中而完全不會重新執行,導致即使 Alpine repo 已有新修補版,
# 建出來的映像仍停留在快取當下的舊版套件(曾實際造成 Trivy 掃到已修補的 CVE 卻過不了)。
# ARG 值由 workflow 每次 build 帶入唯一值(如 run id),強制每次都重新 apk upgrade。
ARG APK_UPGRADE_CACHE_BUST=unset
RUN echo "apk-upgrade-cache-bust:${APK_UPGRADE_CACHE_BUST}" && apk upgrade --no-cache
# 移除 base image 內建的 npm。standalone 入口僅需 `node server.js`,執行期完全用不到 npm,
# 而 npm 自帶的 vendored undici(node:24-alpine 為 6.25.0)帶有 CVE-2026-12151(DoS),
# 上游 npm 尚未隨基底映像更新。移除可同時縮小攻擊面並通過 Trivy 容器掃描。
RUN rm -rf /usr/local/lib/node_modules/npm \
/usr/local/bin/npm \
/usr/local/bin/npx
COPY --from=builder --chown=nextjs:nodejs /app/build/standalone ./
ENV PORT=3000
ENV HOSTNAME=0.0.0.0
ENV NEXT_TELEMETRY_DISABLED=1
ENV ENV_PATH=/app/data/.env
ENV SSL_PATH=/app/data/SSL
ENV JWT_EXPIRES=7d
ENV APP_JWT_EXPIRES=90d
ENV GOOGLE_CLIENT_ID=
# 建立非 root 使用者執行 Next.js(best practice)
RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 nextjs
# Next.js standalone 輸出(內含最小化 node_modules 與 server.js 入口)
# (使用 distDir='build' 輸出,因此從 /app/build/ 拷貝已在上方處理)
# public 目錄(favicon、logo 等)
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
# Next.js 靜態資產(含編譯後 CSS/JS)
COPY --from=builder --chown=nextjs:nodejs /app/build/static ./build/static
# lib/ 透過伺服器執行期 dynamic import 使用,部分路徑未被 Next.js trace,需手動複製
COPY --from=builder --chown=nextjs:nodejs /app/lib ./lib
# 手動維運指令需在 runner 映像內可用
COPY --from=builder --chown=nextjs:nodejs /app/tools ./tools
# /api/changelog 以 readFileSync(process.cwd()/changelog.json) 動態讀檔,standalone
# tracing 無法發現此執行期路徑;私有 repo 又讓 raw.githubusercontent.com fallback 失效
# (404),故須手動複製,否則 App 版本資訊頁拿到空清單、無法顯示更新內容。
COPY --from=builder --chown=nextjs:nodejs /app/changelog.json ./changelog.json
# PostgreSQL runtime uses worker_threads require() to keep pg out of the edge/instrumentation bundle,
# so Next.js standalone tracing cannot discover these packages automatically.
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg ./node_modules/pg
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg-cloudflare ./node_modules/pg-cloudflare
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg-connection-string ./node_modules/pg-connection-string
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg-int8 ./node_modules/pg-int8
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg-pool ./node_modules/pg-pool
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg-protocol ./node_modules/pg-protocol
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pg-types ./node_modules/pg-types
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/pgpass ./node_modules/pgpass
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/postgres-array ./node_modules/postgres-array
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/postgres-bytea ./node_modules/postgres-bytea
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/postgres-date ./node_modules/postgres-date
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/postgres-interval ./node_modules/postgres-interval
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/split2 ./node_modules/split2
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/xtend ./node_modules/xtend
# web-push(Web Push 推播,issue #257)以動態 import 延遲載入,Next.js standalone
# tracing 抓不到這條路徑,需手動複製本體與完整相依閉包(17 個套件,含
# http_ece 加密實作、jws/jwa/ecdsa-sig-formatter 為 VAPID 簽章、
# https-proxy-agent 為代理支援)。漏掉任何一個都會在發送時才以 MODULE_NOT_FOUND 失敗。
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/web-push ./node_modules/web-push
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/http_ece ./node_modules/http_ece
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/jws ./node_modules/jws
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/jwa ./node_modules/jwa
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/ecdsa-sig-formatter ./node_modules/ecdsa-sig-formatter
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/buffer-equal-constant-time ./node_modules/buffer-equal-constant-time
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/safe-buffer ./node_modules/safe-buffer
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/asn1.js ./node_modules/asn1.js
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/bn.js ./node_modules/bn.js
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/inherits ./node_modules/inherits
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/minimalistic-assert ./node_modules/minimalistic-assert
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/safer-buffer ./node_modules/safer-buffer
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/https-proxy-agent ./node_modules/https-proxy-agent
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/agent-base ./node_modules/agent-base
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/debug ./node_modules/debug
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/ms ./node_modules/ms
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/minimist ./node_modules/minimist
# sharp(交易照片壓縮)為 serverExternalPackages,於 runtime require,需手動複製其
# 本體與相依:平台專屬原生套件(@img/sharp-linuxmusl-*、libvips、@img/colour)、
# detect-libc、semver。
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/sharp ./node_modules/sharp
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/@img ./node_modules/@img
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/detect-libc ./node_modules/detect-libc
COPY --from=builder --chown=nextjs:nodejs /app/node_modules/semver ./node_modules/semver
# 持久化資料目錄(.env、SSL 憑證)
RUN mkdir -p /app/data/SSL/Origin\ Certificates \
&& chown -R nextjs:nodejs /app/data
VOLUME /app/data
USER nextjs
EXPOSE 3000
# Next.js standalone 入口(.next/standalone/server.js)
CMD ["node", "server.js"]