8.7 KiB
Changelog / 更新日志
All notable non-WebUI user-visible changes should be documented in this file, newest version on top. 本文件记录所有非 WebUI 的重要用户可见变更,最新版本在最上方。
The format loosely follows Keep a Changelog and can be adapted to the team's habits. 本文档参考了 Keep a Changelog 的思路,也可以根据团队习惯调整。
[Unreleased]
Fixed / 修复
- Rate limiter
IncrWindowuses atomic Lua script for INCR+EXPIRE, preventing permanent IP lockout on EXPIRE failure (B1). - 限流器
IncrWindow改用 Lua 脚本原子执行 INCR+EXPIRE,防止 EXPIRE 失败导致 IP 永久锁定(B1)。 - Distributed lock
Lockhandlesrand.Readfailure by degrading to no-lock instead of using a zero token (B2). - 分布式锁
Lock在rand.Read失败时降级为无锁模式,而非使用全零 token(B2)。 - Lock unlock uses
context.WithoutCancelto survive caller cancellation (B3). - 锁的解锁改用
context.WithoutCancel,在调用方上下文取消后仍能正常释放(B3)。 - Upload part writes to
.tmpthen renames, preventing truncated parts from being reported as received (B4). - 分片上传先写
.tmp再 rename,防止崩溃截断的分片被误报为已接收(B4)。 DeleteUserlast-admin check is now transactional, eliminating TOCTOU race (B5).DeleteUser的最后管理员检查改为事务内执行,消除 TOCTOU 竞态(B5)。- Single-file upload
io.Copyerrors other thanMaxBytesErrorreturn 500 instead of 413 (B6). - 单文件上传中非
MaxBytesError的io.Copy错误返回 500 而非 413(B6)。 /auth/medistinguishesno rows(401) from database errors (503) (B7)./auth/me区分无记录(401)和数据库错误(503)(B7)。- Library creation rejects reserved names (
cache,.uploads) with400 reserved_name(B8). - 创建书库时拒绝保留名(
cache、.uploads),返回400 reserved_name(B8)。 - Scanner lock auto-renews every TTL/2 during long scans; per-library single-flight prevents concurrent scans (B9).
- 扫描锁每 TTL/2 自动续期;库级 single-flight 防止并发扫描(B9)。
- Scanner
SetBookStateerrors are now logged instead of silently discarded (B10). - 扫描器
SetBookState的错误现在会记录日志而非静默丢弃(B10)。 - Cover write errors fully checked; orphan
.tmpfiles cleaned only on failure (B11). - 封面写入错误全部检查;孤儿
.tmp文件仅在失败路径清理(B11)。 - Upload handler retries on
O_EXCLcollision for concurrent same-name uploads (B12). - 上传处理器在
O_EXCL冲突时重试,处理并发同名上传(B12)。 - Bookmark methods check
errbeforeRowsAffectedto avoid invalid reads on query failure (B13). - 书签方法先检查
err再读RowsAffected,避免查询失败时的无效读取(B13)。
Changed / 变更
- Add ordered migration system with
schema_migrationstracking and pg advisory lock for safe multi-replica schema evolution. Existing databases are auto-baselined. To change the schema, add a newNNNN_description.sqlfile underbackend/internal/db/migrations/; never modify an already-applied file. No down migrations — rollback via backup, fix-forward. - 新增有序迁移系统,通过
schema_migrations表和 pg advisory lock 实现安全的多副本 schema 演进,已有数据库自动基线化。修改 schema 时在backend/internal/db/migrations/下新增NNNN_description.sql,已应用的文件不可修改。不支持 down 迁移——回滚靠备份,fix-forward。
Fixed / 修复
- Serve goroutine
log.Fatalfreplaced with channel-based shutdown to preserve graceful teardown (B14). - 服务 goroutine 中的
log.Fatalf改为 channel 通知方式,确保优雅关停流程不被绕过(B14)。 DATABASE_URLis now validated at startup (required, parseable); emptyREDIS_URLlogs a clear "redis disabled" message (B15).DATABASE_URL在启动时校验(必填、可解析);空REDIS_URL记录明确的 "redis disabled" 日志(B15)。scripts/smoke.shaligned with current API contract, removed ignoredroot_pathfield (B17).scripts/smoke.sh对齐当前 API 契约,移除被忽略的root_path字段(B17)。
Added / 新增
- CI workflow (
.github/workflows/ci.yml) compatible with both GitHub Actions and Gitea Actions. - CI 工作流(
.github/workflows/ci.yml),兼容 GitHub Actions 和 Gitea Actions。
Added / 新增
-
API: per-user bookmarks —
GET/POST /api/books/:id/bookmarks(locator+percent snapshot with optional ≤500-char note; list ordered by percent) andPATCH/DELETE /api/bookmarks/:id; not-yours uniformly 404. Newbookmarkstable keyed like progress, cleaned up with the user (no cascade on book delete, same precedent). -
API:新增按用户隔离的书签——
GET/POST /api/books/:id/bookmarks(存当前 locator+percent,备注可选、≤500 字,列表按进度升序)与PATCH/DELETE /api/bookmarks/:id;不属于自己的一律 404。新bookmarks表与进度同款定位键,随用户删除而清(删书不级联,沿用既有先例)。 -
API: CBZ page indexing now skips macOS packaging junk (
__MACOSX/…and._*AppleDouble files), which used to land in the page list as ~163-byte black "pages";GET /api/books/:id/pagesadditionally returnschapters:[{title,start}]derived from the archive's folder structure (e.g. 第1話…), so per-folder comics expose their real organization. -
API:CBZ 页索引现会跳过 macOS 打包垃圾(
__MACOSX/…与._*资源叉文件),此前它们以 ~163 字节黑页混入页列表;GET /api/books/:id/pages新增chapters:[{title,start}],按压缩包内目录结构(如 第1話…)给出真实章节。 -
API: resumable chunked upload protocol for large files —
POST /api/libraries/:id/upload/init(fingerprint-derived deterministicuploadId, rejects totals overUPLOAD_MAX_MBwith413 too_large),PUT /api/uploads/:uid/parts/:index(parts ≤ 32MB),GET /api/uploads/:uid(received parts, for resume),POST /api/uploads/:uid/complete(assemble + atomic land, same path contract as single-POST upload). Sessions persist underBOOKS_DIR/.uploads/with 24h opportunistic sweep.UPLOAD_MAX_MBis now wired through both compose stacks/.env;.env.examplesets 2048 and dropsNGINX_CLIENT_MAX_BODY_SIZEto 32m (nginx only ever sees one chunk). -
API: 新增大文件可续传分片上传协议——
POST /api/libraries/:id/upload/init(按指纹派生确定性uploadId,总量超UPLOAD_MAX_MB返回413 too_large)、PUT /api/uploads/:uid/parts/:index(单片 ≤32MB)、GET /api/uploads/:uid(查询已传分片以续传)、POST /api/uploads/:uid/complete(拼接后原子落盘,返回与单发上传一致的path)。会话存于BOOKS_DIR/.uploads/,超 24h 顺手清理。UPLOAD_MAX_MB已接入两份 compose/.env;.env.example调至 2048 并将NGINX_CLIENT_MAX_BODY_SIZE降为 32m(nginx 只见单个分片)。
Changed / 变更
-
Scanner: an image-list-less archive (
.zip/.cbzwith no page images — video packs, document dumps) is now recorded asstate=error("no images in archive") instead of registering as an empty CBZ with a blank reader. -
扫描器:不含任何图片条目的
.zip/.cbz(视频包、文档包)现记录为state=error("no images in archive"),不再注册成空 CBZ 留下一个白板阅读器。 -
API: upload over
UPLOAD_MAX_MBnow returns413 too_largewith the limit in the message; previously the size abort was misreported as400 bad_request "multipart field 'file' required". -
API:超过
UPLOAD_MAX_MB的上传现在返回413 too_large并在消息中带上限额;此前体积超限被误报为400 bad_request "multipart field 'file' required"。 -
API:
POST /api/librariesnow takes only{name};root_pathis generated server-side asBOOKS_DIR/<sanitized name>(no client-supplied paths, validated at creation). -
API:
POST /api/libraries只需{name};root_path由服务端生成为BOOKS_DIR/<清洗后的库名>(不再接受客户端指定路径,创建时即校验)。 -
Repo structure conformed to
AGENTS.md:web/renamed tofrontend/; backend HTTP layer moved frominternal/apitocmd/webui/{api,handlers}(cmd/server→cmd/webui); README/CHANGELOGs relocated underdocs/(README_zh.mdadded as Chinese mirror); module-level.gitignores added (backend/,deploy/); stray rootlibrary/removed (book files live indeploy/api/storage/); debug binaries untracked. -
Docs:
docs/README.mdis now the English primary; previous Chinese README mirrored todocs/README_zh.md.