refactor: web_server 模块化拆分(web/ 蓝图包 auth/monitor/tasks/admin/tools/devices/apks/tailscale + context/common,web_server 1796→193 行仅留装配与启动)+ 大屏优化(网格 5s 轮询、弹窗独占缩略图、流支持 q/fps)+ 三修复(MIUI 屏幕状态检测改 dumpsys power、补 ADB_PATH 导入修复一键亮屏、renderGrid 增量渲染不重载缩略图)
This commit is contained in:
+123
@@ -0,0 +1,123 @@
|
||||
"""应用管理 API(APK 上传/安装/删除/设备已装应用)。"""
|
||||
import re
|
||||
import shlex
|
||||
import subprocess
|
||||
|
||||
from flask import Blueprint, jsonify, request
|
||||
from flask_login import login_required
|
||||
|
||||
from core import device_pool
|
||||
from core.adb_helper import adb_connect_light, _adb
|
||||
from web import context
|
||||
from web.auth import perm_required, PERM_APKS
|
||||
from web.common import _merged_device_list
|
||||
|
||||
bp = Blueprint("apks", __name__)
|
||||
|
||||
@bp.route("/api/apks")
|
||||
@login_required
|
||||
def api_apks_list():
|
||||
"""列出所有已上传的 APK。"""
|
||||
return jsonify({"ok": True, "apks": context.apk_mgr.list_all()})
|
||||
|
||||
@bp.route("/api/apks/upload", methods=["POST"])
|
||||
@perm_required(PERM_APKS)
|
||||
def api_apks_upload():
|
||||
"""上传 APK 文件(multipart/form-data,字段名 file)。"""
|
||||
file = request.files.get("file")
|
||||
if not file or not file.filename:
|
||||
return jsonify({"ok": False, "error": "未选择文件"}), 400
|
||||
info = context.apk_mgr.upload(file)
|
||||
if info:
|
||||
return jsonify({"ok": True, "apk": info,
|
||||
"msg": f"上传成功: {info['display_name']}"})
|
||||
return jsonify({"ok": False, "error": "上传失败,请检查文件格式"}), 500
|
||||
|
||||
@bp.route("/api/apks/<apk_id>", methods=["DELETE"])
|
||||
@perm_required(PERM_APKS)
|
||||
def api_apks_delete(apk_id):
|
||||
"""删除 APK 文件和记录。"""
|
||||
ok, msg = context.apk_mgr.delete(apk_id)
|
||||
if ok:
|
||||
return jsonify({"ok": True, "msg": msg})
|
||||
return jsonify({"ok": False, "error": msg}), 400
|
||||
|
||||
@bp.route("/api/apks/install/devices")
|
||||
@perm_required(PERM_APKS)
|
||||
def api_apks_install_devices():
|
||||
"""可安装设备列表(应用管理):设备池在线设备 + 本机 adb 设备(含 USB 有线连接)。
|
||||
|
||||
source: pool=设备池 / usb=本机有线 adb / adb=本机网络 adb。
|
||||
"""
|
||||
devices, seen = [], set()
|
||||
try:
|
||||
online = set(device_pool.list_online())
|
||||
for serial in device_pool.list_configured():
|
||||
if serial in online:
|
||||
devices.append({"serial": serial, "model": "", "source": "pool"})
|
||||
seen.add(serial)
|
||||
except Exception:
|
||||
pass
|
||||
out = _adb("devices")
|
||||
for line in (out or "").splitlines()[1:]:
|
||||
parts = line.split()
|
||||
if len(parts) >= 2 and parts[0] and parts[0] not in seen:
|
||||
devices.append({"serial": parts[0], "model": "",
|
||||
"source": "usb" if ":" not in parts[0] else "adb"})
|
||||
seen.add(parts[0])
|
||||
return jsonify({"ok": True, "devices": devices})
|
||||
|
||||
@bp.route("/api/apks/install", methods=["POST"])
|
||||
@perm_required(PERM_APKS)
|
||||
def api_apks_install():
|
||||
"""批量安装 APK 到指定设备。参数: {apk_id, serials:[]}"""
|
||||
data = request.json or {}
|
||||
apk_id = data.get("apk_id", "")
|
||||
serials = data.get("serials", [])
|
||||
ok, msg = context.apk_mgr.install(apk_id, serials)
|
||||
if ok:
|
||||
return jsonify({"ok": True, "msg": msg})
|
||||
return jsonify({"ok": False, "error": msg}), 400
|
||||
|
||||
@bp.route("/api/apks/install/status")
|
||||
@login_required
|
||||
def api_apks_install_status():
|
||||
"""获取安装任务实时状态。"""
|
||||
status = context.apk_mgr.get_install_status()
|
||||
return jsonify({"ok": True, "status": status})
|
||||
|
||||
|
||||
# ================== API:维护(仅管理员) ==================
|
||||
# 维护功能:STF 容器一键重启 + adb 远程终端。两个接口都仅管理员可用。
|
||||
# adb 终端安全红线(技术约束):绝不 kill-server / 绝不 disconnect IP:5555——
|
||||
# STF provider 共享该 adb transport,断开会让 STF 误判全部设备离线并触发重连。
|
||||
_ADB_BLOCKED_PATTERNS = ("kill-server", "disconnect")
|
||||
|
||||
|
||||
def _blocked_adb_cmd(cmd):
|
||||
"""命中红线的 adb 命令(kill-server / disconnect)直接拒绝。"""
|
||||
low = cmd.lower()
|
||||
return any(p in low for p in _ADB_BLOCKED_PATTERNS)
|
||||
|
||||
|
||||
def _merged_device_list():
|
||||
"""本地 adb(含 USB)+ 设备池的合并列表([{serial, state}])。
|
||||
|
||||
供维护终端/剪贴板注入/应用版本管理等设备选择场景共用。
|
||||
状态:device/offline=本机 adb 实际状态;pool=设备池已配置但本机未连接;
|
||||
stf_not_ready=STF agent 未就绪(迁移期保留,阶段 3 摘除 STF 后删除)。
|
||||
"""
|
||||
states = {}
|
||||
out = _adb("devices")
|
||||
for line in (out or "").splitlines()[1:]:
|
||||
parts = line.split()
|
||||
if len(parts) >= 2 and parts[0] and not parts[0].startswith("*"):
|
||||
states[parts[0]] = parts[1]
|
||||
try:
|
||||
for serial in device_pool.list_configured():
|
||||
if serial not in states:
|
||||
states[serial] = "pool"
|
||||
except Exception:
|
||||
pass
|
||||
return [{"serial": s, "state": st} for s, st in sorted(states.items())]
|
||||
|
||||
Reference in New Issue
Block a user