Files
reader/docker-compose.yml
T
CodeBuddy 8e55dc23a3 fix(web,build): 无封面的书不再显示破图;镜像内置 H5 客户端
- 没有封面的书(大部分 txt、不少 pdf)之前是一个 <img> 不设 src,
  浏览器渲染成破图图标 —— 那读起来像"这个书库坏了",而事实是"这本书没有
  封面",读者无从分辨。改为显示书名首字(相当于书脊),并在封面加载失败时
  也回落到同一占位。
- Dockerfile 加 web 构建阶段,`WEB_DIR=/app/public`,镜像里自带完整阅读器。
  构建失败必须让镜像失败:一个没有客户端的服务端会看起来部署成功,
  实际打开是落地页而不是阅读器。
- CI 拆成 server/web 两条,失败模式不同(读不了书 vs 白屏)。
- compose 构建上下文改为仓库根目录(从 ./server 构建会得到一个没有客户端的
  服务端)。

已按 Dockerfile 的三阶段在本机复现验证:web 构建产物 + 服务端 dist +
prune --omit=dev 后的 node_modules,用生产布局起服务,浏览器实测四个格式
全部正常、零控制台错误。
2026-09-16 15:48:52 +08:00

46 lines
1.7 KiB
YAML

# One command to get running:
#
# docker compose up -d
#
# Then open http://<your-host>:8080 and register the first account, which
# automatically becomes the instance admin.
services:
reader:
image: cnb.cool/hectorqin/reader:latest
# To build from source instead of pulling the published image. The build
# context is the repository root, because the image now contains the H5
# client as well as the API — building from `./server` alone would produce a
# server with no reading client.
build:
context: .
dockerfile: server/Dockerfile
container_name: reader
restart: unless-stopped
ports:
- "8080:8080"
environment:
# Polling intervals in seconds. Set to 0 to disable (a manual scan is
# still available from the admin API).
SCAN_INTERVAL: "1800"
WATCH_INTERVAL: "60"
LOG_LEVEL: "info"
# Set this to the address you actually reach the server on, so download
# links work behind a reverse proxy.
# PUBLIC_URL: "https://reader.example.com"
# Uncomment to allow family members to self-register instead of the admin
# creating their accounts:
# ALLOW_REGISTRATION: "true"
volumes:
# --- your books, mounted READ-ONLY ---
# The server NEVER writes to this directory. Replace the left-hand side
# with the path to your own library.
- /path/to/your/books:/books:ro
# --- everything the server writes lives here ---
# SQLite database, cover cache, scan journal.
- ./data:/data
healthcheck:
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:8080/api/v1/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 30s
timeout: 5s
retries: 3