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:
@@ -349,6 +349,26 @@ class AgentAction(db.Model):
|
||||
updated_at = db.Column(db.String(20), default="")
|
||||
|
||||
|
||||
class DeviceInstallLog(db.Model):
|
||||
"""设备端应用商店的下载/安装记录(设备上的 Agent 上报,平台侧展示)。
|
||||
|
||||
平台**不主动**发起这条通道的安装:设备自己拉清单、自己下载、自己调
|
||||
PackageInstaller 安装——这样走的是普通应用安装流程,不会触发 MIUI 针对
|
||||
`adb install` 的「USB 安装」拦截(USER_RESTRICTED)。
|
||||
"""
|
||||
__tablename__ = "device_install_log"
|
||||
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
|
||||
fingerprint = db.Column(db.String(120), default="") # ro.serialno,识别物理设备
|
||||
serial = db.Column(db.String(120), default="") # 上报时的地址(可能换过)
|
||||
device_name = db.Column(db.String(80), default="") # 平台侧的名称(快照)
|
||||
apk_id = db.Column(db.String(32), default="")
|
||||
package_name = db.Column(db.String(200), default="")
|
||||
version_name = db.Column(db.String(50), default="")
|
||||
action = db.Column(db.String(20), default="") # download / install_ok / install_fail
|
||||
message = db.Column(db.String(500), default="") # 失败原因等
|
||||
created_at = db.Column(db.String(20), default="")
|
||||
|
||||
|
||||
class AgentConversation(db.Model):
|
||||
"""AI 控制台会话:整个消息序列以 JSON 存在一行里(单会话几十 KB,够用)。"""
|
||||
__tablename__ = "agent_conversation"
|
||||
|
||||
Reference in New Issue
Block a user