Files
task_pilot/PLAN.md
T
CNWei 8fdc55e17d feat: 首页改版与 uv / 镜像配置收敛,精简任务数据结构
- 页头只保留品牌与操作图标,三栏改为 1:2:1、容器最宽 1320
- 任务卡片:状态标签移入标题行并缩小,隐藏任务标识,操作行贴内容底边
- uv 配置弹窗:删掉与首页重复的状态条,三段配置改成标签页
- 镜像源:弹窗去掉默认镜像汇总,首页卡片改为展示默认镜像与运行时注入的变量
- 后端:迁移合并为单一基线 0001_init,删掉未生效的 concurrency_policy / retry_*,
  运行触发只剩 schedule / manual;contracts、前端生成类型、README / PLAN 同步
2026-11-08 21:26:47 +08:00

474 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 与镜像源不再同页)。