feat: v0.3.8–v0.3.14 工作区汇总(依赖升级、整理来源、手动入库、原地入库等)

生产 v0.3.8~v0.3.13 均由 8013196 加工作区修改构建部署,此前未提交;本提交补齐源码,
并含未部署的 v0.3.14 原地入库。中间各版本源码状态未保存,无法逐版拆分,详见
docs/plan/17-full-upgrade.md 与 docs/plan/18-organize-release.md。

- v0.3.8 全面升级:Deno workspace 依赖、Router 8 / Vite 8、camofox 1.18.0 / Node 24、
  PostgreSQL 18.6、备份客户端 18.6、演示 qB 5.2.4
- v0.3.9 媒体整理:分类规则、整理来源与目录快照(0059)、文件管理整理入口
- v0.3.10 手动下载与入库:独立手动下载、详情页资源下载、TMDB / Local 元数据兜底
  (metadata.resolve_organize)、正常多版本 / 一次性洗版(0060)
- v0.3.11 搜索详情与桌面黑边修复
- v0.3.12 列表分页、搜索历史、首页概况
- v0.3.13 统一整理弹窗与队列
- v0.3.14 原地入库(0061,未部署)

同批审计修复:
- 刮削入队:确认原记录存在后才删重放产生的新记录;写回 run 关联失败时保持 queued,
  由处理器补关联(对齐 MP 先登记、只在入队失败时记失败)
- 识别升级链只在结果更确定时采用,不再用 no_match 顶替原有歧义候选
- bluray_dir 资产以原盘目录本身为刮削根(MP 同做法)
- metadata.resolve_organize 登记到入库排障 Skill 与能力地图
- db/src/types.ts 按迁移重新生成;contracts 整理动作枚举顺序与数据库对齐
- 测试:原地入库用例改为直接查记录表;domain 测试模板补 version
- 前端:通知铃铛弹层与用户菜单统一样式;站点卡片共用卡头、底栏贴底、分页间距

依赖升级证据只提交摘要文件,registry-snapshot.json 移至 .local/(SHA-256 记于文档)。

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-10-02 15:47:17 +08:00
1 parent 8013196e3f
commit ceb66f8c77
265 files changed
+17751 -2483

No files matched your search

+45 -27
View File
@@ -1,18 +1,18 @@
# 部署运行手册(W4-B)
形态:**除 PostgreSQL 外全部在一个容器里**。镜像 `uhd-media-tool` 内一个 `deno compile` 出的二进制 `umt-server`,
形态:**核心应用在一个容器里,PostgreSQL 与浏览器分别部署**。镜像 `uhd-media-tool` 内一个 `deno compile` 出的二进制 `umt-server`,
容器里同时跑 api、worker(io 组)、worker:agent(Agent 会话)三个进程,api 同端口托管 web 静态产物(`WEB_ROOT=/app/public`),对外只有 **4380** 一个端口。
compose 有三个服务:`app` + `postgres` + `camofox`(浏览器服务,只在内网,不映射端口;可关掉或换成自有实例,见 §2.1)。
compose 默认有三个服务:`app` + `postgres` + `camofox`(浏览器服务,只在内网,不映射端口;可关掉或换成自有实例,见 §2.1);`tls` profile 可额外启用 Caddy 提供 HTTPS。
## 1. 构建镜像
```sh
docker build -t uhd-media-tool:latest . # 或 docker compose build
APP_VERSION=v0.1.0 docker compose build # 版本号写进 web 的 version.json
docker build -t uhd-media-tool:latest . # 或 docker compose --env-file .env.deploy build
APP_VERSION=v0.3.11 docker compose --env-file .env.deploy build app # 示例:发布时填写对应版本,写进 web 的 version.json
```
- 多阶段:`denoland/deno:2.9.7`(装依赖 → web 构建 → server 编译)→ `debian:trixie-slim` 运行镜像。
- 运行镜像只额外装 `gosu`(降权)与 `postgresql-client`(备份/恢复用 pg_dump/pg_restore/psql,17 版客户端兼容 16 版服务端),
- 运行镜像额外装 `gosu`(降权)及 PG 客户端动态库,从固定的 `postgres:18.6-trixie` 官方镜像复制 pg_dump/pg_restore/psql 与 libpq 18,
外加 `/usr/local/bin/ffprobe`:从 `mwader/static-ffmpeg:9.0.2` 拷单个静态二进制(约 135 MB;Debian `ffmpeg` 包要 200+ 个依赖约 450 MB)。
换来源用 `--build-arg FFPROBE_IMAGE=<含 /ffprobe 的镜像>`。
- `/app` 下的二进制、静态产物、脚本全部 root 属主只读;运行用户改不了它们。
@@ -27,32 +27,33 @@ APP_VERSION=v0.1.0 docker compose build # 版本号写
## 2. 启动
部署目录须包含完整仓库构建上下文。首次启用默认浏览器前,先完成 [浏览器手册 §1.1](browser.md#11-部署自带缺省) 的基础镜像构建,再执行以下 compose 命令。部署配置统一放 `.env.deploy`,开发配置仍用仓库根 `.env`。
```sh
mkdir -p /opt/umt && cd /opt/umt # 部署目录(放 docker-compose.yml 与 .env)
mkdir -p /opt/umt && cd /opt/umt # 部署目录(完整仓库构建上下文与 .env.deploy)
cp <仓库>/docker/compose.env.example .env.deploy # 部署配置独立于开发用 .env
sed -i "s/^SECRET_MASTER_KEY=.*/SECRET_MASTER_KEY=$(openssl rand -hex 32)/" .env
sed -i "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$(openssl rand -hex 16)/" .env
sed -i "s/^SECRET_MASTER_KEY=.*/SECRET_MASTER_KEY=$(openssl rand -hex 32)/" .env.deploy
sed -i "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$(openssl rand -hex 16)/" .env.deploy
docker compose --env-file .env.deploy up -d --build
docker compose ps # app 变成 healthy 即可访问
docker compose --env-file .env.deploy ps # app 变成 healthy 即可访问
curl -s localhost:4380/api/v1/health
```
### 2.1 浏览器服务(camofox)
缺省随栈起一个 camofox-browser 容器(jo-inc/camofox-browser **v1.17.0**,commit `389c996`),给原站视图与 Agent 浏览器能力用。
缺省随栈起一个 camofox-browser 容器(jo-inc/camofox-browser **v1.18.0**,commit `f94c81b`),给原站视图与 Agent 浏览器能力用。
app 启动时按 `BROWSER_PROVIDER_URL`(缺省 `http://camofox:9377`)自动登记名为 `camofox-env` 的浏览器服务,
`depends_on` 只要求已启动、不等健康:camofox 挂了 app 照常运行,界面标"浏览器不可用"。
- **镜像**:上游不发布镜像,compose 用 `docker/camofox/Dockerfile` 从锁定的 git tag 构建(修了上游 v1.17.0 Dockerfile 的
构建错误与信号转发,见文件头注释)。首次 `up --build` 要拉 GitHub 源码与约 630 MB 的 Camoufox 浏览器包,
镜像约 3.6 GB;国内网络按 `docs/runbook/browser.md` §1 先在本机构建同名镜像 `camofox-browser:1.17.0`,之后 compose 直接复用。
- **访问凭据**:`.env` 里设 `CAMOFOX_ACCESS_KEY`(`openssl rand -hex 24`),camofox 与 app 共用;不设则内网免认证。
- **资源**:空闲约 200 MB(浏览器空闲 5 分钟自动退出后更低);每个站点会话一个 BrowserContext,
`mem_limit` 缺省 2g、`shm_size` 2gb(Firefox 多进程需要)。
- **镜像**:上游不发布镜像。先按 [浏览器手册 §1.1](browser.md#11-部署自带缺省),用 `docker/camofox/Dockerfile` 构建基础镜像 `camofox-browser:1.18.0`(锁上游 git tag,修正构建与信号转发)。
compose 再用 `docker/browser-desktop/Dockerfile` 从该基础镜像构建默认桌面镜像 `umt-browser-desktop:1.18.0`。基础镜像首次构建需要 GitHub 源码和约 630 MB 的 Camoufox 包;桌面层复用浏览器包。
- **访问凭据**:`.env.deploy` 里设 `CAMOFOX_ACCESS_KEY`(`openssl rand -hex 24`),camofox 与 app 共用;不设则内网免认证。
- **资源**:当前默认桌面镜像 `mem_limit` 为 3g、`shm_size` 为 2gb;每个站点会话一个 BrowserContext,另有桌面常驻进程。
早期约 200 MB 的空闲测量属于纯截图镜像,不能直接作为桌面模式的内存基准。
- **登录状态**:按站点/账号存在卷 `umt-camofox`(`/root/.camofox/profiles`,cookies + localStorage),
在会话空闲回收、关闭会话与 `docker compose stop/restart`(SIGTERM)时落盘;**容器被 SIGKILL / OOM 杀掉时,
在会话空闲回收、关闭会话与 `docker compose --env-file .env.deploy stop/restart`(SIGTERM)时落盘;**容器被 SIGKILL / OOM 杀掉时,
上次落盘之后的新登录会丢**(需重新登录)。
- **接自有实例**:`.env` 里 `BROWSER_PROVIDER_URL=<地址>`、`BROWSER_ACCESS_KEY=<它的 CAMOFOX_ACCESS_KEY>`、`CAMOFOX_REPLICAS=0`。
- **接自有实例**:`.env.deploy` 里 `BROWSER_PROVIDER_URL=<地址>`、`BROWSER_ACCESS_KEY=<它的 CAMOFOX_ACCESS_KEY>`、`CAMOFOX_REPLICAS=0`。
- **不要浏览器**:`CAMOFOX_REPLICAS=0` + `BROWSER_ENABLED=false`。
**SECRET_MASTER_KEY** 生成一次后不再更换:站点 Cookie、下载器密码、模型 API Key 都用它加密存在 `secret` 表。
@@ -77,7 +78,7 @@ app 启动时按 `BROWSER_PROVIDER_URL`(缺省 `http://camofox:9377`)自动登
api 首次启动时在日志里打印一次性 setup token(重启会换新的,旧的作废):
```sh
docker compose logs app | grep 'setup token'
docker compose --env-file .env.deploy logs app | grep 'setup token'
```
浏览器打开 `http://<主机>:4380`,登录页进入初始化模式,填 token、用户名、密码(≥10 字符)。
@@ -90,7 +91,7 @@ docker compose logs app | grep 'setup token'
| `/data` | 命名卷 `umt-data`(或宿主目录) | `agent-sessions/`(Pi JSONL 会话,崩溃恢复点)、`backups/`、`exports/`(映射清单)、`tmp/`(`TMPDIR`) | entrypoint 自动 chown 给 `PUID:PGID` |
| `/media/...` | 用户 bind mount | 媒体库根:控制台 设置 → 媒体库根 登记的 `localPath` | 需对 `PUID:PGID` 可读写;**不会被自动 chown** |
| `/downloads/...` | 用户 bind mount | 下载缓存:路径映射的 local 前缀(下载器报告的 remote 路径 → 容器内路径) | 同上 |
| `/var/lib/postgresql/data`(postgres) | 命名卷 `umt-pg-data` | 数据库 | — |
| `/var/lib/postgresql`(postgres) | 命名卷 `umt-pg18-data`,内部 `18/docker` | PG18 数据库;旧16卷不能直接挂入 | — |
| `/root/.camofox`(camofox) | 命名卷 `umt-camofox` | 浏览器登录状态(`profiles/`,按站点/账号)、上游 cookies/uploads/traces 目录 | — |
- 媒体目录在 `docker-compose.yml` 的 `app.volumes` 里取消注释或另写 `docker-compose.override.yml`:
@@ -106,7 +107,7 @@ docker compose logs app | grep 'setup token'
- bind mount 的相对路径按**第一个 compose 文件所在目录**解析,不是按当前目录;不确定时写绝对路径。
源目录不存在时 Docker 会以 root 属主新建它,库根登记就会因哨兵写不进而失败。
- 路径映射:下载器容器里看到的是 `/downloads/complete/...`,本容器也挂成 `/downloads` 时 remote 前缀与 local 前缀相同;
挂到别处就登记 `remote_prefix=/downloads`、`local_prefix=<本容器内路径>`。硬链接要求下载目录与库根在同一文件系统、同一挂载点内。
挂到别处就登记 `remote_prefix=/downloads`、`local_prefix=<本容器内路径>`。目前整理只支持复制和移动。
- 登记库根时服务端会在目录里写哨兵文件 `.uhd-root`,写不进去直接 400(提示检查挂载与属主)。
- **PUID/PGID**:设成宿主媒体目录的属主(`stat -c '%u:%g' /srv/media`)。改 PUID/PGID 后重启容器,entrypoint 会把 `/data` 重新 chown。
也可以 `docker run --user` 直接指定(此时跳过 chown,`/data` 需事先可写)。
@@ -141,25 +142,30 @@ docker compose logs app | grep 'setup token'
## 6. 升级
发布前检查旧镜像的实际在途任务:在任务页核对 queued / running / waiting,尤其是本次变更的任务名称和版本。优先等它们完成;需要中止的任务先保留原输入、冻结计划和操作记录,确认中止后通过现有入口重新发起。已经产生文件操作证据的任务要先核实执行结果,再决定后续处理。没有旧在途任务时不长期保留旧 handler;业务表前滚不能替代任务版本兼容判断。
本次整理来源功能增加 `scanOrganizeSource@1` 和迁移 `0059_organize_source_snapshot`,沿用 `verifyAndImport`。迁移不会扫描或整理已有目录;用户新增来源后先建立基线,存量必须单独触发。
```sh
cd <仓库> && git pull
docker compose exec app umt backup # 升级前先备份
docker compose --env-file .env.deploy exec app umt backup # 升级前先备份
docker compose --env-file .env.deploy up -d --build app # 重建镜像并替换容器;启动时自动迁移
docker compose ps && docker compose logs --tail=50 app
docker compose --env-file .env.deploy ps && docker compose --env-file .env.deploy logs --tail=50 app
```
迁移只前滚。升级失败要回退时:用旧镜像 + 升级前的备份恢复(见 [backup-restore.md](backup-restore.md)),不要用旧镜像直接连新库。
## 7. 日志与排障
- `docker compose logs -f app`:pino JSON,一行一条;worker 日志带 `group`(io / agent)。
- `docker compose --env-file .env.deploy logs -f app`:pino JSON,一行一条;worker 日志带 `group`(io / agent)。
- `worker 事件循环延迟超过阈值`(warn,`lagMs`):worker 主线程被同步重活占住超过 5s,租约续期可能受影响,带上前后日志排查。json-file 驱动,单文件 10 MB × 5 份。
- entrypoint 自身的输出(`entrypoint: ...`)说明哪个进程先退出、退出码多少。
- 自检失败(`check=data_dir`):`/data` 卷不可写,检查是否挂了只读卷或 `--user` 与卷属主不一致。
- 挂载路径告警(`挂载路径不可用`):看审计页 `preflight.path_unavailable` 事件,核对 bind mount 与 PUID/PGID。
- 进容器排查:`docker compose exec app bash`(root);以运行用户执行命令用 `gosu 10001:10001 <cmd>`。
- 一次性子命令:`docker compose exec app umt migrate|preflight|export-mappings|backup`、
`docker compose run --rm --no-deps app restore <备份>`。
- 进容器排查:`docker compose --env-file .env.deploy exec app bash`(root);以运行用户执行命令用 `gosu 10001:10001 <cmd>`。
- 一次性子命令:`docker compose --env-file .env.deploy exec app umt migrate|preflight|export-mappings|backup`、
`docker compose --env-file .env.deploy run --rm --no-deps app restore <备份>`。
## 验证记录
@@ -303,3 +309,15 @@ API Key 在 qB 5.2+ WebUI 选项 → WebUI → API Key 处生成 / 轮换(等价
- 实测矩阵(`packages/adapters/src/qbittorrent/qbittorrent.real.test.ts`,设 `QB_REAL` 才跑):5.1.2 账密 / 无认证 / 失败分类,
5.2.4 账密 / API Key / 无认证 / 失败分类,各跑 登录 → 添加(testkit `makeTorrent({ realPieces: true })` 真实 piece 哈希,
内容写进保存目录,qB 校验即完成)→ 查询 → 标签 → 暂停恢复 → 删除(不删数据)。
## v0.3.10 发布补充
2026-10-01 已发布 v0.3.10,执行 `0060_manual_acquisition`:`acquisition_plan.slot_id` 允许为空,支持独立手动下载;`download_task.import_plan_id` 保存本次入库规划,并以外键引用计划。部署前备份与校验通过,只替换 app 容器;线上接口、版本一致及页面冒烟通过。后续普通 Compose 启动使用已更新的 `.env.deploy` 版本与源码标记。具体证据见 [发布记录](../plan/18-organize-release.md#v0310-手动下载与入库发布)。
### 当前测试入口
用户要求测试自签 HTTPS 后,4380 已由 Caddy 接管为 `https://服务器地址:4380`;原 443 入口保留,app 本机 HTTP 调试入口为 `http://127.0.0.1:4381`。配置保存在 `.env.deploy` 与本机 `docker-compose.override.yml`,重建了 app/Caddy 容器,镜像及数据库版本保持 v0.3.10/0060。证书由原 Caddy 内部 CA 签发,健康、管理员接口和 HTTPS 页面冒烟通过。
## v0.3.11 修复发布
搜索结果标题接入已有资源详情抽屉,已发布 v0.3.11;数据库保持 `0060`。浏览器桌面修复镜像为 `umt-browser-desktop:1.18.0-umt.1`,内置有效的最大化配置,已验证上下黑边消失。`.env.deploy` 已记录当前应用版本、源码标记和浏览器镜像,4380 HTTPS/443 HTTPS/本机 4381 HTTP 映射保留。具体验证见 [发布记录](../plan/18-organize-release.md#v0311-搜索详情与桌面黑边修复)。