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:
2026-08-19 14:55:29 +08:00
parent e07b339a51
commit ce47a5b83d
16 changed files with 1922 additions and 1780 deletions
+123
View File
@@ -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())]