DockerWeb 是一套面向 WhatsApp Web 多开、隔离运行与自动化接入 的浏览器环境管理平台。平台使用 Java 作为控制面,将每个浏览器创建为独立的 Docker 容器,并通过 CloakBrowser、KasmVNC、3proxy 和 Python Worker 组合出可持久化、可远程操作、可设置代理、可通过接口调度的浏览器 Runtime。
在需要同时维护多个 WhatsApp Web 会话时,直接在同一台浏览器中切换账号,会共享 Cookie、缓存、本地存储、进程和网络出口,环境之间难以隔离,也不便于上层业务系统统一创建、分配、回收和监控。
DockerWeb 为每个浏览器提供独立的容器、Profile 目录、资源限制、网络配置和运行状态。创建环境时会生成随机指纹 Seed,由 CloakBrowser Runtime 根据 Seed 构建浏览器环境;Java 控制面不再自行拼接或覆盖 UA、WebGL、GPU 等底层字段。浏览器 Cookie、缓存、本地存储和登录数据保存在独立 Profile 中,因此容器重启后仍可继续使用原会话。
这种隔离方式可以减少多个浏览器共享同一运行环境造成的相互影响和环境关联风险,但不承诺规避 WhatsApp 或其他网站的风控。使用者仍需遵守目标网站条款、当地法律以及代理和第三方组件的许可要求。
本项目主要用于以下场景:
- 在一台或多台服务器上运行多个相互隔离的 WhatsApp Web 浏览器。
- 由外部业务系统按
browserId创建、查询、分配、启动、停止、重启或删除浏览器。 - 为每个浏览器设置独立的 HTTP、HTTPS 或 SOCKS5 代理,并支持运行期间更新代理。
- 通过 KasmVNC 在网页中远程查看和操作容器内的真实浏览器。
- 通过接口提交区号和本地手机号,自动进入 WhatsApp 手机号关联流程并获取配对码。
- 通过 WSS/STOMP 实时接收配对码及状态变化,或通过 HTTP 主动查询当前状态。
- 持续监测配对码更新与登录结果;登录成功后完成任务、更新浏览器状态并释放任务占用。
- 维护一批可供上层系统及时领取和使用的空白浏览器环境。
| 能力 | 说明 |
|---|---|
| 容器级隔离 | 每个浏览器运行在独立 Docker 容器中,并拥有独立 Profile、端口、CPU 和内存配置。 |
| 浏览器环境 | CloakBrowser 根据持久化指纹 Seed 启动浏览器;默认使用中文语言环境,时区由创建请求指定。 |
| Profile 持久化 | Cookie、缓存、本地存储和登录状态写入宿主机独立目录,容器重启不等于会话丢失。 |
| 生命周期管理 | 支持单个或批量创建、启动、停止、重启、删除和状态查询,变更操作使用幂等键。 |
| 动态代理 | 通过容器内 3proxy 桥接 HTTP、HTTPS、SOCKS5 认证代理,支持运行期间更新代理配置。 |
| 远程浏览器 | KasmVNC 提供网页远程桌面,访问链接由短期 Ticket 授权。 |
| WhatsApp 自动化 | Python Worker 自动准备 WhatsApp 页面、提交区号和手机号、进入配对码页面并读取配对码。 |
| 配对码通知 | 配对码及任务状态可通过 WSS/STOMP 推送,也可通过 REST 接口查询。 |
| 登录监测 | Worker 持续检测页面状态;确认登录后发布成功事件,并将自动化任务置为完成。 |
| 运行证据 | 保存操作阶段、稳定错误码、traceId、Runtime 哈希和观测状态,便于定位创建或自动化失败。 |
| OpenAPI | /v3/api-docs 提供 OpenAPI v3 机器可读接口契约。 |
flowchart LR
A["外部业务系统"] --> B["创建独立浏览器"]
B --> C["生成 Profile 与指纹 Seed"]
C --> D["创建并启动 Docker Runtime"]
D --> E["校验 CloakBrowser 与 KasmVNC"]
E --> F{"是否设置代理"}
F -- "是" --> G["3proxy 应用代理"]
F -- "否" --> H["保持直连"]
G --> I["传入区号和手机号"]
H --> I
I --> J["Worker 自动提交号码"]
J --> K["进入配对码页面"]
K --> L["读取并持续监测配对码"]
L --> M["WSS 实时推送"]
L --> N["HTTP 主动查询"]
M --> O["持续检测登录状态"]
N --> O
O --> P["登录成功,更新状态并释放任务占用"]
对应的主要接口调用顺序:
POST /api/v1/integration/browsers/batches:创建一个或多个浏览器。PUT /api/v1/integration/browsers/{browserId}/proxy:按需设置或更新代理。POST /api/v1/integration/browsers/{browserId}/whatsapp/phone-link:提交区号和本地手机号。- 订阅
/user/queue/integration-events:实时接收配对码、状态变化和登录结果。 GET /api/v1/integration/browsers/{browserId}/whatsapp/status:断线补偿或主动轮询任务状态。
flowchart TB
Client["外部系统 / 管理端"]
Gateway["NGINX Gateway<br/>REST、WSS、KasmVNC 路由"]
API["Java / Spring Boot 控制面<br/>OpenAPI、鉴权、任务与生命周期"]
DB[("MySQL<br/>环境、操作、任务与审计")]
Docker["Docker Engine"]
Client -->|"REST / WSS"| Gateway
Gateway --> API
API <--> DB
API --> Docker
subgraph Runtime["每个浏览器独立 Docker Runtime"]
VNC["KasmVNC<br/>远程桌面"]
Cloak["CloakBrowser / Chromium"]
Worker["Python Worker<br/>浏览器控制与 WhatsApp 自动化"]
Proxy["3proxy<br/>本地代理桥接"]
Profile[("独立持久化 Profile")]
VNC --> Cloak
Worker <--> Cloak
Cloak -.->|"可选代理出口"| Proxy
Cloak <--> Profile
end
Docker --> Runtime
Gateway -->|"短期访问 Ticket"| VNC
API <-->|"Worker 命令 / 状态"| Worker
API -->|"配对码与登录事件"| Gateway
Java 控制面只负责可信的生命周期、配置、任务和状态编排;实际浏览器页面操作在容器内由 Python Worker 完成。代理流量通过容器内 3proxy 转发,KasmVNC 只提供可视化远程访问,MySQL 保存控制面事实,浏览器会话数据则保存在宿主机 Profile 目录。
| 路径 | 负责功能 |
|---|---|
browser-platform-server/ |
Java/Spring Boot 控制面,负责环境生命周期、Docker 调度、代理配置、浏览器访问 Ticket、WhatsApp 任务、REST/WSS、鉴权、审计和数据库状态。 |
browser-runtime-image/ |
浏览器 Runtime 镜像,集成 CloakBrowser、KasmVNC、3proxy、字体、启动脚本、健康检查和 Python Worker。 |
browser-runtime-image/worker/ |
Python 浏览器控制层,负责启动/验证 Runtime、动态代理、WhatsApp 页面准备、号码提交、配对码读取和登录状态检测。 |
browser-runtime-image/userscripts/ |
WhatsApp 页面 Hook 的构建、观察、配对逻辑及相关自检脚本。 |
browser-runtime-image/runtime-manifest/ |
Runtime 构建与发布清单、镜像摘要及第三方许可边界。 |
browser-platform-web/ |
NGINX Gateway 镜像及保留的管理端源代码;当前 Compose 中主要负责 REST、WSS 和 KasmVNC 反向代理。 |
deploy/compose/ |
Compose Secret 初始化、数据库权限初始化等部署辅助脚本。 |
deploy/nginx/ |
API、WebSocket、浏览器访问和 KasmVNC 的 NGINX 路由配置。 |
scripts/ |
代理端到端验证、离线浏览器环境数据及其他维护工具。 |
compose.yaml |
本地或单机部署编排,统一启动 Runtime 构建、MySQL、Schema、Java 后端和 Gateway。 |
git clone https://github.com/WeToolX/DockerWeb.git
cd DockerWeb要求 Java 21+、Docker Engine;秘密只通过 Compose secrets 或 Secret Manager 注入,不提交 .env。
完整本地联调优先使用与生产一致的 Compose:
cp .env.compose.example .env
./deploy/compose/init-secrets.sh
docker compose up -d --build默认测试使用 H2 MySQL 兼容模式:
cd browser-platform-server
./mvnw test正式 application.yml 必须提供 MySQL 和 Runtime 配置,不适合无配置直接启动。需要本地联调时,先按部署文档初始化 MySQL/Runtime,再运行:
./mvnw spring-boot:run当前 Compose 使用 browser-platform-web/Dockerfile 构建 NGINX Gateway,但不会构建或发布 browser-platform-web/src 中的遗留 Vue 管理端源码;该源码不进入当前发布门禁。
后端完整默认门禁:
cd browser-platform-server
./mvnw clean verify当前基线:110 tests、0 failures、0 errors、12 skipped。跳过项均为显式 Docker、MySQL、Nginx 或授权外部测试,不能把 skipped 计为通过。
提交前至少执行与改动范围对应的聚焦测试,再执行默认门禁。失败时保留第一个根因和对应日志,不通过重复执行掩盖不稳定测试。
| 门禁 | 开关 | 目的 |
|---|---|---|
| Docker 兼容 | RUN_DOCKER_TESTS=true |
daemon、资源限制、端口绑定 |
| Runtime 生命周期 | RUN_BROWSER_DOCKER_TESTS=true |
Cloak Worker、Profile 重建、单 Context/标签 |
| Nginx 语法 | RUN_NGINX_TESTS=true |
配置语法和动态上游边界 |
| Nginx Upgrade | RUN_NGINX_UPGRADE_TESTS=true |
真实 KasmVNC WebSocket Upgrade |
| MySQL 权限 | RUN_MYSQL_PERMISSION_TESTS=true |
应用 DML 与 Schema DDL 权限分离 |
| MySQL 备份恢复 | RUN_MYSQL_BACKUP_TESTS=true |
临时库 dump/drop/restore |
示例:
RUN_DOCKER_TESTS=true ./mvnw -Dtest=DockerClientCompatibilityTest test
RUN_BROWSER_DOCKER_TESTS=true BROWSER_RUNTIME_IMAGE=browser-platform-runtime:test \
./mvnw -Dtest=BrowserRuntimeDockerLifecycleTest test
RUN_NGINX_TESTS=true ./mvnw -Dtest=NginxBrowserProxyConfigTest test
RUN_NGINX_UPGRADE_TESTS=true ./mvnw -Dtest=NginxBrowserProxyIntegrationTest testMySQL 权限和备份测试还需要 REAL_MYSQL_ADMIN_PASSWORD。测试只允许使用独立临时库,禁止指向生产数据库。
RUN_AUTHORIZED_EXTERNAL_TESTS=true \
./mvnw -Dtest=AuthorizedExternalRuntimeTest#authenticatedProxyAndWhatsAppPairingCodeAreRealAndFailClosed test必需环境变量:
AUTHORIZED_PROXY_HOSTAUTHORIZED_PROXY_PORTAUTHORIZED_PROXY_USERNAMEAUTHORIZED_PROXY_PASSWORDAUTHORIZED_WHATSAPP_COUNTRYAUTHORIZED_WHATSAPP_PHONE- 可选
BROWSER_RUNTIME_IMAGE
该门禁验证正确代理、错误密码失败关闭、号码提交、首码稳定和真实换码。号码、密码和配对码不得出现在命令输出或测试报告。
RUN_AUTHORIZED_WHATSAPP_RELIABILITY_TEST=true \
AUTHORIZED_WHATSAPP_TRIALS=10 \
./mvnw -Dtest=AuthorizedExternalRuntimeTest#freshWindowPhoneLinkReliability test使用同一组授权代理/号码变量。每轮新建独立容器、Profile 和浏览器进程;成功条件为代理验证、进入号码页、提交号码并达到 PAIRING_CODE_READY。测试会执行完全部轮次后汇总失败。
最近一次 10 窗口结果为 0/10,全部在 PROXY_VERIFY 阶段因代理握手失败终止,未执行页面动作。因此该结果不能用于评价国家区号选择、React 重绘或代码页识别的稳定性。
RUN_AUTHORIZED_FULL_CHAIN_TESTS=true \
./mvnw -Dtest=AuthorizedFullChainIntegrationTest test除代理变量外还需:
AUTHORIZED_MYSQL_URLAUTHORIZED_MYSQL_USERNAMEAUTHORIZED_MYSQL_PASSWORDAUTHORIZED_RUNTIME_IMAGE_REF
完整链覆盖 API Key、MySQL、批量创建、Docker/Cloak、代理更新与失败恢复、停止/启动/重启/删除、Profile/Seed 和端口释放。
| 层级 | 必须观察 |
|---|---|
| API | HTTP 状态、稳定 code、traceId、幂等返回 |
| 数据库 | environment、operation、task、审计与密钥引用一致 |
| Docker | 容器状态、资源限制、loopback 端口、Secret 删除 |
| Runtime | Worker 状态、应用/观测摘要、单 Context/标签、Profile 保持 |
| 页面 | 实际号码页、代码页和可见结果,不用 API 成功替代 |
| WSS | Key 鉴权、私有队列、版本递增、断线后 REST 查询 |
| 部署 | TLS、Nginx Upgrade、真实 MySQL权限、重启恢复 |
异步接口返回 202 仅表示 operation/task 已受理。测试必须继续观察 SUCCEEDED、FAILED 或业务终态;批量测试逐项记录成功和失败。
提交前检查新增文件和测试输出:
git diff --check
git status --short不得提交:生产 API Key 原文、代理密码、手机号、WhatsApp 配对码、数据库密码、Profile、浏览器缓存、备份文件和生产日志。示例只使用占位值。
Compose 改动还必须执行:
docker compose config --quiet
docker compose build
docker compose up -d
docker compose ps -a确认三个一次性任务退出 0、三个长期服务健康,并检查生成配置中没有秘密原文。
每个真实缺陷至少记录:
- 使用的提交和 Runtime digest;
- 是否为全新 Profile;
- 请求
traceId、browserId、operationId/taskId; - API、数据库、容器、Worker、页面和 WSS 各层结果;
- 敏感信息已脱敏;
- 可重复次数和成功率。
若外部代理、WhatsApp或网络阻断,标记 PENDING_EXTERNAL,不能修改测试让其假通过。
- 部署形态:Linux 单机 Docker Compose
- 启动命令:
docker compose up -d --build
Compose 会构建并启动 MySQL、Schema Job、数据库权限任务、Java Backend、纯 Nginx API/noVNC Gateway,并自动下载、校验和构建 CloakBrowser Runtime。项目不再构建或部署 Vue 前端;浏览器二进制不进入 Git,也不需要手工执行下载脚本。
- Linux x86_64、Docker Engine 与 Compose v2;建议至少 8 GiB 内存,实际容量按每浏览器约 1 CPU / 2 GiB 评估。
- 允许访问 Maven Central、Docker Hub 和固定 CloakBrowser GitHub Release。
/data/browser/profiles有足够空间;Docker 数据目录能容纳 Runtime 与构建缓存。- 公网只开放现有 TLS 网关。Compose 默认只监听
127.0.0.1:8081,MySQL、Backend 和浏览器 Runtime 不直接公开。
git clone https://github.com/WeToolX/DockerWeb.git
cd DockerWeb
cp .env.compose.example .env
./deploy/compose/init-secrets.sh
docker compose up -d --build
docker compose ps秘密生成脚本创建五个仅本机可读文件:MySQL root 密码、应用数据库密码、平台 API Key、代理 AES-256 密钥和 KasmVNC 随机密码。Backend 与 Gateway 只在运行时读取 KasmVNC Secret,仓库和镜像中不保存固定 Basic 凭据。deploy/compose/secrets/ 已被 Git 和 Docker build context 排除。
轮换 KasmVNC Secret 时必须同步重建 Runtime 镜像、重建全部浏览器容器并重启 Backend 与 Gateway;只替换 Secret 文件不会修改已经运行的旧容器。
从旧版本升级时,历史 admin_password 文件不再被 Compose 挂载。脚本只告警而不自动删除;确认无需回滚到旧登录版本后再由部署者手工清理。
按需修改 .env:
| 配置 | 默认值 | 说明 |
|---|---|---|
GATEWAY_BIND |
127.0.0.1 |
不建议改为公网地址 |
GATEWAY_PORT |
8081 |
外层 TLS 网关的上游端口 |
BACKEND_PORT |
8080 |
仅回环暴露,供 Gateway 使用 |
PROFILE_ROOT |
/data/browser/profiles |
必须使用宿主机绝对路径 |
PLATFORM_BROWSER_PUBLIC_BASE_URL |
https://localhost |
生产必须改为最终用户访问的固定 HTTPS Origin,例如 https://whatsappdocker.com |
mysql healthy
-> schema 成功退出
-> db-permissions 成功退出
-> backend healthy
-> gateway healthy
runtime-ready 成功退出
-> backend
- Runtime Dockerfile 下载固定
146.0.7680.177.5x64 发行包,并校验压缩包及 Chrome SHA-256。 - Backend 启动时从 Docker Engine 读取 Runtime 完整 image ID,生成本次启动 Manifest;创建浏览器只使用该内容寻址 ID,不信任可漂移 tag。
- Schema 使用 root 账号执行 DDL 并自动退出;权限任务随后把
browser_app收敛为SELECT/INSERT/UPDATE/DELETE。 - Gateway 从 Backend 鉴权响应的
X-Browser-Upstream取得 noVNC 上游,不再生成或重载environments.map。
将已有 HTTPS 域名反向代理到 http://127.0.0.1:8081,必须透传 X-API-Key、Host、Cookie、Origin、Referer、Upgrade、Connection 和 X-Forwarded-*。外部控制 API 可以使用受保护的 Gateway 地址;最终用户域名至少代理 /browser/** 与 /browser-launch/**。启动链接路径包含单次 Bearer 凭证,外层网关必须对 /browser-launch/** 关闭访问日志。对于无 Origin/Referer 的首个 302 跳转,/browser/** 外层反代必须设置 Origin: https://$host,否则 gateway 的 Origin 绑定会返回 401。noVNC 的 Secure Cookie 依赖客户端使用 HTTPS。
Compose Gateway 使用 host network,是为了访问仅绑定宿主机回环的动态 KasmVNC 端口。该部署目标是 Linux;Docker Desktop 只用于开发冒烟。
docker compose ps -a
docker compose logs schema db-permissions
docker compose logs -f backend gateway
curl -fsS http://127.0.0.1:8081/actuator/health
test "$(curl -sS -o /dev/null -w '%{http_code}' http://127.0.0.1:8081/)" = 404期望:schema、db-permissions、runtime-ready 为 Exited (0);mysql、backend、gateway 为 healthy;根路径为 404,确认没有发布项目自身前端。
git pull --ff-only
docker compose up -d --build普通停止不会删除数据:
docker compose downMySQL 位于 mysql-data 命名卷;浏览器 Profile 位于 PROFILE_ROOT。只有明确需要永久销毁时才使用 docker compose down -v 并手工处理 Profile 根目录。
- 回滚代码后再次执行
docker compose up -d --build;数据库与 Profile 保持不动。 - 结构升级前必须备份 MySQL;破坏性 DDL 不自动执行。
- Docker Socket 仅挂载给 Backend,权限等同宿主 Docker 管理员;不能把 Backend 端口或 Socket 暴露公网。
- 首次构建需要下载较大的 Runtime 与依赖;后续由 Docker 缓存复用。若下载或哈希校验失败,部署应失败关闭,不能跳过验证。
- 契约版本:OpenAPI
1.1.0 - REST 范围:全部
/api/v1/** - WSS/STOMP endpoint:
/ws/integration - 鉴权:
X-API-Key - 完整机器可读契约:部署后携带
X-API-Key访问/v3/api-docs。
本接口只控制平台允许的浏览器生命周期、代理和 WhatsApp 配对流程,不开放 Docker、任意脚本、任意页面点击或验证码绕过。
Compose 部署后,调用方只使用统一 TLS Gateway 域名作为 BASE_URL;不要直连 Backend 8080、MySQL、Docker Socket 或浏览器 Runtime 端口。
部署者执行 ./deploy/compose/init-secrets.sh 生成
deploy/compose/secrets/api_key。该文件是 64 位十六进制值(256 bit),只读挂载给 Backend,不写入数据库。
调用方从部署者的 Secret Manager 安全取得同一个值。项目不提供登录、CSRF、Session、Key 签发或吊销接口。轮换时原子替换 api_key 文件、同步调用方 Secret,再重启 Backend。
所有 REST 请求携带:
X-API-Key: <api-key>所有创建、修改、启停、删除、代理更新和 WhatsApp 命令还必须携带:
Idempotency-Key: <1..128 chars unique business key>同一调用方、同一业务动作重试时复用原幂等键;新的业务动作生成新键。不要用时间戳替代稳定业务键。
成功:
{
"success": true,
"code": "OK",
"message": "success",
"data": {},
"traceId": "request-trace-id",
"timestamp": "2026-07-22T00:00:00Z"
}失败:
{
"success": false,
"code": "API_KEY_INVALID",
"message": "API Key 无效",
"data": null,
"traceId": "request-trace-id",
"timestamp": "2026-07-22T00:00:00Z"
}调用方以 HTTP 状态和稳定 code 判断,不解析 message。保存 traceId 用于问题追踪。
普通环境、operation、browser-interaction、realtime、fingerprint 和集成接口全部使用同一 Key。实际 Controller 契约以携带 Key 访问 /v3/api-docs 的结果为准;下表列出面向外部依赖的聚合接口。
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/api/v1/integration/browsers |
按类型、员工、备注、Runtime、空白状态组合分页查询 |
PATCH |
/api/v1/integration/browsers/{browserId} |
修改类型、员工 ID、备注 |
POST |
/api/v1/integration/browsers/batches |
按数量创建并启动 EMPTY 浏览器 |
POST |
`/api/v1/integration/browsers/actions/{START | STOP}` |
DELETE |
/api/v1/integration/browsers/{browserId} |
停止并异步删除 |
PUT |
/api/v1/integration/browsers/{browserId}/proxy |
完整替换代理;运行态重建、失败恢复旧代理 |
POST |
/api/v1/integration/browsers/{browserId}/whatsapp/phone-link |
输入区号和号码,进入代码流程 |
POST |
/api/v1/integration/browsers/{browserId}/whatsapp/monitor |
幂等启动/恢复代码监控 |
GET |
/api/v1/integration/browsers/{browserId}/whatsapp/status |
主动查询当前 WhatsApp 状态;检测到个人主页按钮时返回 loginState=LOGGED_IN |
POST |
/api/v1/environments/{browserId}/browser-launch-links |
外部后端签发 24 小时、单次使用的浏览器启动链接 |
GET |
/browser-launch/{token} |
用户浏览器消费链接、接收 24 小时 HttpOnly Cookie并跳转 |
外部项目后端携带 X-API-Key 调用启动链接接口,把响应中的绝对 launchUrl 交给用户浏览器打开。链接在签发后 24 小时内只允许首次消费;同一 Key 对同一浏览器重新签发会让旧未消费链接失效。DockerWeb 设置限定环境路径的 24 小时 HttpOnly Cookie并跳转到 KasmVNC。调用方不得把配置 API Key 放入 URL、前端代码或浏览器存储,也不得把 launchUrl 写入普通业务日志。
查询示例:
curl -sS "$BASE_URL/api/v1/integration/browsers?browserType=EMPTY&blankStatus=IDLE&page=1&pageSize=20" \
-H "X-API-Key: $API_KEY"可组合参数:
browserType:EMPTY、ACCOUNT;employeeId:精确匹配;remark:包含匹配;runtimeStatus:例如RUNNING、STOPPED、ERROR;blankStatus:IDLE、SCRIPT_RUNNING,只允许与EMPTY一起使用;page:从 1 开始;pageSize:1~100。
修改元数据:
curl -sS -X PATCH "$BASE_URL/api/v1/integration/browsers/1001" \
-H "X-API-Key: $API_KEY" \
-H "Idempotency-Key: metadata-order-1001-v2" \
-H 'Content-Type: application/json' \
-d '{"browserType":"ACCOUNT","employeeId":"E10086","remark":"已绑定业务账号"}'employeeId 或 remark 传 null 表示清空。存在活动自动化任务时不能修改浏览器类型。
新建浏览器类型固定为 EMPTY,创建后自动启动。count 必须为 1~100。proxies 省略、传 null 或 [] 时,本批次全部浏览器使用服务器直接网络;非空时数量必须与 count 完全一致。直连模式必须使用 timezoneMode=MANUAL,查询结果的 proxySummary 为 DIRECT。
直连示例:
curl -sS -X POST "$BASE_URL/api/v1/integration/browsers/batches" \
-H "X-API-Key: $API_KEY" \
-H "Idempotency-Key: create-direct-20260731-001" \
-H 'Content-Type: application/json' \
-d '{
"count": 1,
"proxies": [],
"timezoneMode": "MANUAL",
"timezone": "Asia/Shanghai",
"resources": {"cpuMillis": 1000, "memoryBytes": 2147483648}
}'curl -sS -X POST "$BASE_URL/api/v1/integration/browsers/batches" \
-H "X-API-Key: $API_KEY" \
-H "Idempotency-Key: create-order-20260722-001" \
-H 'Content-Type: application/json' \
-d '{
"count": 1,
"proxies": [{
"type": "SOCKS5",
"host": "proxy.example.com",
"port": 1080,
"username": "proxy-user",
"password": "write-only-secret",
"remoteDns": true,
"stickySessionKey": null
}],
"timezoneMode": "MANUAL",
"timezone": "America/Chicago",
"resources": {"cpuMillis": 1000, "memoryBytes": 2147483648}
}'批量响应中的每一项独立返回 browserId、operationId、status、errorCode。部分失败不回滚已受理项。
批量启停:
curl -sS -X POST "$BASE_URL/api/v1/integration/browsers/actions/START" \
-H "X-API-Key: $API_KEY" \
-H "Idempotency-Key: start-order-20260722-001" \
-H 'Content-Type: application/json' \
-d '{"browserIds":[1001,1002]}'删除:
curl -sS -X DELETE "$BASE_URL/api/v1/integration/browsers/1001" \
-H "X-API-Key: $API_KEY" \
-H "Idempotency-Key: delete-order-1001"202 Accepted 仅代表受理。当前集成 API 没有独立的 GET operationId 接口,也没有向集成 WSS 推送环境 operation 状态;调用方必须分页查询浏览器并观察目标 browserId 的 latestOperation.status 与 runtimeStatus。这是当前契约限制,不应假定 202 已完成。
代理更新是完整替换,不是局部 PATCH:
curl -sS -X PUT "$BASE_URL/api/v1/integration/browsers/1001/proxy" \
-H "X-API-Key: $API_KEY" \
-H "Idempotency-Key: proxy-order-1001-v3" \
-H 'Content-Type: application/json' \
-d '{
"type": "HTTP",
"host": "proxy.example.com",
"port": 8080,
"username": "proxy-user",
"password": "write-only-secret",
"remoteDns": true,
"stickySessionKey": null
}'- 支持
HTTP、HTTPS、SOCKS5;协议必须与供应商实际类型一致。 - 停止态只保存,新代理在下次启动应用。
- 运行态创建候选 Runtime 验证新代理,成功后切换;失败时恢复旧代理。
- 代理密码只写、不回显;查询只返回脱敏
proxySummary。 - 浏览器处于
SCRIPT_RUNNING时拒绝更新代理。
前置条件:浏览器必须是 EMPTY + RUNNING + IDLE。同一浏览器同一时间只允许一个活动任务。
提交号码:
curl -sS -X POST "$BASE_URL/api/v1/integration/browsers/1001/whatsapp/phone-link" \
-H "X-API-Key: $API_KEY" \
-H "Idempotency-Key: phone-link-order-1001" \
-H 'Content-Type: application/json' \
-d '{"countryCode":"1","phoneNumber":"3125550100"}'countryCode 可带或不带 +,1~4 位;phoneNumber 只提交本地号码数字,最长 20 位。响应返回 taskId、browserId 和任务状态。
主动查询:
curl -sS "$BASE_URL/api/v1/integration/browsers/1001/whatsapp/status" \
-H "X-API-Key: $API_KEY"主要状态:ACCEPTED、PREPARING、PHONE_INPUT_READY、WAITING_FOR_PAIRING_CODE、PAIRING_CODE_READY、LOGGED_IN、PAGE_NOT_READY、FAILED。配对码只在当前 Key 有权访问的实时/查询响应中出现。
登录检测使用 WhatsApp 页面中唯一可见的个人主页按钮作为成功标识:
{
"loginState": "LOGGED_IN",
"loginDetectionSupported": true
}未出现该标识时返回 loginState=UNKNOWN,不推断为未登录。检测从非成功状态转为 LOGGED_IN 时,通过 WSS 发送 WHATSAPP_LOGIN_SUCCEEDED。
确认登录成功后,WhatsApp 任务进入 SUCCEEDED 终态并释放浏览器占用;空白浏览器的 blankStatus 随即恢复为 IDLE,此时可以执行代理修改。浏览器停止、删除、任务异常或租约超时也会释放占用。
- 连接
wss://<host>/ws/integration。 - STOMP
CONNECT帧携带X-API-Key。 - 只订阅
/user/queue/integration-events。 - 需要主动刷新某浏览器状态时,向
/app/integration/whatsapp/status发送纯数字browserId。
连接示意:
CONNECT
accept-version:1.2
host:<host>
X-API-Key:<api-key>
\0
SUBSCRIBE
id:integration-events
destination:/user/queue/integration-events
\0
事件类型包括 WHATSAPP_PROGRESS、WHATSAPP_PAIRING_CODE_CHANGED、WHATSAPP_STATUS、WHATSAPP_MONITOR_WARNING、WHATSAPP_FAILED。调用方按 taskId + version 去重;断线重连后先调用 REST status,再恢复订阅。
WSS 仅推送 WhatsApp 任务事件,不接受启动、停止、删除或代理更新命令。
| HTTP | code |
处理建议 |
|---|---|---|
| 400 | VALIDATION_ERROR、PAGINATION_INVALID、BATCH_SIZE_INVALID |
修正请求,不重试原内容 |
| 401 | API_KEY_INVALID |
核对配置 Secret,不自动无限重试 |
| 403 | ACCESS_DENIED |
当前 Key 无权访问该任务 |
| 404 | ENVIRONMENT_NOT_FOUND、WHATSAPP_TASK_NOT_FOUND |
校验 browserId/task 所属关系 |
| 409 | BROWSER_BUSY、OPERATION_IN_PROGRESS、METADATA_CONFLICT |
查询当前状态后决定重试 |
| 422 | PROXY_TYPE_UNSUPPORTED |
修正代理协议 |
| 429 | API_KEY_RATE_LIMITED |
退避后重试;默认每 Key 每分钟 600 请求 |
| 503 | CLOAK_RUNTIME_UNAVAILABLE、PROXY_SECRET_KEY_UNAVAILABLE |
联系平台运维,调用方不要降级直连 |
批量接口还会在单项 errorCode 返回拒绝原因,调用方必须逐项处理。
- Key 只保存在部署 Secret 和调用方 Secret Manager,日志和异常不打印请求头。
- 每个变更动作使用稳定幂等键,网络超时重试复用原键。
202后持续查询真实终态,批量结果逐项处理。- 代理密码、手机号和配对码在调用方日志中脱敏。
- WSS 使用私有 destination,按版本去重,断线后 REST 补偿查询。
- 登录状态保持
UNKNOWN,直到平台以后提供经真实页面验收的登录检测契约。 - 使用当前有效代理完成创建、运行态换代理和 WhatsApp 代码页验收后再接入生产。
DockerWeb 的实现建立在以下项目之上,感谢这些项目的维护者和贡献者:
| 项目 | 本项目中的用途 | 上游许可 |
|---|---|---|
| CloakBrowser | Chromium 自动化 wrapper 与 Runtime 获取入口 | wrapper:MIT;编译二进制:单独 Binary License |
| KasmVNC | 浏览器远程桌面与 Web 客户端 | GPL-2.0 |
| Kasm Workspaces Images | kasmweb/chrome Runtime 基础镜像来源 |
MIT(仅该仓库维护的源码) |
| 3proxy | 容器内本地代理桥接 | Apache-2.0(采用上游提供的可选许可) |
| Playwright | CloakBrowser 的 Python 浏览器控制基础 | Apache-2.0 |
| Spring Boot | Java 控制面 | Apache-2.0 |
| docker-java | Docker Engine Java 客户端 | Apache-2.0 |
| MyBatis-Plus | 数据持久化 | Apache-2.0 |
| AutoTable | 数据库结构维护 | Apache-2.0 |
| springdoc-openapi | OpenAPI v3 契约 | Apache-2.0 |
| Bouncy Castle Java | 加密能力 | MIT |
| MaxMind GeoIP2 Java | 可选 GeoIP 数据读取 | Apache-2.0 |
| MySQL Server | 控制面数据库 | GPL-2.0 |
| NGINX | API 与 KasmVNC Gateway | BSD-2-Clause |
| Vue、Vite、Pinia、UnoCSS | 遗留管理端源码与前端工具链 | MIT |
| Noto CJK、Noto Emoji、Liberation Fonts | 中文、英文、符号和 Emoji 字体 | OFL-1.1 |
其他直接与传递依赖以 pom.xml、package.json、锁文件、基础镜像及各上游发行包内的许可证为准。第三方名称和商标归各自权利人所有,引用不代表其对 DockerWeb 的认可或背书。
本仓库不包含 CloakBrowser 编译二进制。browser-runtime-image/Dockerfile 在构建时从 CloakHQ 官方 GitHub Release 下载固定版本并校验 SHA-256。
CloakBrowser wrapper 源码的 MIT 许可不适用于编译后的 Chromium 二进制。该二进制受上游 BINARY-LICENSE.md 单独约束,其中包含禁止未经许可再分发、打包和向第三方提供浏览器能力等限制。使用前必须自行核对当前上游条款;未经 CloakHQ 另行授权,不得公开发布包含该二进制的 Docker Runtime 镜像。本项目的 Apache-2.0 许可不会覆盖或改变该限制。
KasmVNC、基础镜像、字体和其他镜像内组件也继续适用各自许可证。分发构建后的镜像时,分发者必须自行履行相应的源码、版权声明和许可证义务。
DockerWeb 自有源代码采用 Apache License 2.0 开源。第三方组件继续适用各自许可证或使用条款,不因本仓库采用 Apache-2.0 而被重新许可。