docs: 文档同步(README/API/架构/部署)
This commit is contained in:
@@ -200,7 +200,11 @@ adb disconnect + STF release(释放设备)
|
|||||||
| `cron` | 定时启动:到 cron 时间点自动启动所有目标设备 |
|
| `cron` | 定时启动:到 cron 时间点自动启动所有目标设备 |
|
||||||
| `cron_stop` | 定时启停:启动 cron 到点启动,停止 cron 到点停止本任务 worker |
|
| `cron_stop` | 定时启停:启动 cron 到点启动,停止 cron 到点停止本任务 worker |
|
||||||
|
|
||||||
cron 表达式为标准 5 段格式:`分 时 日 月 周`(如 `0 9 * * *` = 每天 9:00)
|
**简单设置**:编辑任务时选"定时启动/定时启动+停止"后,频率用下拉选择——每天(选时间)、每小时(整点)、每隔 N 小时、每周(选星期+时间)——cron 表达式自动生成,不需要懂 cron 语法。老手可在"自定义 cron(高级)"里直接填。
|
||||||
|
|
||||||
|
cron 表达式为标准 5 段格式:`分 时 日 月 周`(如 `0 9 * * *` = 每天 9:00,`0 */2 * * *` = 每 2 小时整点,周 `0`/`7` 均为周日)
|
||||||
|
|
||||||
|
**运行窗口**:任务可勾选"启用运行窗口",设置每天允许运行的时间段(如 `21:00-09:00` = 晚 9 点到次日早 9 点,支持跨午夜)。窗口外**定时触发和手动执行都不会启动**(手动执行会提示"当前不在运行窗口内")。典型用法:`每小时`定时 + 窗口 `09:00-21:00`,即只在白天每小时跑一次。
|
||||||
|
|
||||||
### 设备分组
|
### 设备分组
|
||||||
|
|
||||||
@@ -241,15 +245,34 @@ self.set_progress(done=5, total=80, unit="视频",
|
|||||||
|
|
||||||
### 页面结构
|
### 页面结构
|
||||||
|
|
||||||
单页应用(`templates/admin/monitor.html`),5 个 Tab:
|
单页应用(`templates/admin/monitor.html`),6 个 Tab:
|
||||||
|
|
||||||
| Tab | 功能 |
|
| Tab | 功能 | 可见性 |
|
||||||
|-----|------|
|
|-----|------|--------|
|
||||||
| 监控 | 设备状态大屏:在线/离线、运行任务、当前动作、进度条、前台 App、截图、停止 |
|
| 监控 | 设备状态大屏:在线/离线、运行任务、当前动作、进度条、前台 App、截图、停止 | 所有登录用户(设备操作按钮需"设备控制"权限) |
|
||||||
| 任务 | 任务计划 CRUD:新建/编辑/删除/启用停用/立即执行 |
|
| 任务 | 任务计划 CRUD:新建/编辑/删除/启用停用/立即执行 | 所有登录用户可看,写操作需"任务管理"权限 |
|
||||||
| 分组 | 设备分组管理:创建/编辑/删除分组 |
|
| 应用 | APK 上传/安装/删除 | 需"应用管理"权限 |
|
||||||
| 日志 | 实时日志查看:按模块切换(core/task/web/action) |
|
| 分组 | 设备分组管理:创建/编辑/删除分组 | 所有登录用户可看,写操作需"任务管理"权限 |
|
||||||
| 用户 | 用户管理:创建/删除/修改密码 |
|
| 日志 | 实时日志查看:按模块切换(core/task/web/action) | 需"日志查看"权限 |
|
||||||
|
| 用户 | 用户管理:创建/删除/修改密码/分配权限 | 仅管理员 |
|
||||||
|
| 维护 | STF 容器一键重启、adb 远程终端(设备快捷选择/自动 `-s`、卸载 STF agent、重连设备) | 仅管理员 |
|
||||||
|
|
||||||
|
### 用户与权限
|
||||||
|
|
||||||
|
默认账号 `admin` / `admin123`(管理员,拥有全部权限)。管理员可在"用户"Tab 创建普通用户并分配权限:
|
||||||
|
|
||||||
|
| 权限位 | 说明 | 覆盖功能 |
|
||||||
|
|--------|------|---------|
|
||||||
|
| 任务管理 `tasks` | 任务/自定义动作/分组的增删改、启停、立即执行 | 任务 Tab、分组 Tab 的写操作 |
|
||||||
|
| 设备控制 `devices` | 停止设备、释放占用、清除异常、前台扫描、元素抓取 | 监控 Tab 的设备操作按钮、步骤编辑器的"抓取元素" |
|
||||||
|
| 应用管理 `apks` | APK 上传、安装、删除 | 应用 Tab |
|
||||||
|
| 日志查看 `logs` | 日志页 | 日志 Tab |
|
||||||
|
|
||||||
|
规则:
|
||||||
|
- **管理员拥有全部权限**,不受权限位限制
|
||||||
|
- 查看类接口(设备/任务/分组列表、状态、截图)所有登录用户可用
|
||||||
|
- **用户管理仅管理员可用**;不能删除/取消最后一个管理员
|
||||||
|
- 无权限的 tab 和按钮在界面上自动隐藏(后端同样拦截,返回 403)
|
||||||
|
|
||||||
### 前台 App 扫描
|
### 前台 App 扫描
|
||||||
|
|
||||||
@@ -298,7 +321,17 @@ self.set_progress(done=5, total=80, unit="视频",
|
|||||||
| 步骤类型 | 说明 |
|
| 步骤类型 | 说明 |
|
||||||
|---------|------|
|
|---------|------|
|
||||||
| `open_app` | 启动 App(指定包名,可选等待首页) |
|
| `open_app` | 启动 App(指定包名,可选等待首页) |
|
||||||
|
| `stop_app` | 强制结束 App(am force-stop,清后台,下次打开冷启动) |
|
||||||
|
| `screen_on` | 亮屏(息屏时唤醒并滑动解锁) |
|
||||||
|
| `screen_off` | 息屏 |
|
||||||
|
| `keep_screen` | 保持亮屏/恢复自动息屏(充电时屏幕常亮,适合长任务) |
|
||||||
|
| `key_event` | 按键:返回/Home/回车/菜单等(退出评论、返回上一页) |
|
||||||
| `swipe` | 滑动(上/下/左/右,可配置时长) |
|
| `swipe` | 滑动(上/下/左/右,可配置时长) |
|
||||||
|
| `swipe_until` | 滑动直到元素出现(最多 N 次,可选找到后点击) |
|
||||||
|
| `click_xy` | 点击坐标(屏幕百分比,无选择器时兜底) |
|
||||||
|
| `long_click` | 长按元素(选择器 + 时长) |
|
||||||
|
| `wait_el` | 等待元素出现(条件等待,替代固定时长) |
|
||||||
|
| `input_text` | 输入文字(随机候选/指定文字,可选输入前先清空) |
|
||||||
| `click` | 点击元素(支持 xpath/description/resourceId/text 选择器) |
|
| `click` | 点击元素(支持 xpath/description/resourceId/text 选择器) |
|
||||||
| `input_text` | 输入文字(随机候选/固定文字) |
|
| `input_text` | 输入文字(随机候选/固定文字) |
|
||||||
| `wait` | 等待(可配置时长范围) |
|
| `wait` | 等待(可配置时长范围) |
|
||||||
|
|||||||
+141
-28
@@ -14,6 +14,21 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
**权限模型**(v2 起):
|
||||||
|
- 所有接口需登录;**查看类 GET 接口**(状态/列表/截图)所有登录用户可用
|
||||||
|
- **写操作按权限位授权**(管理员拥有全部权限):
|
||||||
|
| 权限位 | 中文 | 覆盖接口 |
|
||||||
|
|--------|------|---------|
|
||||||
|
| `tasks` | 任务管理 | 任务/自定义动作/分组的增删改、启停、立即执行 |
|
||||||
|
| `devices` | 设备控制 | 停止设备、释放占用、清除异常、前台扫描、元素抓取 |
|
||||||
|
| `apks` | 应用管理 | APK 上传、安装、删除 |
|
||||||
|
| `logs` | 日志查看 | `GET /api/logs` |
|
||||||
|
- **用户管理接口仅管理员可用**(普通用户即使被授予业务权限也无法访问)
|
||||||
|
- 无权限访问返回 `403 {"ok": false, "error": "无权限执行此操作..."}`
|
||||||
|
- 当前用户权限查询:`GET /api/me`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 1. 认证
|
## 1. 认证
|
||||||
|
|
||||||
### POST /login
|
### POST /login
|
||||||
@@ -83,7 +98,7 @@
|
|||||||
|
|
||||||
**worker_status 取值**:`idle` / `connecting` / `running` / `done` / `error` / `failed` / `released`
|
**worker_status 取值**:`idle` / `connecting` / `running` / `done` / `error` / `failed` / `released`
|
||||||
|
|
||||||
### POST /api/scan_foreground
|
(权限:设备控制)
|
||||||
|
|
||||||
手动触发前台 App 扫描(后台异步执行,不打扰设备)。
|
手动触发前台 App 扫描(后台异步执行,不打扰设备)。
|
||||||
|
|
||||||
@@ -176,12 +191,14 @@
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"ok": true,
|
"ok": true,
|
||||||
"jobs": [{"id": "abc123", "name": "抖音养号", "task_type": "douyin_nurture", "...": "..."}],
|
"jobs": [{"id": "abc123", "name": "抖音养号", "task_type": "douyin_nurture",
|
||||||
|
"next_run": "2026-08-12 09:00", "...": "..."}],
|
||||||
"task_types": [...]
|
"task_types": [...]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
`next_run`:下次真正执行时间(已按运行窗口跳过窗口外触发点,格式 `YYYY-MM-DD HH:MM`);手动任务/已停用为 `null`。
|
||||||
|
|
||||||
### POST /api/jobs
|
(权限:任务管理)
|
||||||
|
|
||||||
创建任务计划。
|
创建任务计划。
|
||||||
|
|
||||||
@@ -192,7 +209,7 @@
|
|||||||
"task_type": "douyin_nurture",
|
"task_type": "douyin_nurture",
|
||||||
"target": {"mode": "all"},
|
"target": {"mode": "all"},
|
||||||
"params": {"watch_count": 50, "actions": {"like": {"params": {"rate": 0.5}}}},
|
"params": {"watch_count": 50, "actions": {"like": {"params": {"rate": 0.5}}}},
|
||||||
"schedule": {"mode": "once"},
|
"schedule": {"mode": "cron", "cron": "0 */2 * * *"},
|
||||||
"retry": {"max_attempts": 3, "delay": 60},
|
"retry": {"max_attempts": 3, "delay": 60},
|
||||||
"enabled": true
|
"enabled": true
|
||||||
}
|
}
|
||||||
@@ -203,7 +220,15 @@
|
|||||||
{"ok": true, "msg": "任务已创建", "job": {"id": "abc123", "...": "..."}}
|
{"ok": true, "msg": "任务已创建", "job": {"id": "abc123", "...": "..."}}
|
||||||
```
|
```
|
||||||
|
|
||||||
### PUT /api/jobs/:job_id
|
`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`)均不启动,手动执行返回错误提示 |
|
||||||
|
|
||||||
|
(权限:任务管理)
|
||||||
|
|
||||||
更新任务计划。只需传要更新的字段。
|
更新任务计划。只需传要更新的字段。
|
||||||
|
|
||||||
@@ -217,7 +242,7 @@
|
|||||||
{"ok": true, "msg": "任务已更新", "job": {"...": "..."}}
|
{"ok": true, "msg": "任务已更新", "job": {"...": "..."}}
|
||||||
```
|
```
|
||||||
|
|
||||||
### DELETE /api/jobs/:job_id
|
(权限:任务管理)
|
||||||
|
|
||||||
删除任务计划。
|
删除任务计划。
|
||||||
|
|
||||||
@@ -226,7 +251,7 @@
|
|||||||
{"ok": true, "msg": "任务已删除"}
|
{"ok": true, "msg": "任务已删除"}
|
||||||
```
|
```
|
||||||
|
|
||||||
### POST /api/jobs/:job_id/run
|
(权限:任务管理)
|
||||||
|
|
||||||
立即执行任务(异步,不阻塞)。
|
立即执行任务(异步,不阻塞)。
|
||||||
|
|
||||||
@@ -235,7 +260,7 @@
|
|||||||
{"ok": true, "msg": "任务 抖音养号 已触发"}
|
{"ok": true, "msg": "任务 抖音养号 已触发"}
|
||||||
```
|
```
|
||||||
|
|
||||||
### POST /api/jobs/:job_id/toggle
|
(权限:任务管理)
|
||||||
|
|
||||||
启用/停用任务。
|
启用/停用任务。
|
||||||
|
|
||||||
@@ -267,7 +292,7 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### POST /api/groups
|
(权限:任务管理)
|
||||||
|
|
||||||
创建分组。
|
创建分组。
|
||||||
|
|
||||||
@@ -276,7 +301,7 @@
|
|||||||
{"name": "A组", "serials": ["192.168.1.100:5555"], "description": "测试组"}
|
{"name": "A组", "serials": ["192.168.1.100:5555"], "description": "测试组"}
|
||||||
```
|
```
|
||||||
|
|
||||||
### PUT /api/groups/:name
|
(权限:任务管理)
|
||||||
|
|
||||||
更新分组。
|
更新分组。
|
||||||
|
|
||||||
@@ -285,7 +310,7 @@
|
|||||||
{"serials": ["192.168.1.100:5555", "192.168.1.101:5555"], "description": "更新描述"}
|
{"serials": ["192.168.1.100:5555", "192.168.1.101:5555"], "description": "更新描述"}
|
||||||
```
|
```
|
||||||
|
|
||||||
### DELETE /api/groups/:name
|
(权限:任务管理)
|
||||||
|
|
||||||
删除分组。
|
删除分组。
|
||||||
|
|
||||||
@@ -293,7 +318,7 @@
|
|||||||
|
|
||||||
## 7. 运行控制
|
## 7. 运行控制
|
||||||
|
|
||||||
### POST /api/stop_device
|
(权限:设备控制)
|
||||||
|
|
||||||
停止单台设备的 worker(并阻止后续重试)。
|
停止单台设备的 worker(并阻止后续重试)。
|
||||||
|
|
||||||
@@ -307,7 +332,7 @@
|
|||||||
{"ok": true, "msg": "已发送停止信号给 192.168.1.100:5555"}
|
{"ok": true, "msg": "已发送停止信号给 192.168.1.100:5555"}
|
||||||
```
|
```
|
||||||
|
|
||||||
### POST /api/stop_all
|
(权限:设备控制)
|
||||||
|
|
||||||
停止所有运行中的 worker。
|
停止所有运行中的 worker。
|
||||||
|
|
||||||
@@ -316,7 +341,7 @@
|
|||||||
{"ok": true, "stopped": ["192.168.1.100:5555", "192.168.1.101:5555"]}
|
{"ok": true, "stopped": ["192.168.1.100:5555", "192.168.1.101:5555"]}
|
||||||
```
|
```
|
||||||
|
|
||||||
### POST /api/release
|
(权限:设备控制)
|
||||||
|
|
||||||
释放当前账户占用的所有 STF 设备(清理用)。
|
释放当前账户占用的所有 STF 设备(清理用)。
|
||||||
|
|
||||||
@@ -329,7 +354,7 @@
|
|||||||
|
|
||||||
## 8. 用户管理
|
## 8. 用户管理
|
||||||
|
|
||||||
### GET /api/users
|
(仅管理员)
|
||||||
|
|
||||||
列出所有用户。
|
列出所有用户。
|
||||||
|
|
||||||
@@ -338,7 +363,7 @@
|
|||||||
{"ok": true, "users": [{"id": 1, "username": "admin", "is_admin": true}]}
|
{"ok": true, "users": [{"id": 1, "username": "admin", "is_admin": true}]}
|
||||||
```
|
```
|
||||||
|
|
||||||
### POST /api/users
|
(仅管理员)
|
||||||
|
|
||||||
创建用户。
|
创建用户。
|
||||||
|
|
||||||
@@ -347,7 +372,7 @@
|
|||||||
{"username": "user1", "password": "pass123", "is_admin": false}
|
{"username": "user1", "password": "pass123", "is_admin": false}
|
||||||
```
|
```
|
||||||
|
|
||||||
### PUT /api/users/:uid
|
(仅管理员)
|
||||||
|
|
||||||
更新用户(修改密码/管理员权限)。
|
更新用户(修改密码/管理员权限)。
|
||||||
|
|
||||||
@@ -356,7 +381,7 @@
|
|||||||
{"password": "newpass", "is_admin": true}
|
{"password": "newpass", "is_admin": true}
|
||||||
```
|
```
|
||||||
|
|
||||||
### DELETE /api/users/:uid
|
(仅管理员)
|
||||||
|
|
||||||
删除用户(不能删除 admin 和当前登录用户)。
|
删除用户(不能删除 admin 和当前登录用户)。
|
||||||
|
|
||||||
@@ -364,7 +389,7 @@
|
|||||||
|
|
||||||
## 9. 日志
|
## 9. 日志
|
||||||
|
|
||||||
### GET /api/logs
|
(权限:日志查看)
|
||||||
|
|
||||||
查看日志文件内容。
|
查看日志文件内容。
|
||||||
|
|
||||||
@@ -394,7 +419,7 @@
|
|||||||
|
|
||||||
列出所有自定义动作(步骤打包)。
|
列出所有自定义动作(步骤打包)。
|
||||||
|
|
||||||
### POST /api/custom_actions
|
(权限:任务管理)
|
||||||
|
|
||||||
创建自定义动作。
|
创建自定义动作。
|
||||||
|
|
||||||
@@ -403,11 +428,11 @@
|
|||||||
{"name": "登录流程", "icon": "📦", "steps": [{"type": "click", "...": "..."}]}
|
{"name": "登录流程", "icon": "📦", "steps": [{"type": "click", "...": "..."}]}
|
||||||
```
|
```
|
||||||
|
|
||||||
### PUT /api/custom_actions/:action_id
|
(权限:任务管理)
|
||||||
|
|
||||||
更新自定义动作。
|
更新自定义动作。
|
||||||
|
|
||||||
### DELETE /api/custom_actions/:action_id
|
(权限:任务管理)
|
||||||
|
|
||||||
删除自定义动作。
|
删除自定义动作。
|
||||||
|
|
||||||
@@ -424,7 +449,7 @@
|
|||||||
{"ok": true, "running": true}
|
{"ok": true, "running": true}
|
||||||
```
|
```
|
||||||
|
|
||||||
### GET /api/uiauto/devices
|
(权限:设备控制)
|
||||||
|
|
||||||
获取 uiauto2 已连接的设备列表。
|
获取 uiauto2 已连接的设备列表。
|
||||||
|
|
||||||
@@ -433,7 +458,7 @@
|
|||||||
{"ok": true, "devices": [{"serial": "192.168.1.100:5555", "model": "Pixel 6"}]}
|
{"ok": true, "devices": [{"serial": "192.168.1.100:5555", "model": "Pixel 6"}]}
|
||||||
```
|
```
|
||||||
|
|
||||||
### GET /api/uiauto/screenshot
|
(权限:设备控制)
|
||||||
|
|
||||||
通过 uiauto2 获取设备截图(JPEG)。
|
通过 uiauto2 获取设备截图(JPEG)。
|
||||||
|
|
||||||
@@ -444,7 +469,7 @@
|
|||||||
|
|
||||||
**响应**:成功返回 `image/jpeg`,失败返回 JSON 错误。
|
**响应**:成功返回 `image/jpeg`,失败返回 JSON 错误。
|
||||||
|
|
||||||
### GET /api/uiauto/elements
|
(权限:设备控制)
|
||||||
|
|
||||||
获取设备 UI 元素树。
|
获取设备 UI 元素树。
|
||||||
|
|
||||||
@@ -498,7 +523,7 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### POST /api/apks/upload
|
(权限:应用管理)
|
||||||
|
|
||||||
上传 APK 文件(自动解析包名/版本/应用名)。
|
上传 APK 文件(自动解析包名/版本/应用名)。
|
||||||
|
|
||||||
@@ -512,11 +537,11 @@
|
|||||||
{"ok": true, "apk": {"...": "..."}, "msg": "上传成功: 抖音"}
|
{"ok": true, "apk": {"...": "..."}, "msg": "上传成功: 抖音"}
|
||||||
```
|
```
|
||||||
|
|
||||||
### DELETE /api/apks/:apk_id
|
(权限:应用管理)
|
||||||
|
|
||||||
删除 APK 文件和记录。
|
删除 APK 文件和记录。
|
||||||
|
|
||||||
### POST /api/apks/install
|
(权限:应用管理)
|
||||||
|
|
||||||
批量安装 APK 到指定设备。
|
批量安装 APK 到指定设备。
|
||||||
|
|
||||||
@@ -530,6 +555,19 @@
|
|||||||
{"ok": true, "msg": "开始安装 抖音 到 2 台设备"}
|
{"ok": true, "msg": "开始安装 抖音 到 2 台设备"}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### GET /api/apks/install/devices
|
||||||
|
|
||||||
|
可安装设备列表:STF 在线设备池 + 本机 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
|
### GET /api/apks/install/status
|
||||||
|
|
||||||
获取安装任务实时状态。
|
获取安装任务实时状态。
|
||||||
@@ -554,3 +592,78 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. 维护(仅管理员)
|
||||||
|
|
||||||
|
维护页两个接口都**仅管理员可用**(普通用户即使有业务权限也访问不了,返回 403)。
|
||||||
|
|
||||||
|
### POST /api/stf/restart
|
||||||
|
|
||||||
|
一键重启 STF Docker 容器。通过 SSH 到部署机执行 `docker restart`,随后轮询 STF API 确认恢复。
|
||||||
|
|
||||||
|
- 目标与容器名:`STF_SSH_TARGET`(默认 `[email protected]`)、`STF_DOCKER_CONTAINER`(默认 `stf`)环境变量配置
|
||||||
|
- 前置条件:本机可免密 SSH 到部署机
|
||||||
|
- **有任务在运行时返回 409 拒绝执行**(STF 重启会重置设备占用状态)
|
||||||
|
- 失败返回 502(SSH 不可达/容器名错误);执行超时返回 408
|
||||||
|
|
||||||
|
**响应**:
|
||||||
|
```json
|
||||||
|
{"ok": true, "msg": "STF 容器已重启", "detail": "stf: Up 3 seconds", "api_alive": true}
|
||||||
|
```
|
||||||
|
|
||||||
|
### GET /api/adb/devices
|
||||||
|
|
||||||
|
维护终端设备列表:本地 adb 已连接(含 offline)+ STF 在线设备池(标记 `stf`)。
|
||||||
|
供终端设备选择器使用——选中后前端自动附加 `-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)——会断开 STF provider 共享的 adb transport,导致全部设备被 STF 误判离线。
|
||||||
|
|
||||||
|
**响应**:
|
||||||
|
```json
|
||||||
|
{"ok": true, "stdout": "...", "stderr": "", "code": 0}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. 步骤测试(仅管理员可触发,需"设备控制"权限)
|
||||||
|
|
||||||
|
### POST /api/steps/test
|
||||||
|
|
||||||
|
在指定设备上单步试执行(步骤编辑器"测试此步骤"按钮),验证选择器是否命中。
|
||||||
|
只读连接(adb connect + u2),不占用/释放 STF,与运行中任务互不干扰。
|
||||||
|
|
||||||
|
**请求**(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。
|
||||||
|
|||||||
+11
-1
@@ -185,7 +185,7 @@ SQLAlchemy 模型,存于 `data/users.db`:
|
|||||||
|
|
||||||
| 模型 | 表名 | 说明 |
|
| 模型 | 表名 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `User` | user | 后台用户(Flask-Login 认证,SHA256 密码) |
|
| `User` | user | 后台用户(Flask-Login 认证,SHA256 密码;`is_admin` 管理员 + `perms` 业务权限位) |
|
||||||
| `DeviceGroup` | device_group | 设备分组(serials 存 JSON) |
|
| `DeviceGroup` | device_group | 设备分组(serials 存 JSON) |
|
||||||
| `TaskJob` | task_job | 任务计划(target/params/schedule/retry 存 JSON) |
|
| `TaskJob` | task_job | 任务计划(target/params/schedule/retry 存 JSON) |
|
||||||
| `CustomAction` | custom_action | 自定义动作(步骤打包,steps 存 JSON) |
|
| `CustomAction` | custom_action | 自定义动作(步骤打包,steps 存 JSON) |
|
||||||
@@ -195,6 +195,16 @@ SQLAlchemy 模型,存于 `data/users.db`:
|
|||||||
- 创建所有表
|
- 创建所有表
|
||||||
- 首次启动创建默认管理员 `admin/admin123`
|
- 首次启动创建默认管理员 `admin/admin123`
|
||||||
- 自动迁移旧 `groups.json` / `jobs.json` 到 SQLite(迁移后归档为 `.migrated`)
|
- 自动迁移旧 `groups.json` / `jobs.json` 到 SQLite(迁移后归档为 `.migrated`)
|
||||||
|
- 版本化 schema 迁移(`SCHEMA_MIGRATIONS`):结构变更必须追加迁移条目,`create_all` 只建新表不加列
|
||||||
|
|
||||||
|
### 权限模型
|
||||||
|
|
||||||
|
- `User.perms` 存业务权限位 JSON 数组(`tasks`/`devices`/`apks`/`logs`),`is_admin=true` 拥有全部权限(`has_perm` 短路)
|
||||||
|
- 后端统一用 `@perm_required(PERM_X)` / `@admin_required` 装饰器拦截(web_server.py),无权限返回 403;
|
||||||
|
查看类 GET 接口只要求登录;用户管理、维护接口(STF 重启 `/api/stf/restart`、adb 终端 `/api/adb/cmd`)强制 `admin_required`
|
||||||
|
- adb 终端安全红线:拒绝 `kill-server` / `disconnect`(STF provider 共享 adb transport,断开会误判全设备离线)
|
||||||
|
- 前端 `loadMe()` 拉取 `/api/me`,用 `data-perm` 属性隐藏无权限的 tab/按钮,行内按钮用 `_can(perm)` 判断
|
||||||
|
- 安全兜底:**前端隐藏只是 UX,权限强制在后端**;新增路由时按"写操作必须带权限装饰器"的约定
|
||||||
|
|
||||||
### 3.7 APK 管理(`core/apk_manager.py`)
|
### 3.7 APK 管理(`core/apk_manager.py`)
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,11 @@ STF_TOKEN = "你的STF_API_Token"
|
|||||||
| `WEB_HOST` | `0.0.0.0` | 仅本机访问改为 `127.0.0.1` |
|
| `WEB_HOST` | `0.0.0.0` | 仅本机访问改为 `127.0.0.1` |
|
||||||
| `WEB_PORT` | `18050` | 端口冲突时修改 |
|
| `WEB_PORT` | `18050` | 端口冲突时修改 |
|
||||||
| `ADB_PATH` | 自动识别 | 用自定义 adb 时修改 |
|
| `ADB_PATH` | 自动识别 | 用自定义 adb 时修改 |
|
||||||
|
| `STF_SSH_TARGET` | `[email protected]` | 维护页"一键重启 STF 容器"的 SSH 目标(需免密登录) |
|
||||||
|
| `STF_DOCKER_CONTAINER` | `stf` | 220 上 STF Docker 容器名(`docker ps` 查看) |
|
||||||
|
|
||||||
|
> **维护页 STF 重启前置**:本机需能免密 SSH 到部署机(把本机公钥加入目标机 `authorized_keys`),
|
||||||
|
> 且 `STF_DOCKER_CONTAINER` 与真实容器名一致;未配置好时按钮会返回明确错误。
|
||||||
|
|
||||||
### 2.4 启动服务
|
### 2.4 启动服务
|
||||||
|
|
||||||
@@ -196,6 +201,8 @@ server {
|
|||||||
### 3.3 安全加固
|
### 3.3 安全加固
|
||||||
|
|
||||||
- **修改默认密码**:登录后立即在"用户"Tab 修改 admin 密码
|
- **修改默认密码**:登录后立即在"用户"Tab 修改 admin 密码
|
||||||
|
- **最小权限分配**:需要多人使用后台时,在"用户"Tab 创建普通用户并只勾选必要权限
|
||||||
|
(任务管理/设备控制/应用管理/日志查看),不要把 admin 密码共享出去
|
||||||
- **修改 SECRET_KEY**:编辑 `web_server.py`,把 `app.config["SECRET_KEY"]` 改成随机字符串
|
- **修改 SECRET_KEY**:编辑 `web_server.py`,把 `app.config["SECRET_KEY"]` 改成随机字符串
|
||||||
- **限制访问**:生产环境把 `WEB_HOST` 改为 `127.0.0.1`,配合反向代理
|
- **限制访问**:生产环境把 `WEB_HOST` 改为 `127.0.0.1`,配合反向代理
|
||||||
- **防火墙**:只开放必要端口
|
- **防火墙**:只开放必要端口
|
||||||
|
|||||||
Reference in New Issue
Block a user