feat: 设备端应用商店(平台侧)—— Agent 直连拉清单/下载/安装,绕开 MIUI 的 USB 安装拦截

背景:`adb install` 在 MIUI 上被「USB 安装」拦下(USER_RESTRICTED),必须人工点确认;
而**由设备上的 App 自己调系统安装器**走的是普通应用安装流程,不触发那道拦截。

平台侧实现(设备端 Agent APK 在另一个仓库,契约见 doc/DEVICE_AGENT.md):

- web/device_agent_api.py(新):
  * 设备侧 `/api/device/agent/{bootstrap,apk/<id>,report}` —— 无登录会话,靠
    `X-Device-Token`(常量时间比对)鉴权;`X-Device-Fingerprint` 让平台认出是哪台设备
    (换 IP 也认得出,并把平台分配的名称回给它);**默认关闭,关闭时统一 404**,
    不暴露接口存在性;只读清单与文件,不返回任何配置/密钥
  * 管理侧 `/api/agent-store/{config,logs}` —— 登录 + 应用管理权限;启用时自动生成令牌,
    可一键重置(旧令牌立即失效)
- core/models.py:新增 `device_install_log`(下载/安装记录,自动裁剪保留 500 条)
- 应用管理页新增「📱 设备端应用商店」面板:开关 / 接入地址 / 令牌(复制·重置)/ 安装记录;
  面板里写明它与「平台批量推送」的分工
- doc/DEVICE_AGENT.md(新):**两个仓库共享的接口契约** —— 接入流程、三个设备接口的
  完整规格与错误码、adb 指令协议(剪贴板/身份显示/配置下发)、版本兼容约定、安全须知;
  API.md、DATA_MODEL.md、doc/README.md 索引同步

验证:
- 后端 9 组用例:默认关闭 404、无/错令牌 401、指纹认出设备名、清单、下载、上报与失败
  原因落库、关闭后重新 404、重置令牌后旧令牌立即失效、未登录管理端被拒 —— 全通过
- 真实浏览器:面板渲染、点开关 → 自动生成令牌 → 设备端带令牌拉到清单且 device.name=A08、
  无 JS 报错;测试后已把开关还原为关闭
- 全量回归 105 项通过(42 GET + 62 写探测 + 7 关键业务)
This commit is contained in:
2026-09-13 14:40:38 +08:00
parent 66632bb7d6
commit dad1af8b0b
11 changed files with 668 additions and 3 deletions
+55 -1
View File
@@ -219,6 +219,26 @@
| POST | `/api/system/backup/preview` | 上传备份校验预览(multipart,字段 `file`) |
| POST | `/api/system/backup/apply` | 应用恢复(重启生效) |
### 2.11 device_agent(`web/device_agent_api.py`)
**设备端专用**(无登录会话,靠 `X-Device-Token` 鉴权;平台未启用时统一 404):
| 方法 | 路径 | 功能 |
|------|------|------|
| GET | `/api/device/agent/bootstrap` | 设备拉自己的身份 + 可安装应用清单 |
| GET | `/api/device/agent/apk/<apk_id>` | 下载 APK 文件 |
| POST | `/api/device/agent/report` | 上报下载/安装结果 |
**管理端**(登录 + 应用管理权限):
| 方法 | 路径 | 功能 |
|------|------|------|
| GET | `/api/agent-store/config` | 读配置(含设备令牌) |
| POST | `/api/agent-store/config` | 启用/停用、重置令牌 |
| GET | `/api/agent-store/logs` | 设备端安装记录 |
> 完整协议、鉴权与版本约定见 [DEVICE_AGENT.md](DEVICE_AGENT.md)。
---
## 3. 认证与页面
@@ -656,7 +676,41 @@ multipart 上传 `.zip` 或 `.db`(字段 `file`)→ 校验并暂存:
---
## 14. 非 JSON 响应汇总
## 14. 设备端应用商店
**设备侧**三个接口(`bootstrap` / `apk` / `report`)的完整协议、鉴权、错误码与版本约定
见 [DEVICE_AGENT.md](DEVICE_AGENT.md)——那份文档同时是设备端 APK 仓库的对接契约。
**管理侧**(登录 + 应用管理权限):
### GET /api/agent-store/config
```json
{"ok": true, "enabled": false, "token": "…", "token_created_at": "2026-09-13 14:38:11",
"api_version": 1, "base_url_hint": "http://192.168.20.220:18050"}
```
`base_url_hint` 由请求的 Host 回填,直接可作为设备端要填的平台地址。
### POST /api/agent-store/config
`{"enabled": true}` 启用(并自动生成令牌);`{"regenerate_token": true}` 重置令牌
(旧令牌立即失效)。返回 `{"ok": true, "enabled": …, "token": …}`。
### GET /api/agent-store/logs?limit=50
```json
{"ok": true, "logs": [{"id": 1, "device_name": "A08", "serial": "192.168.20.100:5555",
"fingerprint": "…", "apk_id": "56b114aa", "package_name": "com.example.clipinject",
"version_name": "1.0", "action": "install_fail", "message": "MIUI 拦截未确认",
"created_at": "2026-09-13 14:40:00"}]}
```
`action` ∈ `download` / `install_ok` / `install_fail`;记录表最多保留 500 条(自动裁旧)。
---
## 15. 非 JSON 响应汇总
| 方法 | 路径 | 响应类型 | 说明 |
|------|------|---------|------|