Files
zTools2/tests/manual_whiteboard_kick.py
zikai 7188c62d3a refactor: 白板路由改 /wb 前缀,消除 HTML 与 REST 同路径冲突
问题:GET /whiteboard/{board_id} 同时被 REST(返回 JSON)与 main.py 的 HTML 页面
注册,FastAPI 按注册顺序匹配到 REST,导致浏览器访问拿到 JSON 而非前端页面。

改为按职责分命名空间,避免冲突:
- HTML 页面:/wb/{id}(main.py)、/wb-admin(main.py,Basic Auth)
- 公开 REST:GET /api/wb/{id}(前端 init 拉取初始笔画)
- WS:/ws/wb/{id}(实时同步 + 心跳)
- 管理 REST:GET /api/admin/wb、DELETE /api/admin/wb/{id}(Basic Auth)

前端 whiteboard.js / whiteboard_admin.js、测试脚本、README 路径同步更新。
旧 /whiteboard/* 路径不再注册(404)。
2026-07-21 14:51:33 +00:00

53 lines
1.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""验证删除白板时,在线 WS 连接被服务端踢出(收到 error 帧并断连)。"""
from __future__ import annotations
import asyncio
import json
import urllib.request
import websockets
BASE_WS = "ws://127.0.0.1:6867/ws/wb"
BOARD = "kicktest"
AUTH = "Basic YTo2NjUxMTMxNQ==" # a:66511315
async def main() -> None:
async with websockets.connect(f"{BASE_WS}/{BOARD}") as a:
await a.send(json.dumps({"type": "hello", "client_id": "A"}))
init = json.loads(await asyncio.wait_for(a.recv(), timeout=2))
assert init["type"] == "init"
print("A connected, init received")
# 通过 REST 删除白板(带 Basic Auth
req = urllib.request.Request(
f"http://127.0.0.1:6867/api/admin/wb/{BOARD}",
method="DELETE",
headers={"Authorization": AUTH},
)
with urllib.request.urlopen(req) as r:
print("delete response:", r.read().decode())
# A 应收到 error 帧随后连接关闭
try:
raw = await asyncio.wait_for(a.recv(), timeout=3)
msg = json.loads(raw)
print("A received:", msg)
assert msg["type"] == "error", "应收到 error 通知"
except (websockets.ConnectionClosed, asyncio.TimeoutError) as e:
print("连接关闭/超时:", e)
# 确认连接已断
try:
await asyncio.wait_for(a.recv(), timeout=2)
print("ERROR: 连接仍存活(应已断开)")
except websockets.ConnectionClosed:
print("连接已被服务端关闭 ✅")
except asyncio.TimeoutError:
print("连接未关闭但无消息(部分通过)")
if __name__ == "__main__":
asyncio.run(main())