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

4.9 KiB
Raw Permalink Blame History

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

涉及文件清单

文件 改动类型 涉及功能
server/application/services/sync_engine_v2.py 修改 G1 取消检查
server/api/sync_v2.py 新增端点 G1 取消端点
server/api/schemas.py 新增模型 G1 取消请求
server/api/servers.py 修改参数 G4 历史筛选分页
web/app/push.html 修改 G1-G6 全部前端
docs/changelog/2026-05-29-push-round3.md 新增 changelog

实现步骤

Step 1: G1 推送取消(后端+前端)

后端:

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

    class SyncCancel(BaseModel):
        batch_id: str = Field(..., min_length=1, max_length=32)
    
  2. server/api/sync_v2.py — 新增 POST /api/sync/cancel:

    • Redis SET sync:cancel:{batch_id} 1 EX 3600
    • 审计日志
    • 返回 {"cancelled": true, "batch_id": ...}
  3. server/application/services/sync_engine_v2.py:

    • _rsync_push(): 执行前检查 sync:cancel:{batch_id},存在则返回 {"exit_code": -1, "stderr": "推送已取消"}
    • sync_files(): _sync_one() 任务启动前(async with sem 之后、rsync 之前)检查取消标记
    • 取消的服务器 status="cancelled",不创建 retry job

前端:

  1. doPush() 执行期间显示"取消推送"按钮
  2. cancelPush() — 调用 POST /api/sync/cancel
  3. updateProgressFromWS() — 处理 status=cancelled
  4. 取消后按钮隐藏,已完成的服务器结果保留

Step 2: G6 拖拽上传 ZIP(纯前端)

文件: web/app/push.html

  1. #sourceUpload 区域改为拖拽区:
    • <div>dragover/dragleave/drop 事件
    • dragover: 高亮边框 border-brand
    • drop: 取 ev.dataTransfer.files[0],调用 uploadZip() 逻辑
  2. 保留 <input type="file"> 隐藏,拖拽区 click 触发 zipFile.click()
  3. 文件类型校验:仅 .zip

Step 3: G5 浏览器推送完成通知(纯前端)

文件: web/app/push.html

  1. doPush() 开始时请求 Notification.requestPermission()
  2. 推送完成(doPush() finally 块前)发送通知:
    if (Notification.permission === 'granted') {
      new Notification('Nexus 推送完成', {body: `${completed} 成功, ${failed} 失败`});
    }
    
  3. 降级:授权拒绝时 document.title 闪烁(3 次交替 🔔 Nexus / Nexus — 推送

Step 4: G2 推送页面直接调度(纯前端)

文件: web/app/push.html

  1. "开始推送"按钮旁加" 定时推送"按钮
  2. 点击弹出简单模态框:datetime-local 选择器 + 确认按钮
  3. 确认后调用 POST /api/schedules/body 包含:
    • name: "手动推送-{时间}"
    • schedule_type: "push"
    • run_mode: "once"
    • fire_at: 选择的时间
    • source_path, server_ids, sync_mode: 从当前表单取
  4. 成功后 toast 提示 + 链接到调度页面

Step 5: G3 推送模板/收藏(纯前端)

文件: web/app/push.html

  1. localStorage key: nexus_push_templates — JSON 数组
  2. 模板结构: {name, source_path, target_path, sync_mode, server_ids[]}
  3. UI:
    • 表单上方"模板"下拉框 + "保存当前配置"按钮
    • 选择模板 → 自动填充 source_path/target_path/sync_mode/server_ids
    • 下拉旁"删除"按钮删除模板
  4. saveTemplate(): 弹出 prompt 输入名称 → 存入 localStorage
  5. loadTemplate(): 从 localStorage 读取模板列表 → 渲染下拉框
  6. applyTemplate(): 填充表单字段
  7. deleteTemplate(): 从 localStorage 移除

Step 6: G4 推送历史筛选(后端+前端)

后端:

  1. server/api/servers.pylist_sync_logs():
    • 新增参数: status: Optional[str], server_id: Optional[int], page: int = 1
    • 增加过滤条件到 SQLAlchemy 查询
    • 增加 COUNT(*) 查询获取总数
    • 返回增加: total, page, pages

前端:

  1. 历史区增加筛选控件:
    • 状态下拉: 全部/成功/失败
    • 服务器搜索输入框
  2. loadHistory() 增加参数传递
  3. 分页控件: 上一页/下一页 + 页码显示

依赖与配置变更

无新依赖。无数据库 schema 迁移。 G1 需要已有的 Redis 连接。

测试要点

  1. G1: 推送 10 台 → 取消 → 仅已完成的有结果,其余显示"已取消"
  2. G1: 推送完成后再取消 → 无效果(batch_id 已失效)
  3. G2: 定时推送 → 调度页出现对应任务 → 到时间自动执行
  4. G3: 保存模板 → 刷新 → 选择模板 → 表单填充正确
  5. G3: 删除模板 → 下拉框更新
  6. G4: 筛选"失败" → 仅显示失败记录 → 分页翻页
  7. G5: 推送完成 → 浏览器通知弹出 → 点击通知回到页面
  8. G6: 拖拽 ZIP → 上传解压成功 → 文件管理器显示
  9. G6: 拖拽非 ZIP → 提示仅支持 .zip

回滚方式

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

  1. git revert 提交
  2. supervisorctl restart nexus