Files
Nexus/docs/design/plans/2026-05-29-push-round2.md
2026-07-08 22:31:31 +08:00

5.1 KiB
Raw Permalink Blame History

2026-05-29 推送页面迭代 Round 2 — 技术文档

涉及文件清单

文件 改动类型 涉及功能
server/infrastructure/telegram/__init__.py 新增函数 F3 Telegram
server/application/services/sync_engine_v2.py 修改 F1 重试 + F3 Telegram
server/api/websocket.py 修改参数 F1 重试
server/api/sync_v2.py 新增端点 F4 预览
server/api/schemas.py 新增模型 F4 预览
web/app/push.html 修改 F1+F2+F4+F5 前端
docs/changelog/2026-05-29-push-round2.md 新增 changelog

实现步骤

Step 1: F5 同步模式详细说明(纯前端)

文件: web/app/push.html

  1. 同步模式 </div> 后增加 <div id="syncModeDesc"> 说明文字
  2. onSyncModeChange() 函数末尾增加说明文字更新逻辑
  3. 默认显示增量同步说明

说明内容:

  • 增量同步:仅传输源目录中新增或修改的文件,目标已有且未变的文件跳过。安全快速,适合日常更新。
  • 全量同步:使目标目录与源目录完全一致,会删除目标中不存在于源的文件。⚠️ 可能造成数据丢失,建议先预览。
  • 校验和:通过文件内容校验和(而非修改时间+大小)判断是否需要传输。更精确但更慢,适合时钟不同步的环境。

Step 2: F2 服务器在线状态标识(纯前端)

文件: web/app/push.html

  1. filterServers() 中渲染 <option> 时,根据 s.is_online🟢/🔴前缀
  2. 离线服务器选项加 style="color: #666" 提示
  3. 数据来源:/api/servers/ 已返回 is_online(从 Redis heartbeat 实时读取),无需改后端

Step 3: F1 失败自动重试(后端+前端)

后端:

  1. server/application/services/sync_engine_v2.py_sync_one():

    • sync_log.status == "failed" 时,创建 PushRetryJob:
      retry_job = PushRetryJob(
          server_id=server.id, server_name=server.name,
          source_path=source_path, target_path=dest,
          status="pending", retry_count=0, max_retries=3,
          next_retry_at=datetime.now(timezone.utc),
          last_error=sync_log.error_message[:500] if sync_log.error_message else None,
      )
      retry_job = await self.retry_repo.create(retry_job)
      
    • retry_job.id 存入变量,传给 WS 广播
  2. server/api/websocket.pybroadcast_sync_progress() 增加参数:

    • retry_job_id: int = None
    • WS 消息中增加 retry_job_id 字段

前端:

  1. updateProgressFromWS() — 失败行渲染"🔄 重试"按钮
  2. updateProgressFromResult() — 同上
  3. 新增 retryServer(retryJobId) — 调用 POST /api/retries/{id}/retry
  4. 存储映射 _retryJobMap = {}server_id → retry_job_id

Step 4: F3 推送完成 Telegram 通知(后端)

  1. server/infrastructure/telegram/__init__.py — 新增函数:

    async def send_telegram_sync_complete(
        completed: int, failed: int, total: int,
        source_path: str, operator: str,
        failed_servers: list[str] = None,
        duration_seconds: int = None,
    ) -> bool:
    
    • 消息格式:HTML,含 emoji 标识状态
    • 全部成功: 🟢 推送完成
    • 部分失败: 🟡 推送完成(部分失败)+ 失败服务器名列表
    • 全部失败: 🔴 推送失败
    • source_pathsanitize_external_message() 脱敏
  2. server/application/services/sync_engine_v2.pysync_files():

    • asyncio.gather() 完成后,计算总耗时
    • 收集失败服务器名列表
    • 调用 send_telegram_sync_complete()
    • try/except 包裹,Telegram 失败不影响推送结果

Step 5: F4 文件内容预览(后端+前端)

后端:

  1. server/api/schemas.py — 新增:

    class LocalFilePreview(BaseModel):
        path: str = Field(..., min_length=1)
    
  2. server/api/sync_v2.py — 新增 POST /api/sync/local-file-preview:

    • 安全:os.path.realpath() + startswith("/tmp/nexus_upload_")
    • 限制:仅文件(非目录)+ 最多读 4KB
    • 返回:{name, size, content_base64, truncated, encoding_hint}
    • encoding_hint: 尝试 UTF-8 解码,成功 "text" 否则 "binary"
    • 审计日志

前端:

  1. renderFileManager() — 文件名加 cursor-pointer + onclick="previewLocalFile(path, name)"
  2. 新增 previewLocalFile(path, name):
    • 调用 /api/sync/local-file-preview
    • 弹出模态框显示文件名、大小、内容
    • 二进制文件提示"无法预览"
  3. 新增模态框 HTML#previewModal

依赖与配置变更

无新依赖。无数据库 schema 迁移。

测试要点

  1. F5: 切换同步模式 → 说明文字正确切换
  2. F2: 在线服务器🟢、离线服务器🔴显示正确
  3. F1: 推送失败 → 行内重试按钮 → 点击重试成功
  4. F3: 推送完成 → Telegram 收到通知
  5. F4: 点击文件 → 预览模态框显示内容
  6. F4: 点击二进制文件 → 提示无法预览
  7. F4 安全: 尝试预览 /etc/passwd → 403 拒绝

回滚方式

所有改动均为增量(新增函数/端点/前端元素),回滚只需:

  1. git revert 提交
  2. supervisorctl restart nexus