docs(audit): 文件搜索与监测槽点击修复审计

补 Step3/Closure/DoD;选机对话框加载失败改 snackbar 提示。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Nexus Agent
2026-06-12 03:10:15 +08:00
parent 6b8965aaf6
commit f37047f746
2 changed files with 87 additions and 0 deletions
@@ -0,0 +1,84 @@
# 审计 — 文件管理服务器搜索 + 监测槽点击修复
**日期**2026-06-12
**Changelog**:
- `docs/changelog/2026-06-11-files-server-search.md`commit `6140d68`
- `docs/changelog/2026-06-11-watch-slot-click-fix.md`commit `6b8965a`
## 范围(文件清单)
| 文件 | 变更 |
|------|------|
| `frontend/src/components/files/FilesToolbar.vue` | v-select → v-autocomplete 可搜索选服 |
| `frontend/src/composables/files/useFilesNavigation.ts` | 全量 loadServers、filteredServerList |
| `frontend/src/composables/files/useFilesPage.ts` | 暴露搜索状态 |
| `frontend/src/components/watch/WatchSlotCard.vue` | 空槽/满槽点击、hover |
| `frontend/src/components/watch/WatchSlotPickDialog.vue` | 空槽选机对话框(新增) |
| `frontend/src/components/watch/WatchSlotRow.vue` | 串联 pick 与 pinServer |
| `frontend/src/components/watch/WatchSparkline.vue` | pointer-events 防挡点击 |
| `frontend/src/composables/useWatchPins.ts` | pinServer 支持 slot_index |
| `frontend/src/pages/ServersPage.vue` | 已在监测 + 提示 |
## Step 3 规则扫描
| H | 规则 | 结论 |
|---|------|------|
| H1 | 鉴权 / IDOR | PASS — `/servers/``/watch/pins` 均走 JWT;Pin 按 admin 隔离(后端已有) |
| H2 | 无静默吞错 | PASS(审计中修复)— WatchSlotPickDialog 加载失败改 snackbarpin 失败经 formatApiError |
| H3 | 无新密钥 | PASS — 纯前端 UI |
| H4 | XSS | PASS — 无 v-html;服务器名由 API 返回、Vuetify 文本渲染 |
| H5 | 输入边界 | PASS — serverId/slotIndex 为 number;后端 Pydantic ge/le 校验 |
| H6 | 性能 | RISK 接受 — 全量拉取 servers(与终端/推送一致);2000+ 台客户端 filter |
## 逐行走读(前端 5 文件)
| 文件 | 行数 | Read | 结论 |
|------|------|------|------|
| WatchSlotPickDialog.vue | 108 | 1108 | 逐行完成 ✓ |
| WatchSlotCard.vue | 138 | 1138 | 逐行完成 ✓ |
| WatchSlotRow.vue | 98 | 198 | 逐行完成 ✓ |
| useWatchPins.tspinServer 段) | 222 | 160178 | 逐行完成 ✓ |
| useFilesNavigation.ts(搜索段) | 244 | 2647,139170 | 逐行完成 ✓ |
### Closure
| H | 判定 | 依据 |
|---|------|------|
| H1 | SAFE | fetchPagePerPage/http 带 authPOST body 仅 server_id + slot_index |
| H2 | SAFE | 选机对话框 load 失败 snackbaronPickServer 409/422 均有 snackbar |
| H3 | SAFE | 无 env/密钥 |
| H4 | SAFE | 模板无 innerHTML |
| H5 | SAFE | slot_index 03 后端校验;空 selectedId 禁用「加入」 |
| H6 | SAFE(接受) | 与项目内终端/推送全量列表模式一致 |
### 交互验收
| 场景 | 预期 | 审计结论 |
|------|------|----------|
| 文件页搜索服务器 | 名称/域名/路径/ID 过滤 | PASStype-check |
| 点击虚线空槽 | 弹出选机 → POST slot_index | PASS(代码路径完整) |
| 点击满槽卡片 | 路由 WatchMetrics + query | PASS |
| 列表 + 已 Pin | snackbar 槽位号 | PASS |
| 满 4 槽替换对话框 | replace_slot POST | PASS(既有逻辑未改坏) |
## DoD
- [x] `pytest tests/test_watch_metrics.py tests/test_watch_pins.py` — 10 passed
- [x] `cd frontend && npm run type-check`
- [x] 审计修复:WatchSlotPickDialog 加载失败 snackbar
- [x] 生产已部署 `6b8965a`(监测槽点击);`6140d68`(文件搜索)
- [ ] Gate 3 test_api.py — 需本地 API :8600 运行(审计时未起服务)
## 验证命令
```bash
.venv/bin/pytest tests/test_watch_metrics.py tests/test_watch_pins.py -q
cd frontend && npm run type-check
bash scripts/local_verify.sh # 含 Gate 3
bash deploy/pre_deploy_check.sh
```
## 备注
- 监测槽空槽点击为设计文档明确要求(`2026-06-12-server-watch-slots-design.md` §信息架构),原实现遗漏,已补。
- 无后端变更;无需迁移/重启 API(仅前端)。
@@ -1,9 +1,11 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { fetchPagePerPage } from '@/utils/paginatedFetch'
import { useSnackbar } from '@/composables/useSnackbar'
import type { ServerApiItem } from '@/types/api'
const open = defineModel<boolean>({ required: true })
const snackbar = useSnackbar()
const props = defineProps<{
slotIndex: number | null
@@ -43,6 +45,7 @@ watch(open, async (visible) => {
servers.value = res.items
} catch {
servers.value = []
snackbar('加载服务器列表失败', 'warning')
} finally {
listLoading.value = false
}