feat(browser): ship camofox v1.17.0 in compose, verified on a real instance

- compose: camofox service on by default (internal only, volume umt-camofox,
  healthcheck, CAMOFOX_REPLICAS=0 to skip); app depends on it with
  service_started/required=false and auto-registers camofox-env from
  BROWSER_PROVIDER_URL / BROWSER_ACCESS_KEY / BROWSER_ENABLED
- docker/camofox/Dockerfile: builds upstream tag v1.17.0 (389c996); fixes the
  upstream Dockerfile (missing postinstall.js before npm ci, sh -c not
  forwarding SIGTERM so logins were never persisted on restart)
- adapter fixes from the real container: coordinate click via evaluate +
  selector, focus typing with mode=keyboard, password values redacted from
  ARIA snapshots/extract, downloads field names, 410 after browser restart
  treated as lost tab; navigate reopens a tab in the same session
- verified-capability table keyed by upstream version (1.17.0 all ten);
  other versions stay unverified; probe reports auth failures
- settings: browser providers block (create/edit/delete/probe, capability
  table); API POST/PATCH/DELETE /browser/providers, POST .../probe
- scripts/browser-verify: umtbr stack + mock NexusPHP run of REQ 4.7.4 items
- docs: runbook browser §1/§7 (real-instance record), deploy §2.1

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-29 00:05:21 +08:00
1 parent b4c9b1ead2
commit 40dd0e69e4
29 files changed
+1661 -73

No files matched your search

+27 -1
View File
@@ -2,7 +2,7 @@
形态:**除 PostgreSQL 外全部在一个容器里**。镜像 `uhd-media-tool` 内一个 `deno compile` 出的二进制 `umt-server`,
容器里同时跑 api、worker(io 组)、worker:agent(Agent 会话)三个进程,api 同端口托管 web 静态产物(`WEB_ROOT=/app/public`),对外只有 **4380** 一个端口。
compose 只有 `app` + `postgres` 两个服务。
compose 有三个服务:`app` + `postgres` + `camofox`(浏览器服务,只在内网,不映射端口;可关掉或换成自有实例,见 §2.1)。
## 1. 构建镜像
@@ -37,6 +37,24 @@ docker compose ps # app 变成 hea
curl -s localhost:4380/api/v1/health
```
### 2.1 浏览器服务(camofox)
缺省随栈起一个 camofox-browser 容器(jo-inc/camofox-browser **v1.17.0**,commit `389c996`),给原站视图与 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 多进程需要)。
- **登录状态**:按站点/账号存在卷 `umt-camofox`(`/root/.camofox/profiles`,cookies + localStorage),
在会话空闲回收、关闭会话与 `docker compose stop/restart`(SIGTERM)时落盘;**容器被 SIGKILL / OOM 杀掉时,
上次落盘之后的新登录会丢**(需重新登录)。
- **接自有实例**:`.env` 里 `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` 表。
把它存进密码管理器或离线介质,**和数据库备份分开保管**;备份里不含它,丢了它备份中的凭证无法解密。
@@ -73,6 +91,7 @@ docker compose logs app | grep 'setup token'
| `/media/...` | 用户 bind mount | 媒体库根:控制台 设置 → 媒体库根 登记的 `localPath` | 需对 `PUID:PGID` 可读写;**不会被自动 chown** |
| `/downloads/...` | 用户 bind mount | 下载缓存:路径映射的 local 前缀(下载器报告的 remote 路径 → 容器内路径) | 同上 |
| `/var/lib/postgresql/data`(postgres) | 命名卷 `umt-pg-data` | 数据库 | — |
| `/root/.camofox`(camofox) | 命名卷 `umt-camofox` | 浏览器登录状态(`profiles/`,按站点/账号)、上游 cookies/uploads/traces 目录 | — |
- 媒体目录在 `docker-compose.yml` 的 `app.volumes` 里取消注释或另写 `docker-compose.override.yml`:
@@ -110,6 +129,13 @@ docker compose logs app | grep 'setup token'
| `ORGANIZE_WALK_MAX_FILES` | `5000` | 手动按路径整理时递归遍历的文件数上限;dryRun 超出返回 `truncated: true`,正式执行拒绝 |
| `BACKUP_KEEP` | `14` | 备份保留份数,0 不轮转 |
| `LOG_LEVEL` | `info` | pino 级别 |
| `CAMOFOX_ACCESS_KEY` | 空 | camofox 访问凭据(camofox 与 app 共用);空 = 内网免认证 |
| `BROWSER_PROVIDER_URL` | `http://camofox:9377` | app 登记的浏览器服务地址(`camofox-env`);接自有实例时改 |
| `BROWSER_ACCESS_KEY` | `CAMOFOX_ACCESS_KEY` | 该服务的访问凭据,只在进程内使用,不入库 |
| `BROWSER_ENABLED` | `true` | `false` = 停用 `camofox-env`,界面标"浏览器不可用" |
| `CAMOFOX_REPLICAS` | `1` | `0` = 不起 camofox 容器 |
| `CAMOFOX_SESSION_TIMEOUT_MS` / `CAMOFOX_BROWSER_IDLE_TIMEOUT_MS` | `1800000` / `300000` | 会话空闲回收(落盘登录状态)/ 浏览器空闲退出 |
| `CAMOFOX_MAX_SESSIONS` / `CAMOFOX_MEM_LIMIT` | `8` / `2g` | 最多并发会话 / 容器内存上限 |
## 6. 升级