Files
auto_control/doc/API.md
T

20 KiB
Raw Blame History

API 接口文档

platform-tools Web 后台提供 JSON API,所有接口需登录后访问(Flask-Login session 认证)。

Base URL:http://localhost:18050

认证方式:Cookie Session(先 POST /login 获取 session cookie,后续请求带上)

通用响应格式:

{"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 秒缓存)。

响应:

{
  "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 扫描(后台异步执行,不打扰设备)。

响应:

{"ok": true, "msg": "扫描已启动"}
{"ok": false, "error": "已有扫描在进行中"}

GET /api/devices

返回所有在线设备 serial 列表(供分组表单勾选用)。

响应:

{"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

返回所有已注册任务类型。

响应:

{
  "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 任务类型

响应:

{
  "ok": true,
  "actions": [
    {"action_type": "like", "name": "点赞", "description": "...", "default_params": {"rate": 0.3}}
  ]
}

5. 任务计划 CRUD

GET /api/jobs

列出所有任务计划。

响应:

{
  "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):

{
  "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
}

响应:

{"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):

{"params": {"watch_count": 100}}

响应:

{"ok": true, "msg": "任务已更新", "job": {"...": "..."}}

(权限:任务管理)

删除任务计划。

响应:

{"ok": true, "msg": "任务已删除"}

(权限:任务管理)

立即执行任务(异步,不阻塞)。

响应:

{"ok": true, "msg": "任务 抖音养号 已触发"}

(权限:任务管理)

启用/停用任务。

请求(JSON):

{"enabled": false}

响应:

{"ok": true, "msg": "任务已停用"}

6. 设备分组

GET /api/groups

列出所有分组。

响应:

{
  "ok": true,
  "groups": [
    {"name": "A组", "serials": ["192.168.1.100:5555"], "description": "测试组"}
  ]
}

(权限:任务管理)

创建分组。

请求(JSON):

{"name": "A组", "serials": ["192.168.1.100:5555"], "description": "测试组"}

(权限:任务管理)

更新分组。

请求(JSON):

{"serials": ["192.168.1.100:5555", "192.168.1.101:5555"], "description": "更新描述"}

(权限:任务管理)

删除分组。


7. 运行控制

(权限:设备控制)

停止单台设备的 worker(并阻止后续重试)。

请求(JSON):

{"serial": "192.168.1.100:5555"}

响应:

{"ok": true, "msg": "已发送停止信号给 192.168.1.100:5555"}

(权限:设备控制)

停止所有运行中的 worker。

响应:

{"ok": true, "stopped": ["192.168.1.100:5555", "192.168.1.101:5555"]}

(权限:设备控制)

(已随 STF 摘除移除,此接口不再存在;设备互斥由调度器内存锁保证)

响应:

{"ok": true, "released": ["192.168.1.100:5555"]}

8. 用户管理

(仅管理员)

列出所有用户。

响应:

{"ok": true, "users": [{"id": 1, "username": "admin", "is_admin": true}]}

(仅管理员)

创建用户。

请求(JSON):

{"username": "user1", "password": "pass123", "is_admin": false}

(仅管理员)

更新用户(修改密码/管理员权限)。

请求(JSON):

{"password": "newpass", "is_admin": true}

(仅管理员)

删除用户(不能删除 admin 和当前登录用户)。


9. 日志

(权限:日志查看)

查看日志文件内容。

参数:

参数 类型 默认 说明
file string core.log 日志文件名
lines int 300 返回最后 N 行

可选文件:core.log / task.log / web.log / action.log

响应:

{
  "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):

{"name": "登录流程", "icon": "📦", "steps": [{"type": "click", "...": "..."}]}

(权限:任务管理)

更新自定义动作。

(权限:任务管理)

删除自定义动作。


11. 元素抓取(uiauto2)

GET /api/uiauto/status

探测 uiauto2 本地服务是否运行。

响应:

{"ok": true, "running": true}

(权限:设备控制)

获取 uiauto2 已连接的设备列表。

响应:

{"ok": true, "devices": [{"serial": "192.168.1.100:5555", "model": "Pixel 6"}]}

(权限:设备控制)

通过 uiauto2 获取设备截图(JPEG)。

参数:

参数 类型 说明
serial string 设备 serial

响应:成功返回 image/jpeg,失败返回 JSON 错误。

(权限:设备控制)

获取设备 UI 元素树。

参数:

参数 类型 说明
serial string 设备 serial

响应:

{
  "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。

响应:

{
  "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 文件

响应:

{"ok": true, "apk": {"...": "..."}, "msg": "上传成功: 抖音"}

(权限:应用管理)

删除 APK 文件和记录。

(权限:应用管理)

批量安装 APK 到指定设备。

请求(JSON):

{"apk_id": "abc123", "serials": ["192.168.1.100:5555", "192.168.1.101:5555"]}

响应:

{"ok": true, "msg": "开始安装 抖音 到 2 台设备"}

GET /api/apks/install/devices

可安装设备列表:设备池在线设备(标记 pool)+ 本机 adb 设备(含 USB 有线连接,serial 无冒号标记 usb)。 安装弹窗用此列表,USB 设备安装时跳过 adb connect 直接安装。

响应:

{"ok": true, "devices": [
  {"serial": "100.100.10.11:5555", "model": "22120RN86C", "source": "stf"},
  {"serial": "ZY322ABCDEF", "model": "", "source": "usb"}
]}

GET /api/apks/install/status

获取安装任务实时状态。

响应:

{
  "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。

响应:

{"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 帧/秒)。

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):

{"mode": "on", "serials": ["100.100.10.11:5555"]}

serials 可选:指定设备(离线自动过滤);不带则作用于全部在线设备。息屏会中断运行中的任务,前端有确认提示。

GET /api/adb/devices

维护终端设备列表:本地 adb 已连接(含 offline)+ 设备池已配置设备(标记 pool)。 供终端设备选择器使用——选中后前端自动附加 -s <serial>。

响应:

{"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):

{"cmd": "adb -s 100.100.10.11:5555 shell ls /sdcard"}

(开头的 adb 前缀可省略)

安全红线:包含 kill-server / disconnect 的命令直接拒绝(400)——会断开共享的 adb transport,导致全部设备连接重建。

响应:

{"ok": true, "stdout": "...", "stderr": "", "code": 0}

14. 步骤测试(仅管理员可触发,需"设备控制"权限)

POST /api/steps/test

在指定设备上单步试执行(步骤编辑器"测试此步骤"按钮),验证选择器是否命中。 只读连接(adb connect + u2),与运行中任务互不干扰。

请求(JSON):

{
  "serial": "100.100.10.11:5555",
  "step": {"type": "click", "label": "测试", "params": {"selector_type": "xpath", "selector_value": "//*[@resource-id=\"x\"]"}}
}

响应:

{"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

配置状态检查。

响应:

{"ok": true, "configured": false, "hint": "TAILSCALE_API_KEY(...);TAILSCALE_TAILNET(...)"}

GET /api/tailscale/devices

列出 tailnet 全部设备。

响应:

{"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):

{"key_expiry_disabled": true}

POST /api/tailscale/devices/:device_id/ip

设置设备的 Tailscale IPv4 地址(未公开端点,实测可用)。

请求(JSON):

{"ipv4": "100.100.10.16"}

⚠ 改 IP 会断开设备当前 tailscale 会话;平台设备池 serial 随之变化,需同步更新设备池/分组/任务目标。

DELETE /api/tailscale/devices/:device_id

从 tailnet 移除设备(下次上线需重新授权)。

POST /api/tailscale/authkey

生成设备接入 auth key(key 只返回一次)。

请求(JSON):

{"description": "新设备接入", "reusable": false, "ephemeral": false,
 "preauthorized": true, "expiry_seconds": 3600}

响应:

{"ok": true, "key": "tskey-auth-...", "id": "k1", "expires": "2026-08-11T01:00:00Z"}

16. 工具(仅管理员)

工具页(剪贴板注入 / 应用版本管理 / 设备池管理)接口,均仅管理员可用。

POST /api/tools/clipboard/set

剪贴板注入:把指定文字写入一台或多台设备的剪贴板。

请求(JSON):

{"serials": ["100.100.10.11:5555", "0123456789ABCDEF"], "text": "要注入的文字"}

设备来源与维护终端一致(本地 adb 含 USB + 设备池)。 实现:u2 jsonrpc.setClipboard(实测 cmd clipboard 在 MIUI 上不存在),支持中文/引号/换行; IP 设备先 adb connect(已连接跳过,绝不 disconnect),USB 设备首次自动推送 atx-agent。

响应:

{"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):

{"pkg": "com.ss.android.ugc.aweme"}

响应:

{"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 字段。