Files
reader/server/Dockerfile
T
cnb.dQC5J6ohAGA ca68a03e5d merge: 解决 auto/init-server-scaffold-81b3 与 main 的冲突
两个分支各自演进:一侧引入格式注册表、H5 客户端与 Android 外壳,另一侧
引入漫画目录书与逐页取图。冲突集中在扫描器、metadata、library 路由和 web
客户端,逐项按「两边能力都要保留」的原则合并。

合并后修复了 4 个被冲突掩盖的真实缺陷:

- scanner 同时保留了两套目录书实现,类型检查与测试均无法通过;改为统一
  走格式注册表,目录书在文件遍历之前发现,其内容不再重复入库。
- /manifest 的 files 对漫画目录书返回的是文件夹路径,客户端无法逐页取图;
  改为由 manifest 派生真实页路径。
- 单个文件端点只认 book_files 行,漫画目录书的每一页都 404;改为目录书
  额外认领其目录下的路径,并修正路径归一化使穿越尝试仍被拒绝。
- 目录书判定只看图片数量,会把夹着一本书的目录整体吞掉;改为注册表驱动
  的判定,含书籍文件的目录始终是书架。

同时修复了 web 端两套测试运行器互相无法执行对方用例的问题(vitest 与
node:test 混跑会静默跳过),以及 web 侧 5 处类型错误。

Ref: #9
2026-09-16 18:04:11 +08:00

64 lines
2.4 KiB
Docker

# --- web client build stage ---------------------------------------------------
# The H5 client is built here and baked into the server image, because serving it
# from the same container is what keeps the deployment to a single command: the
# reader opens http://<nas-ip>:8080 and gets the app, with no second container and
# no CORS configuration to think about.
#
# `web` is optional. When the build context has no `web` directory the copy below
# fails, so the client stage is gated behind a build arg and the runtime stage
# tolerates an absent bundle. A server-only deployment stays possible.
FROM docker.cnb.cool/dockerhub/node:24-alpine AS web
WORKDIR /web
COPY web/package.json web/package-lock.json* ./
RUN npm ci
COPY web/ ./
RUN npm run build
# --- server build stage ------------------------------------------------------
FROM docker.cnb.cool/dockerhub/node:24-alpine AS build
WORKDIR /app
COPY server/package.json server/package-lock.json* ./
# Dev dependencies included: the TypeScript toolchain is needed to compile.
RUN npm ci --include=dev
COPY server/tsconfig.json server/tsconfig.build.json ./
COPY server/src ./src
RUN npm run build
# Strip dev dependencies for the runtime image.
RUN npm prune --omit=dev
# --- runtime stage -----------------------------------------------------------
FROM docker.cnb.cool/dockerhub/node:24-alpine AS runtime
WORKDIR /app
ENV NODE_ENV=production \
BOOKS_DIR=/books \
DATA_DIR=/data \
PORT=8080 \
WEB_DIR=/app/web
# Run as an unprivileged user. The library mount is read-only, so this user only
# ever needs write access to /data.
RUN addgroup -S reader && adduser -S reader -G reader \
&& mkdir -p /data /books /app/public && chown -R reader:reader /data /books /app/public
COPY --from=build --chown=reader:reader /app/node_modules ./node_modules
COPY --from=build --chown=reader:reader /app/dist ./dist
COPY --from=build --chown=reader:reader /app/package.json ./
# The built H5 client, served by the same process at `/`.
COPY --from=web --chown=reader:reader /web/dist ./web
USER reader
EXPOSE 8080
VOLUME ["/data"]
# The healthcheck hits the unauthenticated endpoint so an orchestrator can tell
# "server up but library unreadable" from "server down".
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD node -e "fetch('http://127.0.0.1:'+(process.env.PORT||8080)+'/api/v1/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
CMD ["node", "dist/main.js"]