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]。
  • Python 脚本支持显式虚拟环境配置,并会自动向上探测 .venv、venv、env。
  • 编辑器会随工作目录实时探测虚拟环境,显示 已关联虚拟环境: .venv\Scripts\python.exe 并同步更新调用预览。
  • 工作目录支持「选择文件夹…」打开页面内的目录浏览器(盘符 → 逐级进入 → 使用此目录),选中后回填输入框,同时保留手动输入。
  • 执行前会校验 pyvenv.cfg 和 Scripts/python.exe,优先使用虚拟环境解释器,并注入 PATH、VIRTUAL_ENV 和无缓冲输出设置。
  • 日志按系统 ANSI 代码页(如 GBK)解码非 UTF-8 输出,中文程序不再显示为 ����,且长日志跨 64 KiB 分块读取不会丢失字符。
  • 日志支持保留期限和总容量清理,单次日志仍有 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 时会自动切换到项目固定版本:

volta --version
node --version
pnpm --version
rustc --version

本地开发

首次拉取项目后安装前端依赖:

pnpm --dir frontend install

终端一,启动 Rust API:

cargo run --manifest-path backend/Cargo.toml

终端二,启动 React 前端:

pnpm --dir frontend dev

访问 http://127.0.0.1:5173。Rust 服务默认监听 127.0.0.1:3000,可通过 TASK_PILOT_PORT 修改端口:

$env:TASK_PILOT_PORT = "3100"
cargo run --manifest-path backend/Cargo.toml

修改后端端口后,还需要同步调整 frontend/vite.config.ts 中的代理目标。

Windows Release

构建前端生产资源、Rust Release 可执行文件和可分发目录:

.\scripts\build-release.ps1

产物位于 release/TaskPilot/。启动打包版本:

.\release\TaskPilot\start.ps1

访问 http://127.0.0.1:3000。打包使用同源 API,因此 Release 模式修改 TASK_PILOT_PORT 后无需重新构建前端。

打包目录中的 RELEASE.md 会作为使用与故障排查说明一并复制。

SQLite 数据库默认创建在启动目录的 data/task_pilot.db。通过以下环境变量可以使用其他数据库路径:

$env:TASK_PILOT_DATABASE_URL = "sqlite://data/custom.db"
cargo run --manifest-path backend/Cargo.toml

数据库迁移会在后端启动时自动执行。

运行日志默认写入 logs/{task_id}/{run_id}.log,可通过以下环境变量配置:

$env:TASK_PILOT_LOG_DIR = "logs"
$env:TASK_PILOT_LOG_RETENTION_DAYS = "30"
$env:TASK_PILOT_LOG_MAX_TOTAL_MB = "512"
$env:TASK_PILOT_LOG_ENCODING = "auto"

TASK_PILOT_LOG_ENCODING 默认为 auto:逐行先按 UTF-8 解码,失败时回退到系统 ANSI 代码页(简体中文为 GBK),保证中文程序输出不会变成 ����。需要强制某一种编码时可填写 gbk、utf-8、windows-1251 等标签。

单次运行日志达到 5 MiB 后会停止记录后续输出。程序启动和每次运行结束时会清理过期日志,并在总容量超限时优先删除最旧日志。

API

方法 路径 用途
GET /api/v1/health 后端健康检查
GET /api/v1/session 获取当前进程的本地管理令牌
GET /api/v1/openapi.json 运行时 OpenAPI 文档
POST /api/v1/python/venv 探测工作目录会关联的 Python 虚拟环境
GET /api/v1/system/directories 列出盘符或指定目录下的子文件夹
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 过滤。

健康检查示例:

{
  "status": "ok",
  "service": "task-pilot-backend",
  "version": "0.1.0"
}

创建脚本任务示例:

{
  "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"
    }
  }
}

校验失败和资源不存在时统一返回结构化错误:

{
  "code": "VALIDATION_ERROR",
  "message": "Cron 表达式无效",
  "field": "cron",
  "details": {}
}

开发命令

完整检查

在项目根目录执行:

.\scripts\check.ps1

该脚本依次运行:

  • 前端 lint
  • TypeScript 类型检查
  • Vitest
  • 前端生产构建
  • Rust 格式检查
  • Rust Clippy
  • Rust 单元和集成测试
  • Playwright 浏览器端到端测试

浏览器端到端测试

pnpm --dir frontend test:e2e

端到端测试会清理并重建 .e2e-data/,启动临时 Rust 服务和 Vite,使用本机 Chrome 完成真实任务执行和页面操作。

生成接口契约和类型

修改后端 API DTO 或路由后执行:

.\scripts\generate-api.ps1

该命令会更新:

  • contracts/openapi.json
  • frontend/src/api/generated.ts

生成文件不应手工修改。

单独检查

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

项目结构

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

架构决策记录:

当前限制

  • 重试策略字段已持久化,但自动重试尚未实现。
  • HTTP 任务执行器尚未提供响应 Header 转换等高级断言,只校验状态码。
  • 敏感值脱敏基于变量名关键词和值替换,不是系统凭据保险箱。
  • Conda 环境不会按 venv 自动激活,目前需要显式填写其 Python 解释器路径。
  • 当前版本是本地单用户工具,不支持多用户账户和远程部署。
  • 尚未安装为 Windows 系统服务,关闭启动脚本后调度器也会停止。
  • 旧 task_pilot.html 暂时保留在根目录作为设计参考,正式入口为 React 应用。

开发计划

完整架构、数据模型、API 草案、阶段任务和验收标准见 PLAN.md。六个开发阶段均已完成,后续进入维护、功能反馈和版本迭代。

S
Description
No description provided
Readme
946 KiB
Languages
Rust 46.2%
TypeScript 40.1%
HTML 6.2%
CSS 4.5%
Python 1.8%
Other 1.2%