Adds the multiplayer functionality the user requested:
- LobbyRoomList UI component (create / list / join via boardgame.io's
built-in Lobby REST API). Credentials are server-issued; no more hardcoded
p0/p1 in OnlineConfig.
- LobbyClient re-export wrapper in @tts-like/engine (typed factory
createLobbyClient).
- CORS origin default: dev-friendly regex list (boardgame.io's
isOriginAllowed treats literal '*' as exact-match, which silently blocks
everything; we now use RegExp matching localhost + Tauri schemes).
- War shuffle accepts an injected random fn so unit tests are deterministic;
fixed a tie-handling bug where piles were cleared without redistributing
cards (now piles stay on tie for a "war" round).
- Engine unit tests (vitest + Local master for 2-client sync):
packages/engine/src/games/{drag-test,war,mill}.test.ts (34 cases)
- Server REST tests (vitest + supertest, port 0 random):
packages/server/src/server.test.ts (9 cases)
- Playwright E2E (apps/web/e2e/*): 3 specs covering create → join →
sync for War / DragTest / Mill across two browser contexts.
- Dockerfile fix: pnpm deploy --prod leaves boardgame.io as a symlink to
.pnpm/boardgame.io@…; copy -rL materialises the dist so the runtime
image can resolve boardgame.io/server.
- Root scripts: dev:server, test:e2e, verify (ts + test + e2e).
76 lines
2.5 KiB
Docker
76 lines
2.5 KiB
Docker
# 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@<version>/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"]
|