# 2026-05-29 推送页面 Round 2 迭代功能

## 变更摘要
1. F5: 同步模式详细说明 — 每种模式下方显示说明文字，切换时动态更新
2. F2: 服务器在线状态标识 — 服务器列表选项显示🟢/🔴在线/离线标识
3. F1: 失败自动重试 — 推送失败自动创建重试任务 + 失败行"🔄 重试"按钮
4. F3: 推送完成 Telegram 通知 — 推送完成/部分失败/全部失败发 Telegram 消息含详情
5. F4: 文件内容预览 — 文件管理器点击文件名弹出内容预览模态框

## 动机
推送页面 Round 1 功能已上线，但存在 5 个体验/运维缺陷：同步模式含义不明、无法区分在线/离线服务器、推送失败需手动跳转重试页面、推送完成无远程通知、文件管理器无法预览文件内容确认推送正确性。

## 涉及文件

### 后端
- `server/application/services/sync_engine_v2.py` — 失败自动创建 PushRetryJob + Telegram 通知
  - `_sync_one()`: 失败时自动创建 `PushRetryJob(status="pending", max_retries=3)`
  - `sync_files()`: 完成后调用 `send_telegram_sync_complete()` 发 Telegram
  - 返回 dict 中失败 server 增加 `retry_job_id` 字段
- `server/api/websocket.py` — `broadcast_sync_progress()` 增加 `retry_job_id` 参数
- `server/infrastructure/telegram/__init__.py` — 新增 `send_telegram_sync_complete()` 函数
  - 参数: completed, failed, total, source_path, operator, failed_servers, duration_seconds
  - 全部成功🟢 / 部分失败🟡 / 全部失败🔴 三种消息样式
  - 失败服务器列表最多显示 10 台
- `server/api/sync_v2.py` — 新增 `POST /api/sync/local-file-preview` 端点
  - 安全: `os.path.realpath()` + `startswith("/tmp/nexus_upload_")` 校验
  - 读取最多 4KB，base64 编码返回
  - UTF-8 检测: 成功则 `encoding_hint="text"`, 否则 `"binary"`
  - 超过 1MB 的文件不支持预览
  - 审计日志记录
- `server/api/schemas.py` — 新增 `LocalFilePreview` 模型

### 前端
- `web/app/push.html` — 5 项改动
  - F5: 同步模式 radio 组下方 `<div id="syncModeDesc">` 说明区域，`onSyncModeChange()` 更新
  - F2: `filterServers()` 选项文字加🟢/🔴前缀 + 离线灰色样式
  - F1: `showProgress()` 每行加 `srv_retry_{id}` 重试按钮；`updateProgressFromWS/Result()` 失败时显示；`retryServer()` 调用重试 API
  - F4: 文件名加 `cursor-pointer hover:text-brand-light` 点击预览；`previewLocalFile()` 调用 API + 模态框显示；二进制文件提示

## 安全影响
- F4 (文件预览): `os.path.realpath()` + `startswith("/tmp/nexus_upload_")` 双重校验，与 browse-local/local-file-ops 一致
- F1 (自动重试): 失败后自动创建 pending 重试任务，由后台 retry_runner 执行，复用现有重试安全机制
- F3 (Telegram): `sanitize_external_message()` 脱敏处理源路径，`html.escape()` 防 XSS

## 需要迁移/重启
- 是：后端 Python 需重启（supervisorctl restart nexus）
- 否：无数据库 schema 迁移（PushRetryJob 表已存在）

## 验证方式
1. F5: 切换同步模式 → 下方说明文字正确更新
2. F2: 服务器列表 → 在线服务器显示🟢、离线显示🔴
3. F1: 推送失败 → 失败行出现"🔄 重试"按钮 → 点击重试 → 重新推送
4. F3: 推送完成 → Telegram 收到通知（含成功/失败数、失败服务器名）
5. F4: 上传 ZIP → 文件管理器 → 点击文件名 → 弹出预览框显示内容

## 进度条
☑实现 ☐WSL验证 ☐审计8步 ☐部署 ☐健康检查 ☐浏览器验证 ☐changelog
