106 lines
6.2 KiB
Markdown
106 lines
6.2 KiB
Markdown
# WxAgent Control Plane
|
||
|
||
Go 1.23 控制面与 React 管理端。节点只主动出站连接,控制面不暴露节点端口。
|
||
|
||
## 本地启动
|
||
|
||
```bash
|
||
cd control-plane
|
||
npm --prefix web ci --no-audit --no-fund
|
||
npm --prefix web run build
|
||
go run ./cmd/wxagent-control-plane
|
||
```
|
||
|
||
启动前设置(单节点兼容写法):
|
||
|
||
```bash
|
||
export WXAGENT_NODE_ID=local-node
|
||
export WXAGENT_NODE_TOKEN='local-node-token'
|
||
export WXAGENT_WEB_USER=admin
|
||
export WXAGENT_WEB_PASSWORD='change-me'
|
||
```
|
||
|
||
多节点/多用户使用 JSON map:
|
||
|
||
```bash
|
||
export WXAGENT_NODE_TOKENS='{"node-a":"token-a","node-b":"token-b"}'
|
||
export WXAGENT_WEB_USERS='{"admin":"change-me","auditor":"read-only-password"}'
|
||
```
|
||
|
||
可选:`WXAGENT_CONTROL_PLANE_ADDR`(默认 `127.0.0.1:8090`)、`WXAGENT_CONTROL_PLANE_DATA`(默认 `control-plane-data.json`)。凭据可通过环境变量或 `*_FILE` Secret 文件读取,不写入控制面数据文件和日志。节点 `service.json` 可设置 `remoteConfigurationFile` 指向 CLI 管理的 `remote.json`;修改上报配置会在下一轮生效,修改远程地址/Token 后需重启 Agent。
|
||
|
||
生产控制面可直接启用 TLS 和节点 mTLS:
|
||
|
||
```text
|
||
WXAGENT_CONTROL_PLANE_TLS_CERT_FILE
|
||
WXAGENT_CONTROL_PLANE_TLS_KEY_FILE
|
||
WXAGENT_MTLS_CLIENT_CA_FILE
|
||
WXAGENT_MTLS_REQUIRE_NODE_CERT=true
|
||
WXAGENT_MTLS_REVOKED_CERTS_FILE
|
||
WXAGENT_NODE_TOKENS_FILE=/run/secrets/node_tokens
|
||
WXAGENT_WEB_USERS_FILE=/run/secrets/web_users
|
||
```
|
||
|
||
节点证书按证书原始 DER 的 SHA-256 指纹逐行写入撤销文件;文件读取失败时节点认证拒绝。控制面还提供 `/readyz`,并对数据文件使用单写入者锁、自动轮转备份和任务/事件/审计保留期限。生产示例见 [`compose.production.yml`](./compose.production.yml)。
|
||
|
||
浏览器访问 `http://127.0.0.1:8090/`,登录后管理节点、任务、白名单事件和审计记录。生产部署必须使用 HTTPS、节点 mTLS 和外部 Secret 文件;本地 HTTP 仅用于显式的私有网络/loopback 集成测试。
|
||
|
||
远程只读查询使用同一持久化任务队列,返回 `task_id` 后通过 `GET /v1/tasks/{task_id}` 取结果:
|
||
|
||
```text
|
||
POST /v1/reads/sessions { node_id, account_id, idempotency_key, limit?, offset? }
|
||
POST /v1/reads/contacts { node_id, account_id, idempotency_key, groups_only, contains?, limit?, offset? }
|
||
POST /v1/reads/messages { node_id, account_id, idempotency_key, chat_id, include_content?, limit?, offset? }
|
||
```
|
||
|
||
节点只返回本地已启用且 `identityVerified` 的白名单会话;未授权范围在节点读取前拒绝。`chat_id` 必须使用会话列表返回的稳定会话标识,消息结果是当前微信 UI 可见历史,不是数据库全量历史。联系人查询中的 `chat_id` 使用联系人数据库稳定 ID;所有响应仍受任务结果大小和分页上限约束。
|
||
|
||
## 验证
|
||
|
||
```bash
|
||
cd control-plane
|
||
go test ./...
|
||
go vet ./...
|
||
cd ../control-plane/web
|
||
npm run build
|
||
cd ../..
|
||
./scripts/remote-control-smoke.sh
|
||
```
|
||
|
||
`remote-control-smoke.sh` 会启动一次临时控制面,运行 .NET 节点协议客户端,验证注册、心跳、任务租约、幂等、结果回传和白名单拒绝;不会操作真实微信联系人。
|
||
|
||
控制面并发/读取任务基线(使用已注册的测试节点,不执行写任务):
|
||
|
||
```bash
|
||
WXAGENT_SCALE_WEB_PASSWORD='<test-password>' \
|
||
scripts/control-plane-scale-smoke.py --base-url http://127.0.0.1:8090 \
|
||
--node-id node-a --account-id account-a --requests 100 --workers 10
|
||
```
|
||
|
||
`control-plane-data.sh backup`/`restore` 用于停机前后的手工备份和原子恢复;自动备份由 Store 按 `WXAGENT_CONTROL_PLANE_BACKUP_INTERVAL`(默认 5 分钟)产生。当前 JSON 存储使用 active/passive 单写锁,不支持 active-active 多实例共享写入。
|
||
## Windows 手工验收
|
||
|
||
1. 在已登录且未锁定的微信桌面会话中运行 `WxAgent.Host doctor` 和 `WxAgent.Host inspect-ui --output artifacts/ui-tree.json`,确认账号绑定使用稳定 `accountId`,会话使用稳定 `AutomationId`,不使用昵称/PID/窗口句柄猜测。
|
||
2. 为节点配置 `remote.json`,例如先执行:
|
||
|
||
```powershell
|
||
WxAgent.Host remote auth set --config remote.json --address https://control.example --token $env:WXAGENT_NODE_TOKEN --node node-a --active-account <已确认的accountId>
|
||
WxAgent.Host remote reporting enable --config remote.json
|
||
WxAgent.Host remote reporting account-add --config remote.json --account <已确认的accountId>
|
||
WxAgent.Host remote reporting account-enable --config remote.json --account <已确认的accountId>
|
||
WxAgent.Host remote reporting allow --config remote.json --account <已确认的accountId> --type group --chat-id <已确认的稳定chatId> --identity-verified
|
||
WxAgent.Host remote probe run --config remote.json
|
||
```
|
||
|
||
3. 在节点 `service.json` 设置 `"remoteConfigurationFile": "<remote.json绝对路径>"` 后启动 `serve`。控制面应显示节点 `Online`、已验证账号和心跳版本。
|
||
4. 仅使用文件传输助手、`Hao 豪`、`吉祥三宝`、`消息测试专用群组`做发送/接收验证:先验证允许的群聊事件能到达控制面,再执行 `reporting deny`,确认后续正文不再上传;创建 `send-text` 前确认目标账号、稳定 chat ID 和文本。
|
||
5. 在控制面取消任务并验证节点不执行;断开网络后恢复,确认未完成副作用任务显示 `ResultUnconfirmed`,不会自动重放。
|
||
|
||
未解锁的 Session 0、昵称、PID、窗口句柄和未确认的运行时身份都应视为验收失败;手工验收不得改写微信数据库或绕过安全机制。
|
||
|
||
## 容器镜像
|
||
|
||
`.gitea/workflows/build-web-image.yml` 会构建并推送 `git.ipao.vip/<owner>/<repo>` 镜像:主分支额外更新 `latest`,`v*` 标签额外更新对应版本标签。请在 Gitea 账号级 Secrets 配置 `REGISTRY_TOKEN`;登录用户名使用仓库所有者。
|
||
|
||
生产环境不要提交 `compose.production.yml` 所引用的 `secrets/` 文件。将节点 Token map、Web 用户 map、TLS 私钥、客户端 CA 和撤销指纹列表放入外部 Secret 管理或受 ACL 保护的部署目录,再运行 `docker compose -f compose.production.yml up -d`。证书泄露时可用 `scripts/revoke-client-certificate.sh` 更新指纹列表,并重建 Docker Secret 挂载的容器。
|