Files
task_pilot/README.md
T

361 lines
11 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
TaskPilot 是一个面向 Windows 的本地定时任务管理平台。项目目标是用 React 提供任务管理界面,用 Rust 实现 Cron 调度、Windows 进程管理和运行日志。
当前已完成 Phase 1 至 Phase 6:React 任务管理界面、Rust API、SQLite 持久化、Cron 调度、三类任务执行器、实时日志、安全边界、生产静态托管和 Windows 本地打包均已落地。
仓库根目录的 `task_pilot.html` 是最初的单文件视觉原型,目前只作为 React 迁移期间的设计参考,不是正式前端入口。
React 页面沿用该原型作为视觉基线:1040px 主容器、紧凑指标卡、任务表格、660px 编辑弹窗、Cron 预览区和深色日志查看器均在保留真实功能的前提下复刻。
## 当前能力
- React 页面可以检测 Rust 后端连接状态。
- Vite 开发服务器将 `/api` 代理到 Axum 后端。
- Rust 服务提供健康检查和 OpenAPI 文档。
- OpenAPI 可以导出为稳定快照,并生成前端 TypeScript 类型。
- Cron POC 支持 5/6 位表达式、秒级调度、别名、月份/星期名称和 POSIX 日/星期语义。
- 任务可以持久化到 SQLite,并支持创建、查询、更新和删除。
- 任务列表支持名称/ID 搜索、启用状态筛选、Action 类型筛选和分页。
- 脚本、HTTP 和二进制进程任务具有独立的类型化载荷与后端校验。
- 启用的脚本任务由 Tokio 调度循环自动执行,并支持启动后恢复调度。
- 后端重启后,已启用任务仍可持久化并重新注册。
- 支持手动立即运行,重复触发时记录为 `skipped`,不会并发执行同一任务。
- 每次运行记录触发来源、状态、耗时、退出码和错误信息。
- 已实现 Windows Job Object,超时或关闭时会终止目标进程树。
- stdout 和 stderr 会写入独立运行日志,并可通过 API 增量读取。
- HTTP Action 支持 GET/POST、Header、Body、超时和期望状态码。
- 二进制进程 Action 使用 Job Object 执行,并支持工作目录、参数和环境变量。
- 运行中任务可以通过 API 或前端日志抽屉取消。
- `/api/v1/events` 使用 SSE 推送运行状态和日志增量,前端断线时自动回退到轮询。
- 运行历史支持按状态和触发方式筛选,并支持下载完整日志。
- 敏感环境变量和敏感 Header 的值不会写入系统日志,输出中的匹配值会替换为 `[REDACTED]`。
- 日志支持保留期限和总容量清理,单次日志仍有 5 MiB 上限。
- React 页面已接入真实任务列表、搜索筛选、分页、CRUD、启停、手动运行、运行历史和日志轮询。
- 三类 Action 使用不同动态表单,并以类型化载荷提交,不再固定为脚本任务。
- Playwright 已覆盖运行日志、编辑、停用、删除和窄屏布局主流程。
- 后端只监听 `127.0.0.1`,拒绝非本地 Origin。
- 浏览器写操作需要运行期随机管理令牌,进程重启后自动更换。
- Axum 在生产模式托管 React 静态资源,并支持 SPA 路由回退。
- 弹窗和日志抽屉支持初始焦点、Tab 循环、Escape 关闭和焦点恢复。
- 提供 Windows Release 打包和一键启动脚本。
- 前端与后端均具备基础测试和统一质量检查命令。
## 技术栈
### 前端
- React 19
- TypeScript 5.9
- Vite 8
- React Router
- TanStack Query
- React Hook Form
- Zod
- Vitest + Testing Library
- Playwright
### 后端
- Rust 1.95.0 MSVC
- Axum
- Tokio
- Serde
- Utoipa / OpenAPI
- Croner
- SQLx / SQLite
- Tower HTTP
### 工具链
- Volta 2.0.2
- Node.js 24.12.0
- pnpm 11.20.0
Node.js 和 pnpm 版本固定在 `frontend/package.json` 的 `volta` 字段中,Rust 版本固定在根目录的 `rust-toolchain.toml` 中。
## 环境要求
- Windows 10/11
- Volta
- Rustup
- Visual Studio C++ Build Tools,供 Rust MSVC 工具链使用
首次使用 Volta 时会自动切换到项目固定版本:
```powershell
volta --version
node --version
pnpm --version
rustc --version
```
## 本地开发
首次拉取项目后安装前端依赖:
```powershell
pnpm --dir frontend install
```
终端一,启动 Rust API:
```powershell
cargo run --manifest-path backend/Cargo.toml
```
终端二,启动 React 前端:
```powershell
pnpm --dir frontend dev
```
访问 <http://127.0.0.1:5173>。Rust 服务默认监听 `127.0.0.1:3000`,可通过 `TASK_PILOT_PORT` 修改端口:
```powershell
$env:TASK_PILOT_PORT = "3100"
cargo run --manifest-path backend/Cargo.toml
```
修改后端端口后,还需要同步调整 `frontend/vite.config.ts` 中的代理目标。
## Windows Release
构建前端生产资源、Rust Release 可执行文件和可分发目录:
```powershell
.\scripts\build-release.ps1
```
产物位于 `release/TaskPilot/`。启动打包版本:
```powershell
.\release\TaskPilot\start.ps1
```
访问 <http://127.0.0.1:3000>。打包使用同源 API,因此 Release 模式修改 `TASK_PILOT_PORT` 后无需重新构建前端。
打包目录中的 [RELEASE.md](docs/RELEASE.md) 会作为使用与故障排查说明一并复制。
SQLite 数据库默认创建在启动目录的 `data/task_pilot.db`。通过以下环境变量可以使用其他数据库路径:
```powershell
$env:TASK_PILOT_DATABASE_URL = "sqlite://data/custom.db"
cargo run --manifest-path backend/Cargo.toml
```
数据库迁移会在后端启动时自动执行。
运行日志默认写入 `logs/{task_id}/{run_id}.log`,可通过以下环境变量配置:
```powershell
$env:TASK_PILOT_LOG_DIR = "logs"
$env:TASK_PILOT_LOG_RETENTION_DAYS = "30"
$env:TASK_PILOT_LOG_MAX_TOTAL_MB = "512"
```
单次运行日志达到 5 MiB 后会停止记录后续输出。程序启动和每次运行结束时会清理过期日志,并在总容量超限时优先删除最旧日志。
## API
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| `GET` | `/api/v1/health` | 后端健康检查 |
| `GET` | `/api/v1/session` | 获取当前进程的本地管理令牌 |
| `GET` | `/api/v1/openapi.json` | 运行时 OpenAPI 文档 |
| `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?offset=0` | 增量读取运行日志 |
| `GET` | `/api/v1/runs/{run_id}/logs/download` | 下载完整运行日志 |
| `POST` | `/api/v1/runs/{run_id}/cancel` | 取消正在运行的任务 |
| `GET` | `/api/v1/events` | SSE 运行状态和日志事件 |
任务列表查询参数:
- `q`:按任务 ID 或名称搜索。
- `enabled`:按启用状态筛选。
- `action_type`:`script`、`http` 或 `process`。
- `page`:页码,默认 `1`。
- `page_size`:每页数量,默认 `20`,最大 `100`。
运行历史查询额外支持 `status` 和 `trigger` 过滤。
健康检查示例:
```json
{
"status": "ok",
"service": "task-pilot-backend",
"version": "0.1.0"
}
```
创建脚本任务示例:
```json
{
"id": "sync_data",
"name": "数据同步",
"cron": "0 0 2 * * *",
"timezone": "Asia/Shanghai",
"enabled": true,
"timeout_secs": 300,
"retry_policy": {
"max_attempts": 0,
"delay_secs": 0
},
"action": {
"type": "script",
"working_dir": "D:\\Projects\\sync",
"interpreter": "python.exe",
"script": "main.py",
"args": ["--mode", "full"],
"env": {
"PYTHONUNBUFFERED": "1"
}
}
}
```
校验失败和资源不存在时统一返回结构化错误:
```json
{
"code": "VALIDATION_ERROR",
"message": "Cron 表达式无效",
"field": "cron",
"details": {}
}
```
## 开发命令
### 完整检查
在项目根目录执行:
```powershell
.\scripts\check.ps1
```
该脚本依次运行:
- 前端 lint
- TypeScript 类型检查
- Vitest
- 前端生产构建
- Rust 格式检查
- Rust Clippy
- Rust 单元和集成测试
- Playwright 浏览器端到端测试
### 浏览器端到端测试
```powershell
pnpm --dir frontend test:e2e
```
端到端测试会清理并重建 `.e2e-data/`,启动临时 Rust 服务和 Vite,使用本机 Chrome 完成真实任务执行和页面操作。
### 生成接口契约和类型
修改后端 API DTO 或路由后执行:
```powershell
.\scripts\generate-api.ps1
```
该命令会更新:
- `contracts/openapi.json`
- `frontend/src/api/generated.ts`
生成文件不应手工修改。
### 单独检查
```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
```
## 项目结构
```text
task_pilot/
├── backend/
│ ├── src/
│ │ ├── api.rs
│ │ ├── cron.rs
│ │ ├── database.rs
│ │ ├── error.rs
│ │ ├── lib.rs
│ │ ├── main.rs
│ │ ├── process_job.rs
│ │ ├── scheduler.rs
│ │ ├── security.rs
│ │ ├── state.rs
│ │ ├── runs/
│ │ └── tasks/
│ ├── migrations/
│ ├── tests/
│ ├── Cargo.toml
│ └── Cargo.lock
├── contracts/
│ └── openapi.json
├── docs/
│ └── decisions/
├── frontend/
│ ├── e2e/
│ ├── src/
│ │ ├── components/
│ │ ├── lib/
│ │ └── pages/
│ ├── package.json
│ ├── playwright.config.ts
│ ├── vite.config.ts
│ └── vitest.config.ts
├── scripts/
│ ├── check.ps1
│ ├── build-release.ps1
│ ├── e2e.ps1
│ ├── generate-api.ps1
│ └── start.ps1
├── task_pilot.html
├── PLAN.md
├── README.md
└── rust-toolchain.toml
```
架构决策记录:
- [ADR-0001:Cron 解析与任务调度](docs/decisions/0001-cron-engine.md)
- [ADR-0002:前后端开发与生产托管](docs/decisions/0002-static-hosting.md)
## 当前限制
- 重试策略字段已持久化,但自动重试尚未实现。
- HTTP 任务执行器尚未提供响应 Header 转换等高级断言,只校验状态码。
- 敏感值脱敏基于变量名关键词和值替换,不是系统凭据保险箱。
- 当前版本是本地单用户工具,不支持多用户账户和远程部署。
- 尚未安装为 Windows 系统服务,关闭启动脚本后调度器也会停止。
- 旧 `task_pilot.html` 暂时保留在根目录作为设计参考,正式入口为 React 应用。
## 开发计划
完整架构、数据模型、API 草案、阶段任务和验收标准见 [PLAN.md](PLAN.md)。六个开发阶段均已完成,后续进入维护、功能反馈和版本迭代。