# -*- coding: utf-8 -*- r'''管理页 UI:加「允许新设备接入」开关 + 每台设备的「改码」按钮。 设计上遵守用户的"所有东西都要白色简洁": * 开关用系统原生 checkbox + 极简样式,不引入花哨组件 * 改码走 prompt(),不加弹窗层 —— 管理页是自用的, 少一层 DOM 就少一处会坏的地方 * 按钮沿用现有的 .ghost 样式(不抢 .danger 的视觉重量) ⚠ 一个容易踩的坑: 现有 JS 用 `button.danger[data-device]` 绑定"单台删除"。 新加的"改码"按钮**故意不带 data-device 而用 data-recode**, 否则会被删除逻辑一起选中,点改码变成删设备。 ''' from __future__ import annotations import sys from pathlib import Path SRC = Path(r"C:\Users\shu\Desktop\deepseekagent\LanRemote\relay\admin.py") def patch(text: str, old: str, new: str, label: str) -> tuple: if old not in text: print(" ✗ 没找到:%s" % label) return text, False if text.count(old) != 1: print(" ! 出现 %d 次:%s" % (text.count(old), label)) return text, False print(" ✓ %s" % label) return text.replace(old, new, 1), True def main() -> int: text = SRC.read_text(encoding="utf-8") ok_all = True # ------------------------------------------------ 1. 行里加"改码"按钮 old = """ '' '' % ( did, name, "on" if online else "off", name, did, code, "on" if online else "off", "在线" if online else "离线", device["idleSeconds"], device["queuedToClient"], device["queuedToAgent"], _kb(device["bytesIn"]), _kb(device["bytesOut"]), did, name, code) )""" new = """ '' # ⚠ 这个按钮用 data-recode 而不是 data-device —— # 现有"单台删除"是按 button.danger[data-device] 绑的, # 这里要是也带 data-device 就会被当成删除按钮。 '' '' '' % ( did, name, "on" if online else "off", name, did, code, "on" if online else "off", "在线" if online else "离线", device["idleSeconds"], device["queuedToClient"], device["queuedToAgent"], _kb(device["bytesIn"]), _kb(device["bytesOut"]), did, name, code, did, name, code) )""" text, ok = patch(text, old, new, "设备行加「改码」按钮") ok_all = ok_all and ok # ------------------------------------------------ 2. 开关放进"设备"小节 old = """

设备

""" new = """

设备

""" text, ok = patch(text, old, new, "设备小节加开关") ok_all = ok_all and ok # ------------------------------------------------ 3. 传入开关状态 old = """ sessions = snap.get("sessions", 0)""" new = """ sessions = snap.get("sessions", 0) # 开关状态:允许新设备接入时勾上 allow_new = bool(snap.get("allowNew", True))""" text, ok = patch(text, old, new, "读 allowNew 状态") ok_all = ok_all and ok old = """ "rows": _device_rows(devices), "dl": dl_rows,""" new = """ "rows": _device_rows(devices), "allownew": " checked" if allow_new else "", "dl": dl_rows,""" text, ok = patch(text, old, new, "把状态传进模板") ok_all = ok_all and ok # ------------------------------------------------ 4. JS old = """// ---------------- 单台删除 ----------------""" new = """// ---------------- 允许/禁止新设备接入 ---------------- // 关掉之后,只有**已经登记过**的电脑能继续上线, // 没见过的新设备一律拒绝。 const allowNew = document.getElementById('allowNew'); if (allowNew) allowNew.onchange = async () => { const want = allowNew.checked; allowNew.disabled = true; try { const j = await post('/v1/admin/allow_new', {allow: want}); if (j.ok) { toast(j.allowNew ? '已允许新设备接入' : '已禁止新设备接入', 'ok'); } else { toast('设置失败:' + (j.error || '未知错误'), 'err'); allowNew.checked = !want; } } catch (e) { toast('请求出错:' + e, 'err'); allowNew.checked = !want; } allowNew.disabled = false; }; // ---------------- 改配对码 ---------------- document.querySelectorAll('button[data-recode]').forEach(b => { b.onclick = async () => { const name = b.dataset.name, old = b.dataset.code; const typed = prompt('把「' + name + '」的配对码 ' + old + ' 改成:\\n\\n(必须是 6 位数字)', ''); if (typed === null) return; const code = (typed || '').trim(); if (!/^[0-9]{6}$/.test(code)) { toast('配对码必须是 6 位数字', 'err'); return; } b.disabled = true; try { const j = await post('/v1/admin/set_code', {deviceId: b.dataset.recode, code: code}); if (j.ok) { toast(j.unchanged ? '配对码没有变化' : ('配对码已改成 ' + j.code + (j.kicked ? ',踢掉 ' + j.kicked + ' 个会话' : '')), 'ok'); setTimeout(() => location.reload(), 900); } else { toast('改码失败:' + (j.error || '未知错误'), 'err'); b.disabled = false; } } catch (e) { toast('请求出错:' + e, 'err'); b.disabled = false; } }; }); // ---------------- 单台删除 ----------------""" text, ok = patch(text, old, new, "加开关和改码的 JS") ok_all = ok_all and ok if not ok_all: print() print(" 有锚点没匹配,没有写回") return 1 SRC.write_text(text, encoding="utf-8", newline="\n") print() print(" admin.py 已写回") return 0 if __name__ == "__main__": sys.exit(main())