mirror of
https://github.com/hectorqin/reader.git
synced 2026-11-12 05:50:54 +08:00
按产品设计 v0.2 落地服务端,覆盖第一版范围中的扫描、元数据、权限与同步。 核心决策: - 书籍主键用 dc:identifier + 内容哈希,不用文件路径。 重命名/移动不丢进度,副本自动合并,重新编码的文件保持独立。 - 库目录只读挂载,写操作全部落到 DATA_DIR。 启动时断言 DATA_DIR 不在 BOOKS_DIR 内,路径解析统一走 resolveInside 拦截逃逸,扫描不跟随符号链接。 - 元数据优先级:手动修改 > 内嵌 > provider > 文件名解析。 手动修改独立成表,扫描永远碰不到,可单字段撤销。 - 中文文件名解析采取保守策略,无证据时不猜测作者方向。 - 离线合并用 LWW,笔记删除写墓碑,批次单事务。 - 无原生编译依赖:node:sqlite 与 scrypt 均为内置模块。 交付: - Fastify + TypeScript 服务端,Docker 单容器 - 27 个自动化测试用例全部通过 - AGPL-3.0,README 做到一条命令跑起来 - CNB 流水线:typecheck / test / build
41 lines
1.5 KiB
YAML
41 lines
1.5 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:
|
|
# build: ./server
|
|
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
|