fix: 代码审查修复 + 精简重写 README

后端修复:
- 白板删除踢人失效:delete_whiteboard 改 async def,删除后直接 await
  hub.close_board()。原实现用 asyncio.get_running_loop() 在同步 REST handler
  (threadpool)里调用必抛 RuntimeError 被 except 吞掉,close_board 从不执行。
  同时移除 service 的 hub 依赖(close_board 改由 controller 调用,service 只管 DB)。
- delete_file 去重复查询:原先 get_out_with_disk_path + get_by_id 查两次,
  合并为一次;磁盘 unlink 失败加 logger.warning(原静默吞掉致磁盘泄漏无记录)。
- get_hub 单例加 threading.Lock 双重检查(防 REST threadpool 与 WS 事件循环
  并发首访各建一个 hub)。
- file_controller 公开 /api/files list 加 Query(ge=1, le=10000) 约束(原无上限可 DoS)。

前端修复:
- applyRemoteUpdate 有未发送编辑时重发:合并远端更新后若本地有 pending 编辑
  (editor.value !== lastSentText)重新 scheduleSend,避免被 lastSentText 短路丢弃。
- init 不覆盖未发送编辑:断线重连后若本地有未发送内容,作为新版本发上去而非被 init 覆盖。
- applyRemoteUpdate 仅在编辑器已有焦点时恢复焦点,避免抢按钮焦点。
- api() 401 时 location.reload() 触发浏览器 Basic Auth 弹窗(原只 toast 卡死)。

README:
- 精简重写,补全 Ubuntu 从 0 安装、Apache 反代(含 WS)、配置项表格、防火墙说明。
This commit is contained in:
zikai
2026-07-22 01:03:48 +00:00
parent 4de2648178
commit 30a263ed50
8 changed files with 222 additions and 254 deletions

View File

@@ -18,6 +18,7 @@ from __future__ import annotations
import asyncio
import logging
import threading
import time
from dataclasses import dataclass, field
@@ -167,16 +168,21 @@ class WhiteboardHub:
# 进程内单例(由 main.py lifespan / controller 共享)
_hub: WhiteboardHub | None = None
_hub_lock = threading.Lock()
def get_hub() -> WhiteboardHub:
"""获取/创建进程内单例 hub。线程安全lifespan 预热后通常不再进锁)。"""
global _hub
if _hub is None:
_hub = WhiteboardHub()
with _hub_lock:
if _hub is None:
_hub = WhiteboardHub()
return _hub
def reset_hub() -> None:
"""测试用:重置单例。"""
global _hub
_hub = None
with _hub_lock:
_hub = None