# -*- coding: utf-8 -*- """中转管理器加「删除设备(连同配对码)」。 用户要求: "这个中转管理器,首先删除设备,就是删除设备并删除对应的配对码" 现状: /v1/admin/kick 只踢会话,设备还在,码还占着 /v1/admin/forget 只抹配对码绑定,设备还挂在列表上 两个都不是用户要的"删干净"。 新增: Relay.delete_device(device_id) —— 一次做完: 1. 踢掉它的所有会话 2. 删掉设备记录 3. 删掉它的配对码绑定(这样那台设备下次可以重新设码) POST /v1/admin/delete {"deviceId": "..."} 或 {"code": "..."} POST /v1/admin/delete_all 管理页: 每行加一个「删除」按钮(带二次确认) 表格上方加一个「删除全部设备」按钮 """ from __future__ import annotations import sys from pathlib import Path RELAY = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\relay\relay.py") # ---------------- 1. Relay.delete_device ---------------- DELETE_ANCHOR = ''' def forget(self, code: str) -> bool:''' DELETE_NEW = ''' def delete_device(self, *, device_id: str = "", code: str = "") -> dict: """删掉一台设备,**连同它的配对码绑定和所有会话**。 这就是管理页上「删除设备」按钮做的事。 和 kick/forget 的区别: kick 只踢会话,设备还在,码还占着 forget 只抹码绑定,设备还挂在列表上 delete 三样一起清 —— 那台设备下次可以重新设任意配对码 返回做了什么,便于界面显示。 """ target = None with self.lock: if device_id: target = self.devices.get(device_id) if target is None and code: owner = self.codes.get(code) if owner: target = self.devices.get(owner) device_id = owner if target is None: return {"ok": False, "error": "找不到这台设备"} removed_sessions = self.kick(target.device_id) with self.lock: self.devices.pop(target.device_id, None) # 把它的码绑定一起删掉 gone_codes = [c for c, owner in list(self.codes.items()) if owner == target.device_id] for item in gone_codes: self.codes.pop(item, None) # 清掉可能残留的会话记录 for token in list(getattr(self, "sessions", {}).keys()): self.sessions.pop(token, None) self.save_state() log(f"管理操作:删除设备 {target.name}(码 {target.code})," f"踢掉 {removed_sessions} 个会话,清掉 {len(gone_codes)} 条码绑定") return { "ok": True, "deleted": target.device_id, "name": target.name, "code": target.code, "kicked": removed_sessions, "codesCleared": gone_codes, } def forget(self, code: str) -> bool:''' # ---------------- 2. 管理接口 ---------------- API_ANCHOR = ''' if path == "/v1/admin/forget":''' API_NEW = ''' if path in ("/v1/admin/delete", "/v1/admin/delete_all"): body = self._json_body() if path == "/v1/admin/delete_all": # 一次删光(清场用) with RELAY.lock: all_ids = list(RELAY.devices.keys()) results = [RELAY.delete_device(device_id=item) for item in all_ids] done = [r for r in results if r.get("ok")] log(f"管理操作:删除全部设备,共 {len(done)} 台") self._json(200, {"ok": True, "deleted": len(done), "devices": done}) return device = str(body.get("deviceId") or "").strip() code = str(body.get("code") or "").strip() if not device and not code: self._json(400, {"ok": False, "error": "要给 deviceId 或 code"}) return result = RELAY.delete_device(device_id=device, code=code) self._json(200 if result.get("ok") else 404, result) return if path == "/v1/admin/forget":''' # ---------------- 3. 管理页:设备行加删除按钮 ---------------- ROW_OLD = ''' f'{_kb(device["bytesIn"])} / {_kb(device["bytesOut"])}' "")''' ROW_NEW = ''' f'{_kb(device["bytesIn"])} / {_kb(device["bytesOut"])}' # 删除按钮:连同配对码一起删掉。 # 用 data-* 传值,避免设备名里有引号把 HTML 弄坏。 '' "")''' TABLE_OLD = ''' {''.join(rows) or ''}
设备配对码会话空闲 待发(表/机)流量(入/出)
还没有设备上线
数据来自 /v1/health · 只显示配对码与流量,看不到任何传输内容
''' TABLE_NEW = '''
删除会连配对码一起清掉,那台设备下次可以重新设码
{''.join(rows) or ''}
设备配对码会话空闲 待发(表/机)流量(入/出)操作
还没有设备上线
数据来自 /v1/health · 只显示配对码与流量,看不到任何传输内容
''' # 样式 CSS_OLD = ''' .foot{{color:#6e7681;font-size:12px;margin-top:16px}}''' CSS_NEW = ''' .foot{{color:#6e7681;font-size:12px;margin-top:16px}} .bar{{margin-bottom:10px;display:flex;align-items:center;gap:10px;flex-wrap:wrap}} .tip{{color:#6e7681;font-size:12px}} button.del{{background:#3d1418;color:#ff7b72;border:1px solid #6e2b2b; border-radius:6px;padding:4px 12px;font-size:12px;cursor:pointer; font-family:inherit}} button.del:hover{{background:#5a1c22;border-color:#a33;color:#ffa198}}''' def patch(pairs, label: str) -> bool: text = RELAY.read_text(encoding="utf-8") ok = True for old, new, tag in pairs: if old not in text: print(f" [{label}] [跳过] {tag} —— 锚点没找到") ok = False continue text = text.replace(old, new, 1) print(f" [{label}] {tag}") RELAY.write_text(text, encoding="utf-8", newline="\n") return ok def main() -> int: text = RELAY.read_text(encoding="utf-8") if "def delete_device" in text: print(" 已经加过删除设备") return 0 ok = patch([ (DELETE_ANCHOR, DELETE_NEW, "Relay.delete_device"), (API_ANCHOR, API_NEW, "管理接口 /delete 和 /delete_all"), (CSS_OLD, CSS_NEW, "删除按钮样式"), (ROW_OLD, ROW_NEW, "设备行加删除按钮"), (TABLE_OLD, TABLE_NEW, "工具栏 + 脚本"), ], "中转") import ast try: ast.parse(RELAY.read_text(encoding="utf-8")) print(" 语法 OK") except SyntaxError as exc: print(f" 语法错误: 行 {exc.lineno}: {exc.msg}") ok = False return 0 if ok else 1 if __name__ == "__main__": sys.exit(main())