# Changelog: 全站 bug 审查修复 (前端9项 + 后端5项)

**日期**: 2026-06-01
**变更摘要**: 全站巡检发现并修复 14 个 bug（1 P0 + 9 P1 + 4 P2），涵盖前端全局搜索崩溃、TOTP登录流程断裂、服务器编辑域名污染、后端Redis阻塞、并发session竞态等

## 动机

对 Nexus 全站进行系统性 bug 审查，发现多个影响生产使用的问题。最严重的 3 个：全局搜索因 `http` 未导入直接崩溃、TOTP 双因素登录完全不可用、编辑服务器后域名被端口字符串污染导致后续连接失败。

## 涉及文件

| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `frontend/src/App.vue` | 修改 | 添加 `http` 导入 |
| `frontend/src/pages/LoginPage.vue` | 修改 | TOTP异常捕获 + 锁定倒计时响应式 |
| `frontend/src/pages/ServersPage.vue` | 修改 | 编辑时 domain 不再拼接端口 |
| `frontend/src/pages/PushPage.vue` | 修改 | Set 索引 → 迭代器 |
| `frontend/src/pages/SettingsPage.vue` | 修改 | API字符串值转数字 |
| `frontend/src/api/index.ts` | 修改 | 删除重复 getList 定义 |
| `frontend/src/pages/TerminalPage.vue` | 修改 | 路径验证正则放松 |
| `frontend/src/composables/useWebSocket.ts` | 修改 | 关闭旧连接再建新连接 |
| `server/api/websocket.py` | 修改 | redis.keys → scan_iter |
| `server/application/services/auth_service.py` | 修改 | TTL 仅在无TTL时设置 |
| `server/api/servers.py` | 修改 | 批量操作加 asyncio.Lock |
| `server/api/settings.py` | 修改 | 弃用API替换 |

## 详细变更

### 前端 P0 修复

1. **App.vue `http` 未导入** — 第320行 `doSearch()` 调用 `http.get()` 但只导入了 `api`，导致 `ReferenceError: http is not defined`。修复：`import { api, http } from '@/api'`

### 前端 P1 修复

2. **TOTP 登录流程断裂** — `api()` 在后端返回 202 时抛 `TotpRequiredError`（非 `ApiError` 子类），`LoginPage` catch 只检查 `e instanceof ApiError`，TOTP 用户看到"网络错误"无法登录。修复：导入 `TotpRequiredError` 并在 catch 中优先检查

3. **ServersPage 编辑域名污染** — `editServer()` 设置 `form.address = domain:port`，保存时 `domain: form.address` 发送含端口字符串。修复：`form.address = item.domain`

4. **PushPage Set 索引无效** — `selectedIds.value[0]` 对 `Set<number>` 返回 `undefined`。修复：`selectedIds.value.values().next().value`

5. **SettingsPage 类型转换** — API 返回 `value: "80"` 字符串直接赋值给 number 字段。修复：正则检测纯数字字符串并 `Number()` 转换

### 前端 P2 修复

6. **api/index.ts 重复定义** — `getList` 方法定义两次，删除第二个

7. **LoginPage 锁定倒计时** — `remainingMinutes` computed 依赖 `Date.now()` 非响应式。修复：使用 `ref(Date.now())` + `watch(lockoutUntil)` + 30秒定时器

8. **TerminalPage 路径验证过严** — 正则 `/^[a-zA-Z0-9/._\-]+$/` 排除空格/中文/~。修复：`/^[^\x00-\x1f\x7f"']+$/` 仅排除控制字符和引号

9. **useWebSocket 连接泄漏** — CONNECTING/CLOSING 状态时创建新连接但不关闭旧连接。修复：关闭旧连接后再创建新的

### 后端 P1 修复

10. **websocket.py redis.keys() 阻塞** — `KEYS` 命令 O(N) 扫描整个键空间。修复：`scan_iter(match="ws:count:*")` 非阻塞迭代

11. **auth_service.py TTL 重置** — 每次 `sadd` 后 `expire` 重置 TTL，频繁刷新的管理员 refresh token 键永不过期。修复：`ttl = await redis.ttl(key)`，仅在 `ttl < 0` 时设置 expire

12. **servers.py 并发 session commit** — 批量操作中 `asyncio.gather` 并发协程共享同一 db session 并 `commit()`。修复：添加 `asyncio.Lock()` 序列化 commit

### 后端 P2 修复

13. **settings.py asyncio.get_event_loop()** — Python 3.10+ 弃用。修复：`asyncio.get_running_loop()`

14. **settings.py datetime.utcfromtimestamp()** — Python 3.12+ 弃用。修复：`datetime.fromtimestamp(ts, tz=timezone.utc)`

## 是否需迁移/重启

- ✅ 需重启 — 后端代码变更
- ✅ 前端需重新构建部署

## 验证方式

1. 登录页 → 输入 admin/Nexus@2026 → 成功跳转仪表盘 ✓
2. 全局搜索框 → 输入"机器人" → 无崩溃（之前会 ReferenceError）✓
3. 控制台零错误 ✓
4. 健康检查 → `ok` ✓
