- 页头只保留品牌与操作图标,三栏改为 1:2:1、容器最宽 1320 - 任务卡片:状态标签移入标题行并缩小,隐藏任务标识,操作行贴内容底边 - uv 配置弹窗:删掉与首页重复的状态条,三段配置改成标签页 - 镜像源:弹窗去掉默认镜像汇总,首页卡片改为展示默认镜像与运行时注入的变量 - 后端:迁移合并为单一基线 0001_init,删掉未生效的 concurrency_policy / retry_*, 运行触发只剩 schedule / manual;contracts、前端生成类型、README / PLAN 同步
474 lines
18 KiB
Markdown
474 lines
18 KiB
Markdown
# TaskPilot 开发计划
|
||
|
||
> 更新时间:2026-10-18
|
||
> 目标后端:Rust
|
||
> 目标前端:React
|
||
> 首要平台:Windows
|
||
> 当前阶段:Phase 6 已完成,项目进入维护与迭代
|
||
|
||
## 1. 项目目标
|
||
|
||
将现有的单文件 HTML 原型升级为一个真实可用的本地定时任务管理平台,满足以下核心场景:
|
||
|
||
- 创建、编辑、启用、停用和删除定时任务。
|
||
- 使用 Cron 表达式按计划自动执行任务,也可以手动立即执行。
|
||
- 支持脚本、HTTP 请求和 Windows 二进制进程三类任务。
|
||
- 实时展示任务状态、下次执行时间和每次运行结果。
|
||
- 查看真实 stdout、stderr、退出码和耗时。
|
||
- 任务配置和运行历史在重启后仍然保留。
|
||
- 超时或取消时可靠终止目标进程及其子进程。
|
||
- React 前端成为真实 API 的使用者,不再用静态数据或 `alert` 模拟成功。
|
||
|
||
### 首版范围
|
||
|
||
- Windows 单机部署。
|
||
- 单用户、本地运行,默认只监听 `127.0.0.1`。
|
||
- 单后端实例。
|
||
- SQLite 持久化。
|
||
- 同一任务默认不并发执行,重叠触发时跳过并记录原因。
|
||
- 日志按运行实例保存,并配置容量和保留期限。
|
||
|
||
### 暂不纳入首版
|
||
|
||
- 多租户、登录和远程用户管理。
|
||
- 多机分布式调度。
|
||
- DAG、任务依赖和分布式队列。
|
||
- 集群高可用。
|
||
- 跨平台进程树管理。
|
||
- 移动端专用应用。
|
||
|
||
这些能力在单机版本稳定后再评估,避免首版范围过大。
|
||
|
||
## 2. 当前基线
|
||
|
||
仓库目前只有 `task_pilot.html` 和文档,没有前后端工程。现有页面具备完整视觉原型,但运行、删除、保存和日志均为演示行为。
|
||
|
||
已确认的本地工具链:
|
||
|
||
| 工具 | 版本 | 来源 |
|
||
| --- | --- | --- |
|
||
| Volta | 2.0.2 | 本地安装 |
|
||
| Node.js | 24.12.0 | Volta |
|
||
| npm | 11.6.2 | Volta |
|
||
| pnpm | 11.20.0 | Volta |
|
||
| Rust | 1.95.0 | `stable-x86_64-pc-windows-msvc` |
|
||
| Cargo | 1.95.0 | 本地 Rust 工具链 |
|
||
|
||
开发时使用 pnpm,并通过 `package.json` 的 `volta` 字段固定 Node.js 和 pnpm 版本。Rust 使用 `rust-toolchain.toml` 固定 MSVC 工具链。
|
||
|
||
## 3. 技术方案
|
||
|
||
### 3.1 前端
|
||
|
||
- React + TypeScript + Vite。
|
||
- pnpm 作为包管理器,Volta 固定工具版本。
|
||
- React Router 管理任务列表和任务详情路由。
|
||
- TanStack Query 管理服务端状态、缓存、刷新和错误状态。
|
||
- React Hook Form + Zod 管理表单和前端校验。
|
||
- 原生 `fetch` 封装类型化 API 客户端。
|
||
- 沿用现有 CSS 变量和视觉语言,组件样式使用 CSS Modules,不先引入大型 UI 框架。
|
||
- Vitest + React Testing Library 进行单元和组件测试。
|
||
- Playwright 进行关键业务流程端到端测试。
|
||
|
||
### 3.2 后端
|
||
|
||
- Rust + Axum + Tokio。
|
||
- Serde 定义 API 和持久化 DTO。
|
||
- SQLx + SQLite 负责迁移、查询和事务。
|
||
- `tracing` + `tracing-subscriber` 输出结构化运行日志。
|
||
- `reqwest` 执行 HTTP 任务。
|
||
- Windows Job Object 管理子进程树,实现超时和取消时的可靠终止。
|
||
- SSE 或轮询先用于运行状态与日志更新,后续再评估 WebSocket。
|
||
- Cron 引擎在 Phase 1 通过小型 POC 选定,必须支持动态增删任务、秒级 Cron、时区和下次执行时间计算。
|
||
|
||
初步候选为 `tokio-cron-scheduler`。进入正式实现前需要验证其维护状态、Cron 语义和动态任务能力;如果不能满足要求,则改用 Cron 解析 crate 与自建 Tokio 调度循环。
|
||
|
||
### 3.3 接口契约
|
||
|
||
- 后端 DTO 是 API 数据的唯一来源。
|
||
- 使用 OpenAPI 生成接口描述,并提交稳定快照供前端生成 TypeScript 类型。
|
||
- 前端不得手写与后端重复的 DTO 类型。
|
||
- 接口变更时,必须同时更新 OpenAPI 快照、生成类型和契约测试。
|
||
|
||
### 3.4 总体结构
|
||
|
||
```text
|
||
React SPA
|
||
|
|
||
| REST /api/v1 + SSE
|
||
v
|
||
Axum HTTP API
|
||
|
|
||
+--> Task Service --> SQLite
|
||
|
|
||
+--> Scheduler
|
||
|
|
||
+--> Script Executor
|
||
+--> HTTP Executor
|
||
+--> Process Executor
|
||
|
|
||
+--> Windows Job Object
|
||
+--> Log Capture
|
||
```
|
||
|
||
计划目录结构:
|
||
|
||
```text
|
||
task_pilot/
|
||
├── backend/
|
||
│ ├── Cargo.toml
|
||
│ ├── migrations/
|
||
│ ├── src/
|
||
│ └── tests/
|
||
├── frontend/
|
||
│ ├── package.json
|
||
│ ├── src/
|
||
│ └── tests/
|
||
├── contracts/
|
||
│ └── openapi.json
|
||
├── docs/
|
||
│ └── decisions/
|
||
├── task_pilot.html # 迁移完成前保留作视觉参考
|
||
├── PLAN.md
|
||
├── README.md
|
||
└── rust-toolchain.toml
|
||
```
|
||
|
||
## 4. 核心数据模型
|
||
|
||
### 4.1 Task
|
||
|
||
```text
|
||
Task
|
||
├── id 用户可读的唯一任务标识
|
||
├── name 显示名称
|
||
├── cron 5 位或 6 位 Cron 表达式
|
||
├── timezone 默认使用系统时区,可选择 IANA 时区
|
||
├── enabled 是否参与自动调度
|
||
├── timeout_secs 单次执行超时
|
||
├── action 带 type 标签的任务动作
|
||
├── created_at
|
||
└── updated_at
|
||
```
|
||
|
||
### 4.2 Action
|
||
|
||
```text
|
||
Action
|
||
├── script
|
||
│ ├── working_dir
|
||
│ ├── interpreter
|
||
│ ├── script
|
||
│ ├── args[]
|
||
│ └── env{}
|
||
├── http
|
||
│ ├── url
|
||
│ ├── method
|
||
│ ├── headers{}
|
||
│ ├── body
|
||
│ └── expected_status[]
|
||
└── process
|
||
├── executable
|
||
├── working_dir
|
||
├── args[]
|
||
└── env{}
|
||
```
|
||
|
||
- 参数必须使用结构化数组,禁止默认经过 Shell 拼接。
|
||
- 环境变量需要区分普通值和敏感值;敏感值返回前端时使用遮罩。
|
||
- 路径、URL、超时、Cron 和任务类型必须执行后端校验。
|
||
|
||
### 4.3 TaskRun
|
||
|
||
```text
|
||
TaskRun
|
||
├── id
|
||
├── task_id
|
||
├── trigger schedule / manual / retry
|
||
├── status queued / running / succeeded / failed / timed_out / cancelled / skipped
|
||
├── started_at
|
||
├── finished_at
|
||
├── duration_ms
|
||
├── exit_code
|
||
├── error_message
|
||
└── logs
|
||
```
|
||
|
||
## 5. API 草案
|
||
|
||
所有业务接口使用 `/api/v1` 前缀。
|
||
|
||
| 方法 | 路径 | 用途 |
|
||
| --- | --- | --- |
|
||
| `GET` | `/api/v1/health` | 健康检查 |
|
||
| `GET` | `/api/v1/tasks` | 查询任务,支持搜索、筛选和分页 |
|
||
| `POST` | `/api/v1/tasks` | 创建任务 |
|
||
| `GET` | `/api/v1/tasks/{id}` | 查询任务详情 |
|
||
| `PUT` | `/api/v1/tasks/{id}` | 更新任务 |
|
||
| `DELETE` | `/api/v1/tasks/{id}` | 删除任务 |
|
||
| `POST` | `/api/v1/tasks/{id}/enable` | 启用任务 |
|
||
| `POST` | `/api/v1/tasks/{id}/disable` | 停用任务 |
|
||
| `POST` | `/api/v1/tasks/{id}/run` | 手动执行 |
|
||
| `GET` | `/api/v1/tasks/{id}/runs` | 查询运行历史 |
|
||
| `GET` | `/api/v1/runs/{run_id}` | 查询单次运行结果 |
|
||
| `GET` | `/api/v1/runs/{run_id}/logs` | 分页或增量读取日志 |
|
||
| `GET` | `/api/v1/events` | SSE 状态与日志事件 |
|
||
|
||
统一错误结构至少包含:
|
||
|
||
```json
|
||
{
|
||
"code": "TASK_CRON_INVALID",
|
||
"message": "Cron expression is invalid",
|
||
"field": "cron",
|
||
"details": {}
|
||
}
|
||
```
|
||
|
||
## 6. 里程碑
|
||
|
||
### Phase 0:工程基线
|
||
|
||
状态:已完成
|
||
|
||
状态:进行中
|
||
|
||
- [x] 盘点现有 HTML 原型和功能。
|
||
- [x] 生成项目 README。
|
||
- [x] 确认 Volta、Node.js、pnpm 和 Rust 工具链。
|
||
- [x] 制定本开发计划。
|
||
- [x] 为 Cron 引擎和静态资源托管方式编写 ADR。
|
||
|
||
验收:计划中的技术边界明确,后续任务有清晰顺序。
|
||
|
||
### Phase 1:工程脚手架与契约
|
||
|
||
状态:已完成(2026-10-18)
|
||
|
||
- [x] 创建 `frontend/` React + TypeScript + Vite 工程。
|
||
- [x] 通过 Volta 固定 Node.js 24.12.0 和 pnpm 11.20.0。
|
||
- [x] 创建 `backend/` Axum 工程并固定 Rust 1.95.0。
|
||
- [x] 配置 Vite 开发代理到 Rust API。
|
||
- [x] 提供 `/api/v1/health`。
|
||
- [x] 配置 Cargo fmt、clippy、test,以及前端 lint、typecheck、test、build。
|
||
- [x] 添加 OpenAPI 生成与前端类型生成脚本。
|
||
- [x] 确定 Cron 引擎依赖并完成 POC。
|
||
- [x] 保留旧 HTML,作为迁移期间的视觉参考。
|
||
|
||
验收:
|
||
|
||
- `pnpm install`、`pnpm build`、`cargo test` 和 `cargo clippy` 可执行。
|
||
- 前端开发服务器能够通过代理访问 Rust 健康检查。
|
||
- OpenAPI 快照可以重复生成,前后端对接口类型没有手工漂移。
|
||
|
||
### Phase 2:任务 CRUD 与持久化
|
||
|
||
状态:已完成(2026-10-18)
|
||
|
||
- [x] 创建 SQLite 迁移和任务表。
|
||
- [x] 实现领域模型、仓储层和服务层。
|
||
- [x] 实现任务列表、详情、创建、更新、删除接口。
|
||
- [x] 实现 Cron、时区、超时、路径和 Action 类型校验。
|
||
- [x] 实现统一 API 错误格式。
|
||
- [x] 增加任务列表搜索、筛选和分页参数。
|
||
- [x] 为 CRUD、校验和持久化编写后端测试。
|
||
|
||
说明:独立启停接口调整到 Phase 3,与调度器注册和取消同时实现,避免产生只改数据库但不更新调度的中间状态。
|
||
|
||
验收:
|
||
|
||
- 创建三类任务后重启进程,数据仍然存在。
|
||
- 非法 Cron、空路径、无效 URL 和错误 Action 字段被后端拒绝。
|
||
- 删除不存在的任务返回明确错误,而不是成功提示。
|
||
|
||
### Phase 3:调度器与脚本任务闭环
|
||
|
||
状态:已完成(2026-10-18)
|
||
|
||
- [x] 启动时从 SQLite 加载启用任务并注册调度。
|
||
- [x] 支持创建、更新、启停和删除任务时动态刷新调度。
|
||
- [x] 实现手动立即执行。
|
||
- [x] 实现脚本任务执行,使用结构化参数,不经过 Shell。
|
||
- [x] 捕获 stdout、stderr、退出码和耗时。
|
||
- [x] 实现同一任务并发触发时跳过。
|
||
- [x] 通过 Windows Job Object 实现超时和取消时的进程树终止。
|
||
- [x] 记录每次运行状态并处理进程异常退出。
|
||
- [x] 引入确定性时间计算测试和实时秒级调度测试,验证调度逻辑。
|
||
|
||
验收:
|
||
|
||
- 创建一个每秒执行的临时脚本任务后,自动运行并生成可查看日志。
|
||
- 超时脚本及其子进程被终止,运行状态为 `timed_out`。
|
||
- 后端重启后调度恢复,错过的历史运行不会批量重复执行。
|
||
- 前端尚未接入前,可通过 API 完整验证脚本任务链路。
|
||
|
||
### Phase 4:React 前端迁移与垂直切片
|
||
|
||
状态:已完成(2026-10-18)
|
||
|
||
- [x] 将现有页面拆分为布局、指标卡、任务表格、任务编辑器和日志查看器。
|
||
- [x] 接入真实任务列表、详情、创建、更新、删除和启停 API。
|
||
- [x] 接入手动运行 API,并展示 `queued/running/succeeded/failed` 状态。
|
||
- [x] 使用表单校验替代浏览器无效的 `required` 假象。
|
||
- [x] 根据 Action 类型提交完整、不同的载荷。
|
||
- [x] 实现加载、空数据、失败、重试和保存中状态。
|
||
- [x] 日志页面先使用轮询读取真实运行日志。
|
||
- [x] 移除伪造的成功提示和静态任务数据。
|
||
|
||
验收:
|
||
|
||
- 页面中的每个 CRUD、启停和手动运行操作都对应真实 API。
|
||
- 刷新页面后任务状态与 SQLite 一致。
|
||
- HTTP 和进程任务不会在尚未实现执行器时被错误提交为脚本任务。
|
||
- 表单错误能定位到具体字段,Cron 校验错误来自前后端一致结果。
|
||
|
||
### Phase 5:HTTP、进程任务与实时日志
|
||
|
||
状态:已完成(2026-10-18)
|
||
|
||
- [x] 实现 HTTP Executor,支持 GET/POST、Header、Body 和期望状态码。
|
||
- [x] 实现二进制进程 Executor。
|
||
- [x] 将 SSE 接入任务状态和日志增量更新,轮询作为降级方案。
|
||
- [x] 支持取消正在运行的任务。
|
||
- [x] 增加运行历史列表、状态/触发筛选、退出码、错误摘要和日志下载。
|
||
- [x] 实现日志大小上限、保留期限和清理策略。
|
||
- [x] 对敏感环境变量和 Header 值进行输入遮罩,并在日志中脱敏。
|
||
|
||
验收:
|
||
|
||
- 三类任务均可从 React 页面创建、执行和查看结果。
|
||
- 运行中日志可以持续更新,完成后状态正确收敛。
|
||
- HTTP 非预期状态码和进程非零退出码能明确标记失败。
|
||
- 取消或超时不会遗留目标进程树。
|
||
|
||
### Phase 6:质量、安全与交付
|
||
|
||
状态:已完成(2026-10-18)
|
||
|
||
- [x] 补齐后端单元、集成和 Windows 平台测试。
|
||
- [x] 补齐前端组件测试和 Playwright 主流程测试。
|
||
- [x] 完成键盘导航、焦点管理、ARIA 和窄屏布局。
|
||
- [x] 移除所有用户输入的 `innerHTML` 拼接,统一安全文本渲染。
|
||
- [x] 后端默认绑定 `127.0.0.1`,配置严格 CORS 和 Origin 校验。
|
||
- [x] 增加本地管理令牌或等价的本机接口保护方案。
|
||
- [x] 检查路径逃逸、命令注入、日志注入和敏感信息泄露。
|
||
- [x] 由 Axum 托管生产构建后的 React 静态资源。
|
||
- [x] 提供一键启动脚本、生产构建说明和故障排查文档。
|
||
- [x] 更新 README,并评审是否将旧 HTML 移入 `legacy/`。
|
||
|
||
说明:旧 `task_pilot.html` 暂时保留在根目录作为视觉参考;正式运行入口为 React 构建产物。
|
||
|
||
验收:
|
||
|
||
- 完整测试、格式检查、静态检查和前端生产构建通过。
|
||
- 单机 Release 构建能够启动、自动打开或明确提示访问地址。
|
||
- 关闭程序后,任务数据仍保留;重新启动后启用任务恢复调度。
|
||
- README 中的命令全部经过实际验证。
|
||
|
||
## 7. 开发顺序与检查点
|
||
|
||
每个阶段按以下顺序推进:
|
||
|
||
1. 先确定数据模型和 API 契约。
|
||
2. 实现后端能力并补充自动化测试。
|
||
3. 通过 API 或命令行验证真实行为。
|
||
4. 接入 React 页面,不先做没有后端支持的假交互。
|
||
5. 运行完整检查并更新本计划的勾选状态。
|
||
|
||
阶段之间保留可运行状态,不进行长期无法启动的大规模重构。旧 HTML 一直保留到 React 版本完成等价迁移。
|
||
|
||
## 8. 测试策略
|
||
|
||
### 后端
|
||
|
||
- 单元测试:Cron 校验、任务校验、状态转换、重试和并发策略。
|
||
- 集成测试:使用临时 SQLite 验证 CRUD、迁移和重启恢复。
|
||
- 调度测试:使用可控时钟验证下次执行时间和动态调度。
|
||
- 平台测试:在 Windows 上验证脚本参数、退出码、超时和进程树终止。
|
||
- 契约测试:检查 OpenAPI 与实现一致。
|
||
|
||
### 前端
|
||
|
||
- 组件测试:表单类型切换、错误展示、状态徽章和权限式操作可用性。
|
||
- API Mock 测试:加载、失败、空数据和重试。
|
||
- E2E:创建脚本任务、手动执行、查看日志、编辑、停用和删除。
|
||
|
||
### 提交前检查
|
||
|
||
计划提供以下统一命令,具体脚本在 Phase 1 落地:
|
||
|
||
```powershell
|
||
# 前端
|
||
pnpm --dir frontend lint
|
||
pnpm --dir frontend typecheck
|
||
pnpm --dir frontend test
|
||
pnpm --dir frontend build
|
||
|
||
# 后端
|
||
cargo fmt --manifest-path backend/Cargo.toml --check
|
||
cargo clippy --manifest-path backend/Cargo.toml --all-targets -- -D warnings
|
||
cargo test --manifest-path backend/Cargo.toml
|
||
```
|
||
|
||
## 9. 安全与可靠性约束
|
||
|
||
- 后端默认只监听本机地址,不默认暴露到局域网。
|
||
- 脚本和进程任务使用结构化参数调用,不通过字符串拼接进入 Shell。
|
||
- 所有路径在执行前规范化并校验允许范围。
|
||
- 用户提交的文本只通过 React 文本节点渲染,禁止直接拼接 HTML。
|
||
- 环境变量中的敏感值在列表、详情、日志和错误信息中脱敏。
|
||
- stdout/stderr 必须并发读取,避免管道写满导致子进程卡死。
|
||
- 所有运行都有超时和取消路径,终止时覆盖子进程树。
|
||
- 日志设置单次大小和总容量上限,避免磁盘被无限占用。
|
||
- 数据库迁移必须可重复执行,并在失败时给出明确错误。
|
||
- 前端不得把请求失败或未接入能力显示为成功。
|
||
|
||
## 10. 主要风险
|
||
|
||
| 风险 | 影响 | 应对 |
|
||
| --- | --- | --- |
|
||
| Cron crate 语义或动态调度能力不满足要求 | 自动执行时间错误 | Phase 1 POC,覆盖 5/6 位、时区、日和星期语义 |
|
||
| Windows 子进程树难以可靠终止 | 超时后遗留进程 | 使用 Job Object,并增加真实 Windows 集成测试 |
|
||
| stdout/stderr 处理不当 | 子进程阻塞或日志丢失 | 异步并发读取,设置背压和日志上限 |
|
||
| SQLite 与调度器状态不一致 | 重启后漏跑或重复跑 | 事务更新、启动时重建调度、记录运行状态 |
|
||
| 前端迁移范围过大 | 长期无法交付 | 先完成脚本任务垂直切片,再扩展三类 Action |
|
||
| 敏感环境变量泄露 | 凭据暴露 | 遮罩、日志脱敏,后续接入系统凭据存储 |
|
||
| 动态依赖下载受网络影响 | 工程搭建受阻 | 锁定版本和 lockfile,必要时分阶段安装并记录依赖 |
|
||
| 本地接口被其他网页调用 | 未授权操作 | localhost 绑定、Origin 校验和本机管理令牌 |
|
||
|
||
## 11. MVP 完成标准
|
||
|
||
满足以下条件后,第一版可以视为完成:
|
||
|
||
- React 页面完全使用真实 API 数据。
|
||
- 任务 CRUD、启停和手动运行形成闭环。
|
||
- 脚本、HTTP 和二进制进程任务至少各有一条成功路径和一条失败路径验证。
|
||
- Cron 自动调度、时区和下次运行时间可验证。
|
||
- 任务配置、运行历史和日志重启后保留。
|
||
- 超时和取消可以终止 Windows 进程树。
|
||
- 页面能够展示运行中、成功、失败、超时和跳过状态。
|
||
- 后端默认仅本机可访问,敏感值不会出现在日志中。
|
||
- 自动化测试、格式检查和 Release 构建通过。
|
||
|
||
## 12. 后续维护
|
||
|
||
六个计划阶段已经完成。后续版本优先评估:
|
||
|
||
1. 自动重试与退避策略。
|
||
2. Windows 服务安装和开机自启。
|
||
3. 系统凭据存储与更严格的秘密管理。
|
||
4. 任务模板、导入导出和批量操作。
|
||
5. 运行指标、告警和长期监控。
|
||
6. HTTP 响应断言、认证方案和请求重放。
|
||
|
||
## 13. 已完成的后继迭代
|
||
|
||
计划阶段之外已经交付、并已写入 ADR 的改动:
|
||
|
||
- **本地 uv 管理**([ADR-0009](docs/decisions/0009-uv-and-managed-environments.md)):uv 只用程序目录副本或指定路径,
|
||
不读系统 PATH;可从科大镜像下载并校验 sha256;`UV_*` 环境变量可配置,两个路径变量默认相对程序目录(`python` / `uv-cache`)。
|
||
- **托管虚拟环境**:`data/envs/<名称>`、uv 下载的 Python 版本管理、预装包与已装包查看。
|
||
- **环境时机调整**:新建任务保存时用 uv 创建 `.venv`,运行期只校验;运行任务会注入平台 uv、`UV_*` 与默认 `UV_DEFAULT_INDEX`。
|
||
- **界面**:顶部品牌栏 + 三栏工作台,uv / 镜像源 / 虚拟环境各自独立弹窗(uv 与镜像源不再同页)。
|
||
|