Files
auto_control/doc/API.md
T

852 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# API 接口文档
`platform-tools` Web 后台提供 JSON API,所有接口需登录后访问(Flask-Login session 认证)。
**Base URL**:`http://localhost:18050`
**认证方式**:Cookie Session(先 POST `/login` 获取 session cookie,后续请求带上)
**通用响应格式**:
```json
{"ok": true, "data": "..."}
{"ok": false, "error": "错误信息"}
```
---
**权限模型**(v2 起):
- 所有接口需登录;**查看类 GET 接口**(状态/列表/截图)所有登录用户可用
- **写操作按权限位授权**(管理员拥有全部权限):
| 权限位 | 中文 | 覆盖接口 |
|--------|------|---------|
| `tasks` | 任务管理 | 任务/自定义动作/分组的增删改、启停、立即执行 |
| `devices` | 设备控制 | 停止设备、释放占用、清除异常、前台扫描、元素抓取 |
| `apks` | 应用管理 | APK 上传、安装、删除 |
| `logs` | 日志查看 | `GET /api/logs` |
- **用户管理接口仅管理员可用**(普通用户即使被授予业务权限也无法访问)
- 无权限访问返回 `403 {"ok": false, "error": "无权限执行此操作..."}`
- 当前用户权限查询:`GET /api/me`
---
## 1. 认证
### POST /login
用户登录。
**请求**(form-data):
| 参数 | 类型 | 说明 |
|------|------|------|
| username | string | 用户名 |
| password | string | 密码 |
**响应**:成功重定向到 `/`,失败返回登录页(含 error 信息)。
### GET /logout
登出,重定向到登录页。
---
## 2. 页面路由
### GET /
单页应用首页(需登录)。响应头设置 `Cache-Control: no-store` 防止缓存。
### GET /login
登录页面(GET)。
---
## 3. 设备状态
### GET /api/status
获取设备池 + Worker 综合状态(带 5 秒缓存)。
**响应**:
```json
{
"ok": true,
"server_time": 1700000000.0,
"fg_scanning": false,
"fg_last_scan": 1700000000.0,
"devices": [
{
"serial": "192.168.1.100:5555",
"model": "Pixel 6",
"device_name": "测试机1",
"present": true,
"ready": true,
"stf_occupied": false,
"owner": "",
"worker_status": "idle",
"foreground_app": "空闲",
"progress": {"done": 5, "total": 80, "unit": "视频", "action_counts": {"like": 3}},
"current_action": "观看视频 6",
"last_error": "",
"running_job": "",
"task_job": "",
"attempt": 0
}
]
}
```
**worker_status 取值**:`idle` / `connecting` / `running` / `done` / `error` / `failed` / `released`
(权限:设备控制)
手动触发前台 App 扫描(后台异步执行,不打扰设备)。
**响应**:
```json
{"ok": true, "msg": "扫描已启动"}
{"ok": false, "error": "已有扫描在进行中"}
```
### GET /api/devices
返回所有在线设备 serial 列表(供分组表单勾选用)。
**响应**:
```json
{"ok": true, "devices": ["192.168.1.100:5555", "192.168.1.101:5555"]}
```
### GET /api/device/screenshot
获取设备当前画面截图(PNG)。
**参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| serial | string | 设备 serial |
| t | int | 时间戳(避免缓存,前端自动加) |
**响应**:成功返回 `image/png`,失败返回 JSON 错误。
> 用 `adb exec-out screencap -p`,只读操作,任务运行中调用安全。
---
## 4. 任务类型
### GET /api/task_types
返回所有已注册任务类型。
**响应**:
```json
{
"ok": true,
"task_types": [
{
"task_type": "douyin_nurture",
"name": "抖音养号",
"description": "自动观看抖音视频...",
"default_params": {"watch_count": 80, "...": "..."}
},
{
"task_type": "generic_steps",
"name": "通用步骤",
"description": "通过步骤编辑器编排...",
"default_params": {"steps": [...]}
}
]
}
```
### GET /api/actions
返回指定任务类型支持的专属操作。
**参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| task_type | string | 任务类型 |
**响应**:
```json
{
"ok": true,
"actions": [
{"action_type": "like", "name": "点赞", "description": "...", "default_params": {"rate": 0.3}}
]
}
```
---
## 5. 任务计划 CRUD
### GET /api/jobs
列出所有任务计划。
**响应**:
```json
{
"ok": true,
"jobs": [{"id": "abc123", "name": "抖音养号", "task_type": "douyin_nurture",
"next_run": "2026-08-12 09:00", "...": "..."}],
"task_types": [...]
}
```
`next_run`:下次真正执行时间(已按运行窗口跳过窗口外触发点,格式 `YYYY-MM-DD HH:MM`);手动任务/已停用为 `null`。
(权限:任务管理)
创建任务计划。
**请求**(JSON):
```json
{
"name": "抖音养号",
"task_type": "douyin_nurture",
"target": {"mode": "all"},
"params": {"watch_count": 50, "actions": {"like": {"params": {"rate": 0.5}}}},
"schedule": {"mode": "cron", "cron": "0 */2 * * *"},
"retry": {"max_attempts": 3, "delay": 60},
"enabled": true
}
```
**响应**:
```json
{"ok": true, "msg": "任务已创建", "job": {"id": "abc123", "...": "..."}}
```
`schedule` 字段格式:
| 字段 | 说明 |
|------|------|
| `mode` | `once` 手动 / `cron` 定时启动 / `cron_stop` 定时启动+停止 |
| `cron` | 标准 5 段 cron:`分 时 日 月 周`(周 `0`/`7`=周日);如 `0 */2 * * *` 每 2 小时整点 |
| `stop_cron` | (cron_stop 必填)到点停止本任务 worker |
| `window` | 可选,运行窗口 `{"start": "21:00", "end": "09:00"}`(每天重复,支持跨午夜)。窗口外定时触发和手动执行(`POST /api/jobs/:id/run`)均不启动,手动执行返回错误提示 |
(权限:任务管理)
更新任务计划。只需传要更新的字段。
**请求**(JSON):
```json
{"params": {"watch_count": 100}}
```
**响应**:
```json
{"ok": true, "msg": "任务已更新", "job": {"...": "..."}}
```
(权限:任务管理)
删除任务计划。
**响应**:
```json
{"ok": true, "msg": "任务已删除"}
```
(权限:任务管理)
立即执行任务(异步,不阻塞)。
**响应**:
```json
{"ok": true, "msg": "任务 抖音养号 已触发"}
```
(权限:任务管理)
启用/停用任务。
**请求**(JSON):
```json
{"enabled": false}
```
**响应**:
```json
{"ok": true, "msg": "任务已停用"}
```
---
## 6. 设备分组
### GET /api/groups
列出所有分组。
**响应**:
```json
{
"ok": true,
"groups": [
{"name": "A组", "serials": ["192.168.1.100:5555"], "description": "测试组"}
]
}
```
(权限:任务管理)
创建分组。
**请求**(JSON):
```json
{"name": "A组", "serials": ["192.168.1.100:5555"], "description": "测试组"}
```
(权限:任务管理)
更新分组。
**请求**(JSON):
```json
{"serials": ["192.168.1.100:5555", "192.168.1.101:5555"], "description": "更新描述"}
```
(权限:任务管理)
删除分组。
---
## 7. 运行控制
(权限:设备控制)
停止单台设备的 worker(并阻止后续重试)。
**请求**(JSON):
```json
{"serial": "192.168.1.100:5555"}
```
**响应**:
```json
{"ok": true, "msg": "已发送停止信号给 192.168.1.100:5555"}
```
(权限:设备控制)
停止所有运行中的 worker。
**响应**:
```json
{"ok": true, "stopped": ["192.168.1.100:5555", "192.168.1.101:5555"]}
```
(权限:设备控制)
(已随 STF 摘除移除,此接口不再存在;设备互斥由调度器内存锁保证)
**响应**:
```json
{"ok": true, "released": ["192.168.1.100:5555"]}
```
---
## 8. 用户管理
(仅管理员)
列出所有用户。
**响应**:
```json
{"ok": true, "users": [{"id": 1, "username": "admin", "is_admin": true}]}
```
(仅管理员)
创建用户。
**请求**(JSON):
```json
{"username": "user1", "password": "pass123", "is_admin": false}
```
(仅管理员)
更新用户(修改密码/管理员权限)。
**请求**(JSON):
```json
{"password": "newpass", "is_admin": true}
```
(仅管理员)
删除用户(不能删除 admin 和当前登录用户)。
---
## 9. 日志
(权限:日志查看)
查看日志文件内容。
**参数**:
| 参数 | 类型 | 默认 | 说明 |
|------|------|------|------|
| file | string | `core.log` | 日志文件名 |
| lines | int | 300 | 返回最后 N 行 |
**可选文件**:`core.log` / `task.log` / `web.log` / `action.log`
**响应**:
```json
{
"ok": true,
"content": "2026-08-08 10:00:00 [INFO] [core.worker] ...",
"file": "core.log",
"files": {"core": "core.log", "task": "task.log", "web": "web.log", "action": "action.log"}
}
```
---
## 10. 自定义动作
### GET /api/custom_actions
列出所有自定义动作(步骤打包)。
(权限:任务管理)
创建自定义动作。
**请求**(JSON):
```json
{"name": "登录流程", "icon": "📦", "steps": [{"type": "click", "...": "..."}]}
```
(权限:任务管理)
更新自定义动作。
(权限:任务管理)
删除自定义动作。
---
## 11. 元素抓取(uiauto2)
### GET /api/uiauto/status
探测 uiauto2 本地服务是否运行。
**响应**:
```json
{"ok": true, "running": true}
```
(权限:设备控制)
获取 uiauto2 已连接的设备列表。
**响应**:
```json
{"ok": true, "devices": [{"serial": "192.168.1.100:5555", "model": "Pixel 6"}]}
```
(权限:设备控制)
通过 uiauto2 获取设备截图(JPEG)。
**参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| serial | string | 设备 serial |
**响应**:成功返回 `image/jpeg`,失败返回 JSON 错误。
(权限:设备控制)
获取设备 UI 元素树。
**参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| serial | string | 设备 serial |
**响应**:
```json
{
"ok": true,
"elements": [
{
"depth": 0,
"name": "android.widget.FrameLayout",
"resource_id": "",
"text": "",
"description": "",
"class": "android.widget.FrameLayout",
"bounds": "[0,0][1080,2400]",
"suggested": {"type": "xpath", "value": "//*"}
}
]
}
```
---
## 12. 应用管理(APK)
### GET /api/apks
列出所有已上传的 APK。
**响应**:
```json
{
"ok": true,
"apks": [
{
"id": "abc123",
"display_name": "抖音",
"package_name": "com.ss.android.ugc.aweme",
"version_name": "25.0.0",
"version_code": 2500,
"size": 104857600,
"upload_time": "2026-08-08 10:00:00"
}
]
}
```
(权限:应用管理)
上传 APK 文件(自动解析包名/版本/应用名)。
**请求**(multipart/form-data):
| 字段 | 类型 | 说明 |
|------|------|------|
| file | file | APK 文件 |
**响应**:
```json
{"ok": true, "apk": {"...": "..."}, "msg": "上传成功: 抖音"}
```
(权限:应用管理)
删除 APK 文件和记录。
(权限:应用管理)
批量安装 APK 到指定设备。
**请求**(JSON):
```json
{"apk_id": "abc123", "serials": ["192.168.1.100:5555", "192.168.1.101:5555"]}
```
**响应**:
```json
{"ok": true, "msg": "开始安装 抖音 到 2 台设备"}
```
### GET /api/apks/install/devices
可安装设备列表:设备池在线设备(标记 `pool`)+ 本机 adb 设备(**含 USB 有线连接**,serial 无冒号标记 `usb`)。
安装弹窗用此列表,USB 设备安装时跳过 adb connect 直接安装。
**响应**:
```json
{"ok": true, "devices": [
{"serial": "100.100.10.11:5555", "model": "22120RN86C", "source": "stf"},
{"serial": "ZY322ABCDEF", "model": "", "source": "usb"}
]}
```
### GET /api/apks/install/status
获取安装任务实时状态。
**响应**:
```json
{
"ok": true,
"status": {
"apk_name": "抖音",
"finished": false,
"total": 2,
"success": 1,
"failed": 0,
"skipped": 0,
"installing": 1,
"pending": 0,
"items": {
"192.168.1.100:5555": {"name": "Pixel 6", "status": "success", "msg": "安装成功(已验证)"},
"192.168.1.101:5555": {"name": "Pixel 7", "status": "installing", "msg": "正在安装..."}
}
}
}
```
---
## 13. 维护 / 工具(仅管理员)
以下接口都**仅管理员可用**(普通用户即使有业务权限也访问不了,返回 403)。
设备池管理、远程看屏、adb 终端等集中在"工具"页(页内子分栏)。
### GET /api/devices/pool
设备池清单(SQLite devices 表),含实时在线状态。**权限**:`devices`。
**响应**:
```json
{"ok": true, "devices": [
{"serial": "100.100.10.20:5555", "name": "", "model": "22120RN86C",
"enabled": true, "online": true, "note": "", "created_at": "2026-08-17 11:10"}
]}
```
### POST /api/devices/pool/add
添加/更新设备(upsert)。**权限**:`devices`。
**请求**(JSON):`{"serial": "100.100.10.20:5555", "name": "备注", "note": ""}`
IP:5555 设备添加后立即尝试 adb connect,并后台采集型号(getprop ro.product.model)。
### POST /api/devices/pool/toggle
启用/停用设备(停用后不参与任务调度)。**权限**:`devices`。
**请求**(JSON):`{"serial": "...", "enabled": false}`
### POST /api/devices/pool/remove
从设备池删除(不再参与调度,不影响设备本身)。**权限**:`devices`。
**请求**(JSON):`{"serial": "..."}`
### POST /api/devices/pool/reconnect
一键重连:并发 adb connect 池内全部 IP:5555 设备(后台执行),完成后自动批量刷新型号。**权限**:`devices`。
### POST /api/devices/pool/refresh_models
批量采集池内在线设备的型号(后台执行)。**权限**:`devices`。
### GET /api/screen/stream
远程看屏:MJPEG 实时画面流(`multipart/x-mixed-replace`)。**权限**:`devices`。
`?serial=xxx` 指定设备;浏览器 `<img src>` 直接渲染,客户端断开自动停止。数据源 u2(atx-agent minicap,约 3-6 帧/秒)。
### GET /api/screen/thumb
大屏缩略图:单张 JPEG(360px 宽,质量 55)。**权限**:`devices`。
`?serial=xxx` 指定设备。监控大屏按设备每 ~2.5s 轮询一帧(页面不可见时暂停);
一次性请求(非流),与任务并发安全(与任务截图同走 u2 minicap)。
### GET /wall
监控大屏页面(全屏深色控制室风格,登录后可访问):设备卡片网格(缩略图/型号/
状态/当前动作/进度)、顶部统计与时钟;状态每 5s 刷新、缩略图每 2.5s 轮询。
20 台设备整体开销约 0.2 核 CPU + 100KB/s 带宽,普通电脑无压力。
### POST /api/screen/tap
点击设备屏幕。**权限**:`devices`。
**请求**(JSON):`{"serial": "...", "x": 360, "y": 800}`(设备原生分辨率坐标)
### POST /api/screen/swipe
滑动。**请求**(JSON):`{"serial": "...", "x1": 360, "y1": 1200, "x2": 360, "y2": 600, "duration": 0.2}`
### POST /api/screen/key
按键。**请求**(JSON):`{"serial": "...", "key": "back"}`。
支持:back/home/recent/menu/power/volume_up/volume_down/enter/delete/search/camera
### POST /api/screen/text
输入文字(需焦点在输入框)。**请求**(JSON):`{"serial": "...", "text": "你好"}`
### POST /api/device/screen_all
批量亮屏/息屏(并发)。**权限**:`devices`。
**请求**(JSON):
```json
{"mode": "on", "serials": ["100.100.10.11:5555"]}
```
`serials` 可选:指定设备(离线自动过滤);不带则作用于全部在线设备。息屏会中断运行中的任务,前端有确认提示。
### GET /api/adb/devices
维护终端设备列表:本地 adb 已连接(含 offline)+ 设备池已配置设备(标记 `pool`)。
供终端设备选择器使用——选中后前端自动附加 `-s <serial>`。
**响应**:
```json
{"ok": true, "devices": [
{"serial": "100.100.10.11:5555", "state": "device"},
{"serial": "100.100.10.13:5555", "state": "offline"},
{"serial": "100.100.10.12:5555", "state": "stf"}
]}
```
### POST /api/adb/cmd
adb 远程终端:用平台 adb 二进制执行任意 adb 命令(20s 超时)。
**请求**(JSON):
```json
{"cmd": "adb -s 100.100.10.11:5555 shell ls /sdcard"}
```
(开头的 `adb` 前缀可省略)
**安全红线**:包含 `kill-server` / `disconnect` 的命令直接拒绝(400)——会断开共享的 adb transport,导致全部设备连接重建。
**响应**:
```json
{"ok": true, "stdout": "...", "stderr": "", "code": 0}
```
---
## 14. 步骤测试(仅管理员可触发,需"设备控制"权限)
### POST /api/steps/test
在指定设备上单步试执行(步骤编辑器"测试此步骤"按钮),验证选择器是否命中。
只读连接(adb connect + u2),与运行中任务互不干扰。
**请求**(JSON):
```json
{
"serial": "100.100.10.11:5555",
"step": {"type": "click", "label": "测试", "params": {"selector_type": "xpath", "selector_value": "//*[@resource-id=\"x\"]"}}
}
```
**响应**:
```json
{"ok": true, "msg": "步骤已执行(测试)", "result": "命中"}
```
`result`:`命中` / `未找到` / `已执行`(无选择器命中语义的步骤)。
无"设备控制"权限返回 403。
---
## 15. Tailscale 管理(仅管理员)
工具页"Tailscale 管理"子分栏,调用 Tailscale 官方 API v2。所有接口仅管理员可用。
前置:`.env` 配置 `TAILSCALE_API_KEY`(Settings → API Access Tokens)与
`TAILSCALE_TAILNET`(tailnet 名,个人账号一般为邮箱前缀);未配置返回 502 并附提示。
设备 IP 由 tailnet 分配,API 不可修改,列表只读展示。
### GET /api/tailscale/status
配置状态检查。
**响应**:
```json
{"ok": true, "configured": false, "hint": "TAILSCALE_API_KEY(...);TAILSCALE_TAILNET(...)"}
```
### GET /api/tailscale/devices
列出 tailnet 全部设备。
**响应**:
```json
{"ok": true, "devices": [
{"id": "d1", "name": "dev-a", "hostname": "dev-a", "os": "linux",
"addresses": ["100.100.10.11"], "authorized": true,
"key_expiry_disabled": false, "online": true, "last_seen": "...", "tags": []}
]}
```
### POST /api/tailscale/devices/:device_id
更新设备(按传入字段分发到 Tailscale 专属端点,`POST /device/{id}` 本身是 405):
`name` → `/name` 显示名;`authorized` → `/authorized` 授权开关;
`key_expiry_disabled` → `/key`(true=密钥永不过期,即关闭设备密钥验证,恢复后按原定过期时间执行)。
**请求**(JSON):
```json
{"key_expiry_disabled": true}
```
### POST /api/tailscale/devices/:device_id/ip
设置设备的 Tailscale IPv4 地址(未公开端点,实测可用)。
**请求**(JSON):
```json
{"ipv4": "100.100.10.16"}
```
⚠ 改 IP 会断开设备当前 tailscale 会话;平台设备池 serial 随之变化,需同步更新设备池/分组/任务目标。
### DELETE /api/tailscale/devices/:device_id
从 tailnet 移除设备(下次上线需重新授权)。
### POST /api/tailscale/authkey
生成设备接入 auth key(key 只返回一次)。
**请求**(JSON):
```json
{"description": "新设备接入", "reusable": false, "ephemeral": false,
"preauthorized": true, "expiry_seconds": 3600}
```
**响应**:
```json
{"ok": true, "key": "tskey-auth-...", "id": "k1", "expires": "2026-08-11T01:00:00Z"}
```
---
## 16. 工具(仅管理员)
工具页(剪贴板注入 / 应用版本管理 / 设备池管理)接口,均仅管理员可用。
### POST /api/tools/clipboard/set
剪贴板注入:把指定文字写入一台或多台设备的剪贴板。
**请求**(JSON):
```json
{"serials": ["100.100.10.11:5555", "0123456789ABCDEF"], "text": "要注入的文字"}
```
设备来源与维护终端一致(本地 adb 含 USB + 设备池)。
实现:u2 `jsonrpc.setClipboard`(实测 `cmd clipboard` 在 MIUI 上不存在),支持中文/引号/换行;
IP 设备先 adb connect(已连接跳过,绝不 disconnect),USB 设备首次自动推送 atx-agent。
**响应**:
```json
{"ok": true, "results": {"100.100.10.11:5555": {"ok": true, "msg": "已注入"}},
"ok_count": 1, "fail_count": 0, "error": null}
```
### POST /api/tools/appver
应用版本管理:查询所有设备上指定包名的安装情况与版本号(并发 10 台)。
**请求**(JSON):
```json
{"pkg": "com.ss.android.ugc.aweme"}
```
**响应**:
```json
{"ok": true, "total": 8, "fail": 0,
"results": {"100.100.10.11:5555": {"installed": true, "version_name": "28.5.0", "version_code": "280500"}}}
```
未安装返回 `installed: false`;查询失败的设备带 `error` 字段。