Files
ykxmz/_build/dbg_setcode.py
T
xmz7891 d4f335b8f9 管理页新增:允许/禁止新设备接入 + 修改设备配对码
== 功能一:允许新设备接入开关 ==

关掉之后只有已经登记过的电脑能继续上线,没见过的新设备一律拒绝。
状态存在 relay_state.json 里,重启不丢。

判断新设备用的是 deviceId,不是配对码 ——
这一点很关键:管理员给老设备改过码之后,
那台设备必须仍然能进来;按码判断的话它会被自己的开关挡在外面。
测试里专门有一条 A3b 守这个行为。

被拒时回 blocked=True,让客户端能区分被管理员挡了
和配对码冲突,而不是去匹配中文错误文案(文案一改就坏)。

== 功能二:修改任意设备的配对码 ==

管理页每台设备一个「改码」按钮,填 6 位数字即生效。
要点:
  * 新旧码冲突检查
  * 旧码映射必须删掉,否则变成孤儿条目,
    管理页配对码占用的数字会虚高
  * 改码踢掉该设备当前会话(会话绑在码上)
  * 立刻持久化

== 顺手修掉两个真 bug ==

1. 改码功能一上线就 500:Session 用了 __slots__,
   根本没有 session_id 属性(会话是以 token 为 key 存在 dict 里的),
   写 s.session_id 直接 AttributeError。
   改成遍历 sessions.items() 拿 key。

2. snapshot() 里有同样的错(清理超时设备那段):
   平时 stale 列表是空的所以从没炸过,一旦真有设备超过 6 小时
   没上报就会 500。这次一起修了。

== 管理页 UI ==
  * 设备小节加了「允许新设备接入」勾选框
  * 每行加「改码」按钮(走 prompt,不引入弹窗层)
  * 改码按钮用 data-recode 而不是 data-device ——
    现有单台删除按 button.danger[data-device] 绑,
    带上 data-device 会被当成删除按钮,点改码变删设备。
    测试 C6 专门断言这一点。

== 测试 ==
新增 _tests/test_admin_device_control.py(23 项),
已注册进 _tests/verify_all.ps1 第 3e 阶段。
2026-10-07 17:01:14 +08:00

62 lines
2.0 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
r'''单独查 set_code 为什么没返回 kicked。'''
import json
import time
import urllib.error
import urllib.request
BASE = "http://127.0.0.1:8080"
KEY = open("/root/lanremote-relay/data/admin_key.txt", encoding="utf-8").read().strip()
def call(path, payload=None, method="POST", admin=False):
data = json.dumps(payload).encode() if payload is not None else None
h = {"Content-Type": "application/json"}
if admin:
h["X-Admin"] = KEY
req = urllib.request.Request(BASE + path, data=data, headers=h, method=method)
try:
with urllib.request.urlopen(req, timeout=15) as r:
body = r.read().decode("utf-8", "replace")
try:
return r.status, json.loads(body)
except Exception:
return r.status, body
except urllib.error.HTTPError as e:
body = e.read().decode("utf-8", "replace")
try:
return e.code, json.loads(body)
except Exception:
return e.code, body
stamp = int(time.time())
dev = "dbg-%d" % stamp
c1 = "%06d" % ((stamp + 1) % 1000000)
c2 = "%06d" % ((stamp + 2) % 1000000)
print("1. 注册设备")
print(" ", call("/v1/agent/register",
{"deviceId": dev, "code": c1, "name": "dbg", "version": "1.8.2"}))
print("2. attach 造一个会话")
print(" ", call("/v1/client/attach", {"code": c1, "name": "手表"}))
print("3. 看会话数")
st, h = call("/v1/health", method="GET")
print(" sessions =", h.get("sessions"))
print("4. 改码 %s -> %s" % (c1, c2))
st, r = call("/v1/admin/set_code", {"deviceId": dev, "code": c2}, admin=True)
print(" 状态码:", st)
print(" 响应:", json.dumps(r, ensure_ascii=False))
print("5. 再改一次(同样改 %s -> 另一个码)" % c2)
c3 = "%06d" % ((stamp + 3) % 1000000)
st, r = call("/v1/admin/set_code", {"deviceId": dev, "code": c3}, admin=True)
print(" 响应:", json.dumps(r, ensure_ascii=False))
print("6. 清理")
print(" ", call("/v1/admin/delete", {"deviceId": dev}, admin=True))