refactor: 移除 success 字段,简化为 matched 单层判定模型
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-05-09
|
||||
130
openspec/changes/archive/2026-05-09-http-probe-checker/design.md
Normal file
130
openspec/changes/archive/2026-05-09-http-probe-checker/design.md
Normal file
@@ -0,0 +1,130 @@
|
||||
## Context
|
||||
|
||||
Gateway Checker 当前是一个 Bun + React 全栈脚手架,仅包含 demo 验证逻辑(`/api/demo` 端点 + 前端展示连接状态)。项目已有完整的开发、构建、打包、测试链路。需要将其转化为一个可用的 HTTP 拨测工具。
|
||||
|
||||
现有基础设施:
|
||||
- Bun 后端:路由框架(`createFetchHandler`)、服务启动(`startServer`)、运行时配置解析(`readRuntimeConfig`)
|
||||
- React 前端:Vite + React + TypeScript,开发期通过 Vite proxy 转发 `/api/*`
|
||||
- 构建:Vite 前端构建 + Bun 单 executable 打包
|
||||
- 测试:Bun test + smoke test
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
- 提供完整的 HTTP 拨测能力:YAML 配置 → 定时并发拨测 → 结果持久化 → 可视化展示
|
||||
- 支持灵活的拨测配置:per-target interval、自定义 method/header/body、expect 校验
|
||||
- 前端 Dashboard 实时展示:总览统计、目标状态列表、历史记录、延迟趋势图
|
||||
- 保持现有项目架构风格和构建打包链路
|
||||
- 零外部运行时依赖新增(仅前端 recharts)
|
||||
|
||||
**Non-Goals:**
|
||||
- 不做告警通知(邮件/短信/Webhook),仅 Dashboard 展示
|
||||
- 不做数据自动清理/过期策略,保留全部历史记录
|
||||
- 不做 SSE/WebSocket 实时推送,用轮询即可
|
||||
- 不做拨测目标动态增删(需修改 YAML 后重启)
|
||||
- 不做认证/鉴权
|
||||
- 不做分布式/集群部署
|
||||
|
||||
## Decisions
|
||||
|
||||
### 1. 配置管理:YAML 统一配置 + 单 CLI 参数
|
||||
|
||||
**选择**:所有配置(server、数据目录、拨测默认值、目标列表)统一到 YAML 文件,CLI 只接受一个参数即配置文件路径。
|
||||
|
||||
**替代方案**:
|
||||
- CLI 参数 + 环境变量覆盖部分配置 → 配置分散,维护成本高
|
||||
- TOML 格式 → Bun 无内置支持,需引入依赖
|
||||
|
||||
**理由**:
|
||||
- 用户明确要求"配置统一到 YAML 文件"
|
||||
- `Bun.YAML.parse()` 内置支持,零依赖
|
||||
- 单参数 CLI 最简洁:`./gateway-checker ./probes.yaml`
|
||||
|
||||
### 2. 数据存储:SQLite(bun:sqlite)
|
||||
|
||||
**选择**:使用 Bun 内置 `bun:sqlite` 模块,WAL 模式运行。
|
||||
|
||||
**替代方案**:
|
||||
- JSONL 文件追加 → 聚合查询需全表扫描,趋势计算复杂
|
||||
- 外部 SQLite 库(better-sqlite3)→ bun:sqlite 已内置,无需引入
|
||||
|
||||
**理由**:
|
||||
- 趋势分析需要 `AVG(latency) GROUP BY hour` 等聚合查询,SQL 原生支持
|
||||
- bun:sqlite 是 Bun 内置模块,不违反"不引入新依赖"约束
|
||||
- WAL 模式支持并发读写
|
||||
- 单 `.db` 文件,便于管理
|
||||
|
||||
### 3. 调度模型:按 interval 分组 + 组内并发
|
||||
|
||||
**选择**:将所有 target 按其 interval 值分组,每组一个 `setInterval` timer,组内使用 `Promise.all` 并发拨测。
|
||||
|
||||
**替代方案**:
|
||||
- 全局统一 tick → 无法支持 per-target interval
|
||||
- 每个 target 独立 timer → 目标多时 timer 数量大,资源浪费
|
||||
- 使用调度队列(如 BullMQ)→ 过度设计
|
||||
|
||||
**理由**:
|
||||
- 支持 per-target interval,满足不同服务不同频率的需求
|
||||
- 相同 interval 的目标共享 timer,timer 数量 = 不同 interval 值的数量
|
||||
- 组内并发保证批量效率,组间隔离互不影响
|
||||
|
||||
### 4. 前端更新策略:轮询
|
||||
|
||||
**选择**:前端每 5-10 秒轮询 `/api/summary` 和 `/api/targets`。
|
||||
|
||||
**替代方案**:
|
||||
- SSE 服务端推送 → 实现复杂,拨测间隔 15-60s 级别无必要
|
||||
- WebSocket → 更复杂,过度设计
|
||||
|
||||
**理由**:
|
||||
- 拨测间隔本身是 15-60s,5s 轮询延迟完全可接受
|
||||
- 实现简单,无需维护长连接状态
|
||||
- Dashboard 面板按需加载趋势数据(展开详情时请求)
|
||||
|
||||
### 5. 趋势图:recharts
|
||||
|
||||
**选择**:引入 recharts 作为前端图表库。
|
||||
|
||||
**替代方案**:
|
||||
- 纯 SVG 手写 sparkline → 零依赖但代码量大,交互能力有限
|
||||
- Chart.js → 非 React 原生,需要 wrapper
|
||||
- D3 → 过于底层
|
||||
|
||||
**理由**:
|
||||
- 用户确认允许引入轻量图表库
|
||||
- recharts 是 React 原生图表库,与现有 React 技术栈一致
|
||||
- 支持折线图、迷你 Sparkline,满足需求
|
||||
- 社区活跃,文档完善
|
||||
|
||||
### 6. 目标状态判定模型
|
||||
|
||||
**选择**:两层判定——`success`(请求是否完成)+ `matched`(是否符合 expect 规则)。
|
||||
|
||||
```
|
||||
● UP = success ✓ && matched ✓
|
||||
● DOWN = !success || !matched
|
||||
```
|
||||
|
||||
**理由**:
|
||||
- 区分"网络不可达"和"返回了非预期状态码"两种故障场景
|
||||
- expect 规则可选,不配置时 matched 默认为 true
|
||||
- 前端可以根据 `success`/`matched` 分别展示不同故障原因
|
||||
|
||||
### 7. 数据库 Schema 设计
|
||||
|
||||
**targets 表**:从 YAML 同步初始化,运行时只读。
|
||||
**check_results 表**:只追加写入,索引 `(target_id, timestamp)` 加速历史查询。
|
||||
|
||||
**理由**:
|
||||
- targets 从 YAML 来,不提供运行时动态增删(符合 Non-Goals)
|
||||
- check_results 追加写入,无需更新/删除,简单可靠
|
||||
- 按时间范围查询是最高频操作,复合索引覆盖
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **[YAML 格式错误导致启动失败]** → 解析时做完整校验,输出清晰错误信息(字段缺失、格式不对、值非法等),提前失败而非运行时出错
|
||||
- **[并发拨测对目标服务器压力]** → 每组内 Promise.all 并发,但同一 group 的 tick 间隔内不会重复拨测。如果用户配置了大量目标且 interval 很短,可能对目标产生压力,这是用户配置责任
|
||||
- **[SQLite 数据文件增长]** → 当前不清理,长期运行会增长。预留清理策略接口,后续可通过配置保留天数
|
||||
- **[recharts 包体积]** → recharts gzip 后约 70KB,会增加前端 bundle 大小。对于内部工具可接受
|
||||
- **[拨测请求超时阻塞]** → 使用 `AbortController` + `setTimeout` 实现超时,避免单个慢请求阻塞整组
|
||||
- **[进程重启后丢失 timer 状态]** → 拨测是幂等的(无状态定时任务),重启后立即开始新一轮即可,无需恢复状态
|
||||
@@ -0,0 +1,36 @@
|
||||
## Why
|
||||
|
||||
项目当前只有 demo 验证链路(`/api/demo` + 前端展示连接状态),缺少核心业务逻辑。需要一个 HTTP 拨测工具,通过 YAML 配置文件定义拨测目标(URL、method、header、body、期望条件等),后端按配置定时、并行批量拨测,结果持久化到本地 SQLite,前端 Dashboard 展示各目标实时状态、可用率、延迟趋势等。
|
||||
|
||||
## What Changes
|
||||
|
||||
- **清理 demo 样例代码**:移除 `/api/demo` 路由、`DemoResponse` 类型、前端 demo 展示逻辑,保留路由框架、服务启动、构建打包链路和 `/health` 端点
|
||||
- **新增 YAML 配置文件解析**:使用 Bun 内置 `Bun.YAML.parse()` 读取拨测规则文件,包含 server 配置、数据目录、全局默认值和拨测目标列表
|
||||
- **简化 CLI 参数**:只保留一个命令行参数——配置文件路径,所有配置统一到 YAML 文件
|
||||
- **新增 SQLite 数据存储**:使用 `bun:sqlite` 存储拨测目标(从 YAML 同步)和拨测结果(追加写入),支持索引查询
|
||||
- **新增拨测调度引擎**:按 target 的 interval 分组,每组独立 timer,组内 `Promise.all` 并发拨测,支持 expect 校验(状态码、响应体、延迟阈值)
|
||||
- **新增 REST API 层**:提供总览统计、目标列表含当前状态、历史记录、趋势聚合等接口
|
||||
- **新增前端 Dashboard**:使用 React 组件展示统计卡片、目标列表表格(含状态圆点和迷你趋势线)、可展开详情面板(含完整趋势图),通过轮询 5-10s 更新数据
|
||||
- **引入 recharts 依赖**:用于趋势图和迷你 Sparkline 可视化
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
- `probe-config`: YAML 配置文件格式定义、解析校验与 CLI 启动流程
|
||||
- `probe-engine`: 拨测调度引擎——按 interval 分组定时、并发拨测、expect 校验、结果存储
|
||||
- `probe-data-store`: SQLite 数据存储——targets 同步、results 追加、索引与聚合查询
|
||||
- `probe-api`: REST API 层——总览统计、目标列表含状态、历史记录、趋势聚合
|
||||
- `probe-dashboard`: React 前端 Dashboard——统计卡片、目标表格、详情面板、趋势图
|
||||
|
||||
### Modified Capabilities
|
||||
- `fullstack-app-runtime`: CLI 参数从 `--host/--port` 简化为单个配置文件路径参数;移除 `/api/demo` 路由;新增 `/api/*` 拨测相关 API 路由
|
||||
- `frontend-development-workflow`: 前端从 demo 展示页面替换为拨测 Dashboard;移除 `/api/demo` 相关代理场景
|
||||
|
||||
## Impact
|
||||
|
||||
- **代码变更**:`src/server/app.ts` 路由重写、`src/server/config.ts` 简化、`src/shared/api.ts` 类型重写、`src/web/` 前端全部重写
|
||||
- **新增模块**:`src/server/checker/` 目录(engine、fetcher、store、config-loader、types)
|
||||
- **新增依赖**:`recharts`(前端图表)
|
||||
- **无新增外部依赖**:YAML 解析使用 Bun 内置 `Bun.YAML`,SQLite 使用 Bun 内置 `bun:sqlite`
|
||||
- **构建打包**:现有 single executable 打包链路不变,YAML 配置文件为外部文件不嵌入 executable
|
||||
- **API 变更**:**BREAKING** 移除 `/api/demo`,新增 `/api/summary`、`/api/targets`、`/api/targets/:id/history`、`/api/targets/:id/trend`
|
||||
@@ -0,0 +1,12 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: 前端开发期 API 代理
|
||||
前端开发服务器 SHALL 在本地开发期间将 `/api/*` 请求代理到 Bun 后端服务。
|
||||
|
||||
#### Scenario: 前端开发期调用拨测 API
|
||||
- **WHEN** 浏览器从 Vite 开发源请求 `/api/summary`、`/api/targets` 等拨测 API
|
||||
- **THEN** Vite SHALL 将请求转发到 Bun 后端服务,且不需要浏览器 CORS 配置
|
||||
|
||||
#### Scenario: 开发期访问非 API 前端路由
|
||||
- **WHEN** 浏览器从 Vite 开发源请求非 API 前端路由
|
||||
- **THEN** Vite SHALL 将该请求作为前端应用流量处理,而不是转发到后端
|
||||
@@ -0,0 +1,35 @@
|
||||
## MODIFIED Requirements
|
||||
|
||||
### Requirement: Bun HTTP 运行时
|
||||
系统 SHALL 运行一个 Bun HTTP server,由单个进程提供后端 API、健康检查、生产静态资源和 SPA fallback 行为。
|
||||
|
||||
#### Scenario: 启动运行时服务器
|
||||
- **WHEN** server 进程成功启动
|
||||
- **THEN** 它 SHALL 监听 YAML 配置文件中指定的 host 和 port,并记录实际 server URL
|
||||
|
||||
#### Scenario: 通过 YAML 配置提供运行时参数
|
||||
- **WHEN** 通过 YAML 配置文件提供 host、port、数据目录等参数
|
||||
- **THEN** server SHALL 使用该值,且不需要重新构建
|
||||
|
||||
#### Scenario: CLI 只接受配置文件路径
|
||||
- **WHEN** 用户通过命令行启动程序
|
||||
- **THEN** 系统 SHALL 只接受一个命令行参数作为 YAML 配置文件路径
|
||||
|
||||
#### Scenario: 提供拨测相关 API
|
||||
- **WHEN** server 启动完成
|
||||
- **THEN** 系统 SHALL 提供 `/api/summary`、`/api/targets`、`/api/targets/:id/history`、`/api/targets/:id/trend` 端点
|
||||
|
||||
### Requirement: HTTP method 语义
|
||||
系统 SHALL 为运行时端点提供明确的 HTTP method 语义,避免不支持的 method 被错误地当作成功请求处理。
|
||||
|
||||
#### Scenario: GET 请求访问运行时端点
|
||||
- **WHEN** 客户端使用 `GET` 请求 `/health` 或 `/api/*` 端点
|
||||
- **THEN** Bun server SHALL 返回对应端点的成功响应
|
||||
|
||||
#### Scenario: HEAD 请求访问运行时端点
|
||||
- **WHEN** 客户端使用 `HEAD` 请求 `/health` 或 `/api/*` 端点
|
||||
- **THEN** Bun server SHALL 返回与 `GET` 相同的成功状态和 headers,但 MUST NOT 返回响应体
|
||||
|
||||
#### Scenario: 不支持的 method 访问运行时端点
|
||||
- **WHEN** 客户端使用不支持的 method 请求 `/health` 或 `/api/*` 端点
|
||||
- **THEN** Bun server SHALL 返回 405 状态码和 Allow header
|
||||
@@ -0,0 +1,59 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 总览统计 API
|
||||
系统 SHALL 提供 `GET /api/summary` 端点,返回所有目标的总体统计信息。
|
||||
|
||||
#### Scenario: 获取总览统计
|
||||
- **WHEN** 客户端请求 `GET /api/summary`
|
||||
- **THEN** 系统 SHALL 返回 JSON 包含 total(总目标数)、up(正常数)、down(异常数)、avgLatencyMs(所有目标平均延迟)、lastCheckTime(最近一次拨测时间)
|
||||
|
||||
### Requirement: 目标列表 API
|
||||
系统 SHALL 提供 `GET /api/targets` 端点,返回所有目标及其最新状态和统计摘要。
|
||||
|
||||
#### Scenario: 获取目标列表
|
||||
- **WHEN** 客户端请求 `GET /api/targets`
|
||||
- **THEN** 系统 SHALL 返回 JSON 数组,每个元素包含目标基本信息、最近一次拨测结果(timestamp、success、statusCode、latencyMs、error、matched)和统计摘要(totalChecks、availability、avgLatencyMs、p99LatencyMs)
|
||||
|
||||
#### Scenario: 目标无历史记录
|
||||
- **WHEN** 某目标尚未执行过任何拨测
|
||||
- **THEN** 其 latestCheck 为 null,stats 中 totalChecks 为 0
|
||||
|
||||
### Requirement: 历史记录 API
|
||||
系统 SHALL 提供 `GET /api/targets/:id/history` 端点,返回指定目标的最近 N 条拨测记录。
|
||||
|
||||
#### Scenario: 获取最近历史记录
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history?limit=20`
|
||||
- **THEN** 系统 SHALL 返回最多 20 条拨测记录,按时间倒序排列
|
||||
|
||||
#### Scenario: 使用默认 limit
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history`(未指定 limit)
|
||||
- **THEN** 系统 SHALL 默认返回最近 20 条记录
|
||||
|
||||
### Requirement: 趋势聚合 API
|
||||
系统 SHALL 提供 `GET /api/targets/:id/trend` 端点,返回指定目标按小时聚合的趋势数据。
|
||||
|
||||
#### Scenario: 获取 24 小时趋势
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/trend?hours=24`
|
||||
- **THEN** 系统 SHALL 返回按小时分组的聚合数据,每个数据点包含 hour、avgLatencyMs、availability、totalChecks
|
||||
|
||||
#### Scenario: 使用默认时间范围
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/trend`(未指定 hours)
|
||||
- **THEN** 系统 SHALL 默认返回最近 24 小时的趋势数据
|
||||
|
||||
### Requirement: 保留健康检查端点
|
||||
系统 SHALL 保留 `GET /health` 端点,不受拨测功能影响。
|
||||
|
||||
#### Scenario: 访问健康检查
|
||||
- **WHEN** 客户端请求 `GET /health`
|
||||
- **THEN** 系统 SHALL 返回与之前格式一致的健康检查响应
|
||||
|
||||
### Requirement: API 错误处理
|
||||
系统 SHALL 对不存在的目标 ID 和无效参数返回适当的 HTTP 错误响应。
|
||||
|
||||
#### Scenario: 查询不存在的目标
|
||||
- **WHEN** 客户端请求 `GET /api/targets/999/history`
|
||||
- **THEN** 系统 SHALL 返回 404 状态码和错误信息
|
||||
|
||||
#### Scenario: 无效的 limit 参数
|
||||
- **WHEN** 客户端请求 `GET /api/targets/1/history?limit=abc`
|
||||
- **THEN** 系统 SHALL 返回 400 状态码和错误信息
|
||||
@@ -0,0 +1,53 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: YAML 配置文件格式
|
||||
系统 SHALL 支持通过 YAML 配置文件定义全部运行参数,包括 server 配置、数据目录、拨测默认值和拨测目标列表。
|
||||
|
||||
#### Scenario: 完整配置文件解析
|
||||
- **WHEN** 系统启动并读取包含 server、defaults、targets 的 YAML 配置文件
|
||||
- **THEN** 系统 SHALL 正确解析所有字段并用于初始化服务
|
||||
|
||||
#### Scenario: 最简配置文件解析
|
||||
- **WHEN** 系统读取只包含 targets 列表的 YAML 配置文件(省略 server 和 defaults)
|
||||
- **THEN** 系统 SHALL 使用内置默认值填充未指定的字段(host=127.0.0.1, port=3000, dir=./data, interval=30s, timeout=10s, method=GET)
|
||||
|
||||
#### Scenario: per-target 配置覆盖全局默认值
|
||||
- **WHEN** 某个 target 指定了 interval、timeout 或 method
|
||||
- **THEN** 该 target SHALL 使用其自身的值,不受 defaults 影响
|
||||
|
||||
### Requirement: CLI 参数
|
||||
系统 SHALL 通过单一命令行参数接受 YAML 配置文件路径。
|
||||
|
||||
#### Scenario: 指定配置文件启动
|
||||
- **WHEN** 用户执行 `./gateway-checker ./probes.yaml`
|
||||
- **THEN** 系统 SHALL 读取并解析指定路径的 YAML 文件作为配置
|
||||
|
||||
#### Scenario: 未提供配置文件路径
|
||||
- **WHEN** 用户启动程序时未提供任何命令行参数
|
||||
- **THEN** 系统 SHALL 以错误退出并提示需要指定配置文件路径
|
||||
|
||||
#### Scenario: 配置文件不存在
|
||||
- **WHEN** 用户指定的配置文件路径不存在
|
||||
- **THEN** 系统 SHALL 以错误退出并提示文件不存在
|
||||
|
||||
### Requirement: 配置校验
|
||||
系统 SHALL 在启动时对 YAML 配置进行完整校验,校验失败时以非零状态退出并输出清晰的错误信息。
|
||||
|
||||
#### Scenario: target 缺少必填字段
|
||||
- **WHEN** YAML 中某个 target 缺少 name 或 url 字段
|
||||
- **THEN** 系统 SHALL 以错误退出,提示哪个 target 缺少哪个字段
|
||||
|
||||
#### Scenario: target name 重复
|
||||
- **WHEN** YAML 中存在两个 name 相同的 target
|
||||
- **THEN** 系统 SHALL 以错误退出,提示重复的 name
|
||||
|
||||
#### Scenario: interval 格式非法
|
||||
- **WHEN** interval 或 timeout 值不是有效的时长格式(如 `30s`、`5m`)
|
||||
- **THEN** 系统 SHALL 以错误退出并提示格式错误
|
||||
|
||||
### Requirement: YAML 配置使用 Bun 内置解析
|
||||
系统 SHALL 使用 Bun 内置的 `Bun.YAML.parse()` 解析配置文件,不引入外部 YAML 解析库。
|
||||
|
||||
#### Scenario: 解析 YAML 内容
|
||||
- **WHEN** 系统读取 YAML 文件内容
|
||||
- **THEN** 系统 SHALL 调用 `Bun.YAML.parse()` 将内容解析为配置对象
|
||||
@@ -0,0 +1,69 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 总览统计卡片
|
||||
Dashboard SHALL 在页面顶部展示总览统计卡片,包含总目标数、正常数、异常数和平均延迟。
|
||||
|
||||
#### Scenario: 展示统计卡片
|
||||
- **WHEN** 用户打开 Dashboard 页面
|
||||
- **THEN** 页面顶部 SHALL 显示 4 个统计卡片:全部目标数、正常目标数、异常目标数、所有目标平均延迟
|
||||
|
||||
#### Scenario: 统计数据自动刷新
|
||||
- **WHEN** 页面处于打开状态
|
||||
- **THEN** 统计卡片 SHALL 每 5-10 秒自动刷新数据
|
||||
|
||||
### Requirement: 目标列表表格
|
||||
Dashboard SHALL 展示所有拨测目标的列表表格,包含名称、URL、当前状态、最新延迟和迷你趋势线。
|
||||
|
||||
#### Scenario: 展示目标列表
|
||||
- **WHEN** 用户打开 Dashboard 页面
|
||||
- **THEN** 页面 SHALL 显示表格,每行包含目标名称、URL、状态指示圆点(● UP / ● DOWN)、最新延迟值、迷你 Sparkline 趋势线
|
||||
|
||||
#### Scenario: 状态指示圆点
|
||||
- **WHEN** 目标最近一次拨测 success=true 且 matched=true
|
||||
- **THEN** 状态圆点 SHALL 显示为绿色(UP)
|
||||
- **WHEN** 目标最近一次拨测 success=false 或 matched=false
|
||||
- **THEN** 状态圆点 SHALL 显示为红色(DOWN)
|
||||
|
||||
### Requirement: 可展开的目标详情面板
|
||||
Dashboard SHALL 支持在目标列表中展开某行,显示该目标的详细状态、统计摘要、趋势图和最近历史记录。
|
||||
|
||||
#### Scenario: 展开目标详情
|
||||
- **WHEN** 用户点击目标列表中的某一行
|
||||
- **THEN** 该行下方 SHALL 展开详情面板,包含:可用率百分比、平均延迟、P99 延迟、24 小时延迟趋势折线图、最近 5-10 条拨测记录列表
|
||||
|
||||
#### Scenario: 收起目标详情
|
||||
- **WHEN** 用户再次点击已展开的目标行
|
||||
- **THEN** 详情面板 SHALL 收起
|
||||
|
||||
#### Scenario: 趋势图按需加载
|
||||
- **WHEN** 用户展开某个目标的详情面板
|
||||
- **THEN** 系统 SHALL 此时请求该目标的趋势数据,而非页面加载时预加载所有目标的趋势数据
|
||||
|
||||
### Requirement: 历史记录展示
|
||||
Dashboard SHALL 在目标详情面板中展示最近的拨测记录,包含时间、状态码、延迟和成功/失败标记。
|
||||
|
||||
#### Scenario: 展示历史记录
|
||||
- **WHEN** 用户展开目标详情面板
|
||||
- **THEN** 面板 SHALL 显示最近拨测记录列表,每条包含时间戳、HTTP 状态码(或错误信息)、延迟毫秒数、成功/失败图标
|
||||
|
||||
### Requirement: 趋势图可视化
|
||||
Dashboard SHALL 使用 recharts 库渲染趋势图,包括目标列表中的迷你 Sparkline 和详情面板中的完整折线图。
|
||||
|
||||
#### Scenario: 表格行内迷你趋势线
|
||||
- **WHEN** 目标列表表格渲染
|
||||
- **THEN** 每行 SHALL 包含一个基于 recharts 的迷你折线图,展示最近的延迟趋势
|
||||
|
||||
#### Scenario: 详情面板完整趋势图
|
||||
- **WHEN** 用户展开目标详情面板
|
||||
- **THEN** 面板 SHALL 展示基于 recharts 的完整折线图,X 轴为时间(小时),Y 轴为平均延迟,并标注可用率
|
||||
|
||||
### Requirement: 页面加载与错误状态
|
||||
Dashboard SHALL 正确处理加载状态和 API 错误。
|
||||
|
||||
#### Scenario: 首次加载
|
||||
- **WHEN** 页面首次加载且数据尚未返回
|
||||
- **THEN** 页面 SHALL 显示加载状态指示
|
||||
|
||||
#### Scenario: API 请求失败
|
||||
- **WHEN** 前端轮询 API 请求失败
|
||||
- **THEN** 页面 SHALL 显示错误提示,并在下一次轮询周期自动重试
|
||||
@@ -0,0 +1,56 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: SQLite 数据库初始化
|
||||
系统 SHALL 使用 Bun 内置 `bun:sqlite` 模块在配置的数据目录下创建 SQLite 数据库文件,并以 WAL 模式运行。
|
||||
|
||||
#### Scenario: 首次启动创建数据库
|
||||
- **WHEN** 指定的数据目录下不存在数据库文件
|
||||
- **THEN** 系统 SHALL 创建数据库文件并初始化 targets 和 check_results 表
|
||||
|
||||
#### Scenario: 数据目录不存在
|
||||
- **WHEN** 配置的数据目录路径不存在
|
||||
- **THEN** 系统 SHALL 自动创建该目录
|
||||
|
||||
#### Scenario: 数据库已存在时启动
|
||||
- **WHEN** 数据库文件已存在
|
||||
- **THEN** 系统 SHALL 直接打开数据库,不重新建表
|
||||
|
||||
### Requirement: targets 表同步
|
||||
系统 SHALL 在启动时将 YAML 配置中的目标列表同步到 SQLite targets 表。
|
||||
|
||||
#### Scenario: 首次同步目标
|
||||
- **WHEN** 数据库为空且 YAML 中定义了 N 个目标
|
||||
- **THEN** 系统 SHALL 将所有目标插入 targets 表
|
||||
|
||||
#### Scenario: 配置变更后重新同步
|
||||
- **WHEN** YAML 配置发生变更(新增、删除或修改目标)后重启
|
||||
- **THEN** 系统 SHALL 根据 name 字段匹配:新增的插入、删除的移除、修改的更新
|
||||
|
||||
### Requirement: check_results 表追加写入
|
||||
系统 SHALL 将每次拨测结果追加写入 check_results 表,不更新或删除已有记录。
|
||||
|
||||
#### Scenario: 写入拨测结果
|
||||
- **WHEN** 一次拨测完成
|
||||
- **THEN** 系统 SHALL 插入一条包含 target_id、timestamp、success、status_code、latency_ms、error、matched 的记录
|
||||
|
||||
### Requirement: 时间范围查询索引
|
||||
系统 SHALL 在 check_results 表上创建 (target_id, timestamp) 复合索引,加速按目标和时间范围的查询。
|
||||
|
||||
#### Scenario: 查询某目标的历史记录
|
||||
- **WHEN** 查询指定 target_id 的最近 N 条记录
|
||||
- **THEN** 系统 SHALL 使用索引快速定位,无需全表扫描
|
||||
|
||||
### Requirement: 聚合查询支持
|
||||
数据存储 SHALL 支持按时间段聚合查询,用于计算可用率、平均延迟、P99 延迟等统计指标。
|
||||
|
||||
#### Scenario: 计算目标可用率
|
||||
- **WHEN** 查询某目标在指定时间范围内的可用率
|
||||
- **THEN** 系统 SHALL 返回 UP (success=true AND matched=true) 的记录数占总记录数的百分比
|
||||
|
||||
#### Scenario: 计算目标平均延迟
|
||||
- **WHEN** 查询某目标在指定时间范围内的平均延迟
|
||||
- **THEN** 系统 SHALL 返回 latency_ms 的平均值(仅计算 success=true 的记录)
|
||||
|
||||
#### Scenario: 按小时聚合趋势数据
|
||||
- **WHEN** 查询某目标在指定时间范围内的趋势数据
|
||||
- **THEN** 系统 SHALL 返回按小时分组的聚合数据,包括每小时的平均延迟和可用率
|
||||
@@ -0,0 +1,83 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: 按 interval 分组调度
|
||||
系统 SHALL 将拨测目标按 interval 值分组,每组使用独立的定时器进行调度。
|
||||
|
||||
#### Scenario: 相同 interval 的目标共享定时器
|
||||
- **WHEN** 多个 target 配置了相同的 interval(如 30s)
|
||||
- **THEN** 系统 SHALL 使用同一个 `setInterval` 定时器,每次 tick 并发拨测所有该组目标
|
||||
|
||||
#### Scenario: 不同 interval 的目标各自调度
|
||||
- **WHEN** target A 配置 15s interval,target B 配置 30s interval
|
||||
- **THEN** 系统 SHALL 创建两个独立定时器,分别按各自频率调度
|
||||
|
||||
### Requirement: 组内并发拨测
|
||||
系统 SHALL 在每次调度 tick 时,使用 `Promise.all` 并发执行同组内所有目标的拨测。
|
||||
|
||||
#### Scenario: 同组目标并发执行
|
||||
- **WHEN** 调度器触发一次 tick,该组有 3 个目标
|
||||
- **THEN** 系统 SHALL 同时发起 3 个 HTTP 请求,而非顺序执行
|
||||
|
||||
#### Scenario: 单个目标失败不影响同组其他目标
|
||||
- **WHEN** 同组中某个目标的拨测请求超时或失败
|
||||
- **THEN** 其他目标的拨测 SHALL 正常完成并记录结果
|
||||
|
||||
### Requirement: HTTP 拨测执行
|
||||
系统 SHALL 对每个目标执行 HTTP 请求,支持 GET、POST、PUT、DELETE、PATCH、HEAD 方法,并携带配置的 headers 和 body。
|
||||
|
||||
#### Scenario: 执行 GET 请求
|
||||
- **WHEN** 目标配置 method 为 GET
|
||||
- **THEN** 系统 SHALL 发送 GET 请求到目标 URL
|
||||
|
||||
#### Scenario: 执行 POST 请求带 body
|
||||
- **WHEN** 目标配置 method 为 POST 且指定了 body 和 Content-Type header
|
||||
- **THEN** 系统 SHALL 发送带指定 body 的 POST 请求
|
||||
|
||||
#### Scenario: 携带自定义 headers
|
||||
- **WHEN** 目标配置了 headers(如 Authorization)
|
||||
- **THEN** 系统 SHALL 在请求中包含所有配置的 headers
|
||||
|
||||
### Requirement: 请求超时控制
|
||||
系统 SHALL 对每次拨测请求实施超时控制,超时时间使用目标配置的 timeout 值。
|
||||
|
||||
#### Scenario: 请求超时
|
||||
- **WHEN** 拨测请求在 timeout 时间内未收到响应
|
||||
- **THEN** 系统 SHALL 中止该请求,记录为失败并标注超时错误
|
||||
|
||||
#### Scenario: 请求在超时前完成
|
||||
- **WHEN** 拨测请求在 timeout 时间内收到响应
|
||||
- **THEN** 系统 SHALL 正常记录响应结果
|
||||
|
||||
### Requirement: expect 校验
|
||||
系统 SHALL 在拨测完成后根据目标的 expect 配置校验响应,校验结果记入 check result。
|
||||
|
||||
#### Scenario: 校验状态码
|
||||
- **WHEN** 目标配置了 `expect.status: [200, 201]`
|
||||
- **THEN** 系统 SHALL 检查响应状态码是否在列表中,将匹配结果记录到 matched 字段
|
||||
|
||||
#### Scenario: 校验响应体包含
|
||||
- **WHEN** 目标配置了 `expect.bodyContains: "healthy"`
|
||||
- **THEN** 系统 SHALL 检查响应体是否包含该文本,将匹配结果记录到 matched 字段
|
||||
|
||||
#### Scenario: 校验延迟阈值
|
||||
- **WHEN** 目标配置了 `expect.maxLatencyMs: 3000`
|
||||
- **THEN** 系统 SHALL 检查实际延迟是否超过阈值,将匹配结果记录到 matched 字段
|
||||
|
||||
#### Scenario: 无 expect 配置
|
||||
- **WHEN** 目标未配置任何 expect 规则
|
||||
- **THEN** 系统 SHALL 将 matched 字段设为 true
|
||||
|
||||
#### Scenario: 多条 expect 规则
|
||||
- **WHEN** 目标同时配置了 status、bodyContains 和 maxLatencyMs
|
||||
- **THEN** 系统 SHALL 所有规则全部通过时 matched 为 true,任一不通过则为 false
|
||||
|
||||
### Requirement: 拨测结果记录
|
||||
系统 SHALL 在每次拨测完成后,将结果写入 SQLite 数据存储,包含 target_id、timestamp、success、status_code、latency_ms、error、matched 字段。
|
||||
|
||||
#### Scenario: 成功拨测结果记录
|
||||
- **WHEN** 拨测请求成功完成(收到 HTTP 响应)
|
||||
- **THEN** 系统 SHALL 记录 success=true、status_code、latency_ms、matched
|
||||
|
||||
#### Scenario: 失败拨测结果记录
|
||||
- **WHEN** 拨测请求失败(网络错误、超时等)
|
||||
- **THEN** 系统 SHALL 记录 success=false、error 信息,status_code 和 latency_ms 为 null
|
||||
@@ -0,0 +1,62 @@
|
||||
## 1. 项目准备与依赖
|
||||
|
||||
- [x] 1.1 清理 demo 代码:移除 /api/demo 路由、DemoResponse 类型、前端 demo 展示逻辑
|
||||
- [x] 1.2 安装 recharts 依赖
|
||||
- [x] 1.3 创建 src/server/checker/ 目录结构和类型定义文件 types.ts
|
||||
- [x] 1.4 创建示例 YAML 配置文件 probes.example.yaml
|
||||
|
||||
## 2. 配置解析层
|
||||
|
||||
- [x] 2.1 实现 YAML 配置类型定义(ProbeConfig、TargetConfig、ExpectConfig 等)
|
||||
- [x] 2.2 实现 config-loader.ts:读取文件 + Bun.YAML.parse + 配置校验(必填字段、name 唯一性、interval 格式、port 范围)
|
||||
- [x] 2.3 重写 src/server/config.ts:CLI 只接受配置文件路径参数,从 YAML 读取 host/port/dataDir
|
||||
- [x] 2.4 为配置解析和校验编写完整测试
|
||||
|
||||
## 3. 数据存储层
|
||||
|
||||
- [x] 3.1 实现 store.ts:SQLite 初始化(建表、WAL 模式、复合索引)、数据目录自动创建
|
||||
- [x] 3.2 实现 targets 表同步逻辑(根据 name 匹配:新增插入、删除移除、修改更新)
|
||||
- [x] 3.3 实现 check_results 追加写入方法
|
||||
- [x] 3.4 实现查询方法:按 target+时间范围查询、按小时聚合趋势、计算可用率/平均延迟/P99
|
||||
- [x] 3.5 为数据存储层编写完整测试(初始化、同步、写入、查询、聚合)
|
||||
|
||||
## 4. 拨测引擎
|
||||
|
||||
- [x] 4.1 实现 fetcher.ts:HTTP 请求执行(method/header/body)+ AbortController 超时控制
|
||||
- [x] 4.2 实现 expect 校验逻辑(status 列表匹配、bodyContains、maxLatencyMs)
|
||||
- [x] 4.3 实现 engine.ts:按 interval 分组 → setInterval → Promise.all 并发拨测 → 结果写入 store
|
||||
- [x] 4.4 为 fetcher 和 expect 校验编写完整测试(使用 mock HTTP server)
|
||||
- [x] 4.5 为调度引擎编写完整测试(分组逻辑、并发执行、单目标失败隔离)
|
||||
|
||||
## 5. API 路由层
|
||||
|
||||
- [x] 5.1 定义 src/shared/api.ts 响应类型(SummaryResponse、TargetStatus、CheckResult、TrendPoint)
|
||||
- [x] 5.2 重写 src/server/app.ts:注册新 API 路由(/api/summary、/api/targets、/api/targets/:id/history、/api/targets/:id/trend),保留 /health
|
||||
- [x] 5.3 实现 API 错误处理(目标不存在返回 404、参数无效返回 400)
|
||||
- [x] 5.4 为 API 路由编写完整测试(各端点正常响应、边界情况、错误处理)
|
||||
|
||||
## 6. 前端 Dashboard
|
||||
|
||||
- [x] 6.1 创建前端组件目录结构 src/web/components/ 和 src/web/hooks/
|
||||
- [x] 6.2 实现 hooks:useSummary(轮询 /api/summary)、useTargets(轮询 /api/targets)、useTrend(按需加载趋势数据)
|
||||
- [x] 6.3 实现 StatusDot 组件(绿色 UP / 红色 DOWN 圆点)
|
||||
- [x] 6.4 实现 SummaryCards 组件(4 个统计卡片)
|
||||
- [x] 6.5 实现 SparklineChart 组件(recharts 迷你折线图)
|
||||
- [x] 6.6 实现 TrendChart 组件(recharts 完整折线图,含时间轴和双 Y 轴)
|
||||
- [x] 6.7 实现 TargetRow 组件(表格行:名称、URL、状态、延迟、Sparkline,可展开)
|
||||
- [x] 6.8 实现 TargetDetail 组件(展开面板:统计摘要、趋势图、历史记录列表)
|
||||
- [x] 6.9 实现 TargetTable 组件(组合 TargetRow 和 TargetDetail)
|
||||
- [x] 6.10 重写 App.tsx:组合 SummaryCards + TargetTable,处理加载和错误状态
|
||||
- [x] 6.11 重写 styles.css:Dashboard 布局样式(卡片、表格、详情面板、响应式)
|
||||
- [x] 6.12 更新 vite.config.ts 代理配置确保 /api/* 转发
|
||||
|
||||
## 7. 集成与启动流程
|
||||
|
||||
- [x] 7.1 重写 src/server/dev.ts 和 src/server/server.ts:启动流程为 读取配置 → 初始化 store → 同步 targets → 启动 engine → 启动 HTTP server
|
||||
- [x] 7.2 更新构建脚本确保 recharts 正确打包进 executable
|
||||
- [x] 7.3 更新 README.md:新的 CLI 用法、YAML 配置说明、API 端点文档、项目结构变更
|
||||
|
||||
## 8. 端到端验证
|
||||
|
||||
- [x] 8.1 更新 smoke test 脚本适配新的 API 端点和前端路由
|
||||
- [x] 8.2 手动验证完整流程:YAML 配置 → 启动 → 拨测执行 → Dashboard 展示
|
||||
Reference in New Issue
Block a user