# syntax=docker/dockerfile:1.7 # ===== 阶段 1:构建 ===== FROM node:22-alpine AS builder # pnpm 启用(与 boardgame.io 一致) ENV PNPM_HOME=/pnpm ENV PATH=$PNPM_HOME:$PATH RUN corepack enable WORKDIR /repo # 复制 workspace 配置 + lockfile COPY pnpm-workspace.yaml package.json pnpm-lock.yaml* .npmrc ./ COPY tsconfig.json ./ # 复制所有 package.json(pnpm 需要它们来做 workspace 解析) COPY packages/protocol/package.json ./packages/protocol/ COPY packages/engine/package.json ./packages/engine/ COPY packages/server/package.json ./packages/server/ COPY packages/ui/package.json ./packages/ui/ COPY apps/web/package.json ./apps/web/ COPY apps/desktop/package.json ./apps/desktop/ # 只装 server 需要的依赖(避免装前端 dev deps) RUN pnpm install --filter @tts-like/server... --frozen-lockfile # 复制源码 COPY packages/protocol ./packages/protocol COPY packages/engine ./packages/engine COPY packages/server ./packages/server # 构建 server RUN pnpm --filter @tts-like/server build # 生产依赖剥到独立目录 # 注意:pnpm v10 默认 inject-workspace-packages=true;我们的 server 通过 workspace:* 依赖 @tts-like/engine # 这里用 --legacy 保持 v9 行为(剥离到独立目录) RUN pnpm deploy --filter @tts-like/server --prod --legacy /prod/server # ===== 阶段 2:运行时 ===== FROM node:22-alpine AS runtime ENV NODE_ENV=production ENV PORT=8000 WORKDIR /app # 复制构建产物 + 生产依赖 COPY --from=builder /prod/server ./ COPY --from=builder /repo/packages/server/dist ./dist # boardgame.io 的 dist 必须物化(pnpm deploy --prod 留下的 symlink 在新 rootfs 上失效) # pnpm 隔离层里实际的包在 /repo/node_modules/.pnpm/boardgame.io@/node_modules/boardgame.io RUN BGIO_SRC=$(find /prod/server/node_modules/.pnpm -maxdepth 4 -name 'boardgame.io' -type d | head -n1) && \ if [ -z "$BGIO_SRC" ]; then \ echo "ERROR: boardgame.io not found in pnpm store" >&2; \ find /prod/server/node_modules -name 'boardgame.io' -maxdepth 5 >&2; \ exit 1; \ fi && \ echo "Materializing boardgame.io from $BGIO_SRC" && \ mkdir -p /app/node_modules/boardgame.io && \ cp -rL "$BGIO_SRC/." /app/node_modules/boardgame.io/ # 非 root 运行 RUN addgroup -g 1001 -S app && adduser -S app -u 1001 USER app EXPOSE 8000 # 健康检查 HEALTHCHECK --interval=30s --timeout=5s --retries=3 \ CMD wget -qO- http://localhost:8000/healthz || exit 1 CMD ["node", "dist/index.js"]