Co-authored-by: Cursor <cursoragent@cursor.com>
4.3 KiB
2026-06-02 全系统 Bug 扫描与修复
日期
2026-06-02
变更摘要
对全系统进行静态代码扫描,发现并修复 7 个 HIGH 级 Bug。
HIGH-1: download_file SSH 连接在流传输前被过早释放
文件: server/api/sync_v2.py
问题: finally 块中 ssh_pool.release() 在 StreamingResponse 生成器尚未开始执行时就关闭连接,导致文件下载中断。
修复: 将 release() 移入 file_generator() 的 finally 子句,在流传输完成或客户端断开后再释放;提前退出(404/400/413)在各自分支 await release()。
HIGH-2: WebSocket JWT tv 默认值 -1 与 HTTP 中间件 0 不一致
文件: server/api/websocket.py
问题: _verify_ws_token 对无 tv 字段的旧 token 使用默认值 -1,而数据库 token_version 为 NULL/0,导致 -1 != 0 判断失败,合法用户 WebSocket 连接被拒。
修复: 对齐 HTTP 中间件逻辑,改为 payload.get("tv") or 0,DB 侧 admin.token_version or 0。
HIGH-3: retry_runner 达到 max_retries 后任务永久僵尸
文件: server/background/retry_runner.py
问题: 重试次数达到 max_retries 时,任务状态仍维持 pending 且 retry_count < max_retries 条件不再触发,形成永不被处理的僵尸记录。
修复: 成功/失败分支均检查 retry_count >= max_retries,达到上限时将 status 置为 failed,记录原因日志。
HIGH-4: sync_v2.py 和 sync_engine_v2.py 多处 stderr 为 None 引发 TypeError
文件: server/api/sync_v2.py, server/application/services/sync_engine_v2.py
问题: 多处直接执行 result["stderr"][:N],当 SSH 执行结果中 stderr 为 None 时(如连接超时)抛 TypeError: 'NoneType' object is not subscriptable,导致 API 返回 500。
修复: 所有 result["stderr"][:N] 统一改为 (result.get("stderr") or "")[:N],共修复 5 处。
HIGH-5: auth_service._verify_password bcrypt 抛 ValueError 导致 500
文件: server/application/services/auth_service.py
问题: DB 中若存在格式异常的 password_hash,bcrypt.checkpw 会抛 ValueError,导致登录接口 500 而非 401。
修复: 用 try/except 包裹 bcrypt.checkpw,异常时返回 False(密码不匹配)。
HIGH-6: install.py /lock 硬编码查询 "admin" 用户名
文件: server/api/install.py
问题: /lock 端点通过 get_by_username("admin") 验证管理员是否创建,若用户在步骤 4 使用了非 admin 的用户名(如 administrator),会错误地拒绝锁定(状态 400),且 DB 不可达时会静默放行锁定操作。
修复: 改为 SELECT COUNT(*) FROM admins WHERE is_active = 1 验证活跃管理员存在;DB 不可达时改为拒绝锁定(503)而非放行。
HIGH-7: heartbeat_flush.py 单条失败污染整批事务
文件: server/background/heartbeat_flush.py
问题: 所有服务器在同一 AsyncSession 中批量更新,单条 UPDATE 失败时仅打日志,但会话处于需要 rollback 的状态,最终 commit() 使整批失败,日志显示 "N 台更新" 实际全部失败。同时修复 datetime 时区感知问题(naive datetime 写入 MySQL)。
修复: 每台服务器使用独立的 AsyncSessionLocal() 上下文,单条失败不影响其他;fromisoformat 后统一补 tzinfo=timezone.utc。
涉及文件
| 文件 | 修复项 |
|---|---|
server/api/sync_v2.py |
HIGH-1(下载连接)、HIGH-4(stderr None, 3 处) |
server/api/websocket.py |
HIGH-2(WebSocket tv) |
server/api/install.py |
HIGH-6(/lock 用户名) |
server/application/services/auth_service.py |
HIGH-5(bcrypt ValueError) |
server/application/services/sync_engine_v2.py |
HIGH-4(stderr None, 2 处) |
server/background/retry_runner.py |
HIGH-3(僵尸任务) |
server/background/heartbeat_flush.py |
HIGH-7(事务污染、时区) |
需要迁移/重启
- 后端重启生效(
supervisorctl restart nexus) - 无数据库 migration 变更
验证方式
python -c "import server.main"— 导入无报错 ✓- 登录测试 WebSocket 实时推送
- 下载文件功能测试
- 查看
push_retry_jobs表确认无status=pending的僵尸记录