1
0

docs: 重写 README 为结构化项目文档,精简示例配置

This commit is contained in:
2026-05-16 20:58:04 +08:00
parent 146cef982e
commit 04c24e6796
2 changed files with 128 additions and 273 deletions

261
README.md
View File

@@ -1,29 +1,61 @@
# DiAL
基于 Bun + TypeScript 的多类型拨测监控工具。通过 YAML 配置文件定义 HTTP 和命令行拨测目标,后端按配置定时并发拨测,结果持久化到本地 SQLite前端 Dashboard 展示各目标实时状态、可用率、耗时趋势等并支持手动、10 秒、30 秒、1 分钟、5 分钟刷新频率切换,以及系统、明亮、黑暗三种主题模式。主题模式选择会保存在当前浏览器本地存储中,同一浏览器再次访问时自动恢复。
<p align="center">
<strong>轻量级多类型拨测监控工具</strong>
</p>
<p align="center">
基于 Bun + TypeScript 构建 · YAML 配置驱动 · 内置 Dashboard
</p>
---
DiAL 是一个自托管的拨测监控工具,支持 **HTTP**、**命令行** 和 **数据库** 三种拨测类型。通过 YAML 配置文件定义拨测目标,后端定时并发执行拨测并将结果持久化到本地 SQLite前端 Dashboard 展示各目标的实时状态、可用率和耗时趋势。
**功能亮点:**
- 多种拨测类型HTTPGET/POST/PUT 等、Cmd命令行执行、DBPostgreSQL/MySQL/SQLite
- 丰富的校验规则状态码、响应头、JSONPath、CSS 选择器、XPath、正则匹配、数值比较等
- 响应式 Dashboard实时状态、可用率统计、耗时趋势图、手动/自动刷新
- 多主题支持:系统、明亮、黑暗三种主题模式
- 零外部依赖:数据存储使用 SQLite无需额外数据库服务
## 快速开始
**前置条件:** [Bun](https://bun.sh/) >= 1.0
```bash
# 克隆仓库
git clone https://github.com/your-org/DiAL.git
cd DiAL
# 安装依赖
bun install
# 复制示例配置并按需修改
cp probes.example.yaml probes.yaml
# 启动开发服务器
bun run dev probes.yaml
```
`bun run dev` 启动双进程开发服务器Vite :5173 + Bun API :3000访问 `http://127.0.0.1:5173`
`bun run dev` 会同时启动 Vite 开发服务器(`http://127.0.0.1:5173`)和 API 服务器(`http://127.0.0.1:3000`),访问前端地址即可使用 Dashboard
## 开发验证
## 生产部署
```bash
bun run check # schema:check + typecheck + lint + bun test
bun run verify # check + build
# 构建
bun run build
# 运行
./dist/dial-server ./probes.yaml
```
`verify` 会基于当前源码重新构建生产 executable。原 smoke test 已移除executable/E2E 验证后续单独补充
构建产物为独立可执行文件,只需一个 YAML 配置文件即可运行
## 配置文件
程序通过 YAML 配置文件定义所有运行参数
程序通过 YAML 配置文件定义所有运行参数,完整示例参见 [`probes.example.yaml`](probes.example.yaml)。
```yaml
# yaml-language-server: $schema=./probe-config.schema.json
@@ -38,7 +70,7 @@ runtime:
retention: "7d"
defaults:
interval: "5s"
interval: "30s"
timeout: "10s"
http:
method: GET
@@ -65,23 +97,10 @@ targets:
Content-Type:
contains: "application/json"
body:
- contains: "slideshow"
- json:
path: "$.slideshow.title"
equals: "Sample Slide Show"
- name: "HTML 页面示例"
type: http
http:
url: "https://httpbin.org/html"
expect:
status: [200]
body:
- contains: "Moby-Dick"
- xpath:
path: "/html/body/h1/text()"
equals: "Herman Melville - Moby-Dick"
- name: "Bun 脚本检查"
type: cmd
cmd:
@@ -106,145 +125,121 @@ targets:
### 配置说明
- **server**: 服务配置(均可省略,使用默认值)
- `host`: 监听地址,默认 `127.0.0.1`
- `port`: 监听端口,默认 `3000`
- `dataDir`: 数据目录,默认 `./data`,相对路径基于配置文件所在目录解析
- **runtime**: 运行时配置
- `maxConcurrentChecks`: 最大并发拨测数,默认 `20`
- `retention`: 历史数据保留时长,默认 `7d`,支持 `ms`/`s`/`m`/`h`/`d` 单位
- **defaults**: 全局默认值(均可省略)
- `interval`: 拨测间隔,默认 `30s`
- `timeout`: 超时时间,默认 `10s`
- `http`: HTTP 类型默认值
- `method`: HTTP 方法,默认 `GET`,必须使用大写枚举值,支持 `GET``HEAD``POST``PUT``PATCH``DELETE``OPTIONS`
- `maxBodyBytes`: 响应体最大字节数,默认 `100MB`
- `headers`: 默认请求头target 中的 headers 会合并覆盖 defaults 中的同名头)
- `cmd`: Cmd 类型默认值
- `maxOutputBytes`: 输出最大字节数,默认 `100MB`
- `cwd`: 默认工作目录(相对于配置文件所在目录解析,默认 `.`
- **targets**: 拨测目标列表(必填)
- `name`: 目标名称(必填,唯一)
- `type`: 目标类型,`http``cmd``db`(必填)
- `group`: 分组名称(可选,默认 `"default"`
- `http`: HTTP 拨测配置type 为 http 时必填)
- `url`: 目标 URL
- `method``headers``body`: 请求参数(`headers` 会与 `defaults.http.headers` 合并target 优先)
- `ignoreSSL`: 是否忽略 HTTPS 证书校验,默认 `false`,用于自签名或私有证书服务
- `maxRedirects`: 最大重定向跟随次数,默认 `0`(不跟随重定向)
- `cmd`: 命令行拨测配置type 为 cmd 时必填)
- `exec`: 可执行文件名或路径
- `args`: 命令行参数列表
- `env`: 环境变量覆盖(可选,继承进程环境变量并合并覆盖)
- `cwd`: 工作目录(可选,相对于配置文件所在目录解析,默认 `.`
- `db`: 数据库拨测配置type 为 db 时必填)
- `url`: 数据库连接字符串,支持 `postgres://``mysql://``sqlite://` 协议
- `query`: SQL 查询语句(可选,不配置时仅测试连接)
- `interval``timeout`: 覆盖全局默认值
- `expect`: 期望校验
- `status`: 可接受的状态码列表HTTP支持精确状态码和范围模式`"2xx"`)混合配置;未指定时默认 `[200]`
- `exitCode`: 可接受的退出码列表Cmd未指定时不校验退出码
- `headers`: 响应头校验HTTP支持字符串精确匹配或操作符对象
- `maxDurationMs`: 最大耗时阈值(毫秒)
- HTTP覆盖完整执行含重定向、响应体读取和 expect 校验)
- Cmd覆盖命令执行耗时含 stdout/stderr 读取)
- `body`: HTTP 响应体校验(数组,可组合使用)
- `contains`: 响应体包含的文本
- `regex`: 响应体匹配的正则表达式(启动期会拒绝嵌套量词等存在 ReDoS 风险的模式)
- `json`: JSONPath 提取值比较
- `path`: JSONPath 表达式(必填,如 `$.slideshow.title`
- 比较操作符(可选,无操作符时仅检查路径对应值是否存在)
- `css`: CSS 选择器提取 HTML 元素比较
- `selector`: CSS 选择器(必填)
- `attr`: 提取元素属性值而非文本内容(可选,如 `href``class`
- 比较操作符(可选,无操作符时仅检查元素是否存在)
- `xpath`: XPath 提取 XML/HTML 节点比较
- `path`: XPath 表达式(必填,如 `/html/body/h1/text()`
- 比较操作符(可选,无操作符时仅检查节点是否存在)
- `stdout` / `stderr`: Cmd 输出校验(数组,每项为一个操作符对象)
- `rowCount`: DB 查询返回行数校验(支持操作符对象)
- `rows`: DB 查询结果逐行校验(数组,每项为列名→操作符映射)
- 比较操作符:`equals`(默认)、`contains``match`(正则,启动期会拒绝存在 ReDoS 风险的模式)、`empty``exists``gte``lte``gt``lt`
#### server 服务配置(均可省略,使用默认值)
大小说明:`maxBodyBytes``maxOutputBytes` 支持单位 `KB``MB``GB`,也可直接使用数字(非负安全整数字节数)。
| 字段 | 说明 | 默认值 |
| --------- | ------------------------------------------ | ----------- |
| `host` | 监听地址 | `127.0.0.1` |
| `port` | 监听端口 | `3000` |
| `dataDir` | 数据目录,相对路径基于配置文件所在目录解析 | `./data` |
配置校验:系统启动时会先用 TypeBox 生成的 JSON Schema 契约校验字段类型、必填字段、枚举、数组/对象形状和未知字段,再执行语义 validator 校验 target name 唯一性、URL、正则、JSONPath、XPath、size/duration 解析等规则。非法配置会阻止启动并输出中文错误信息。
#### runtime — 运行时配置
未知字段:除 `http.headers``defaults.http.headers``expect.headers``cmd.env` 等动态键值表外,未知字段会导致启动失败。配置备注请使用 YAML 注释,不要添加 `note``comment` 等未声明字段。
| 字段 | 说明 | 默认值 |
| --------------------- | ------------------------------------------------ | ------ |
| `maxConcurrentChecks` | 最大并发拨测数 | `20` |
| `retention` | 历史数据保留时长,支持 `ms`/`s`/`m`/`h`/`d` 单位 | `7d` |
JSON Schema仓库根目录导出 `probe-config.schema.json`,可在 YAML 文件顶部添加 `# yaml-language-server: $schema=./probe-config.schema.json` 获取编辑器提示和静态校验。该 schema 由运行期契约 fragments 生成,提交前可用 `bun run schema:check` 检查同步。
#### defaults — 全局默认值(均可省略)
时长格式支持:`500ms``30s``5m``2h``7d`
| 字段 | 说明 | 默认值 |
| -------------------- | -------------------------------------------------------------------- | ------- |
| `interval` | 拨测间隔 | `30s` |
| `timeout` | 超时时间 | `10s` |
| `http.method` | HTTP 方法,支持 `GET`/`HEAD`/`POST`/`PUT`/`PATCH`/`DELETE`/`OPTIONS` | `GET` |
| `http.maxBodyBytes` | 响应体最大字节数 | `100MB` |
| `http.headers` | 默认请求头target 中的 headers 会合并覆盖同名头) | — |
| `cmd.maxOutputBytes` | 输出最大字节数 | `100MB` |
| `cmd.cwd` | 默认工作目录(相对于配置文件所在目录) | `.` |
## API 端点
#### targets — 拨测目标列表(必填)
| 端点 | 说明 |
| ----------------------------------------------------------------- | ------------------------------------------------------------ |
| `GET /health` | 健康检查 |
| `GET /api/meta` | 运行时元信息checker 类型列表) |
| `GET /api/dashboard?window=24h&recentLimit=30` | Dashboard 首屏聚合数据summary + targets |
| `GET /api/targets/:id/metrics?from=ISO&to=ISO&bucket=1h` | 指定目标的统计、可靠性指标和按小时趋势 |
| `GET /api/targets/:id/history?from=ISO&to=ISO&page=1&pageSize=20` | 指定目标的拨测记录(时间范围 + 分页,`pageSize` 最大 `200` |
每个 target 的通用字段:
### 响应字段
| 字段 | 说明 | 必填 |
| ---------- | ----------------------------- | -------------------- |
| `name` | 目标名称(全局唯一) | 是 |
| `type` | 目标类型:`http``cmd``db` | 是 |
| `group` | 分组名称 | 否,默认 `"default"` |
| `interval` | 覆盖全局拨测间隔 | 否 |
| `timeout` | 覆盖全局超时时间 | 否 |
**DashboardResponse**: `summary``targets`
**HTTP 类型** (`type: http`)
**DashboardResponse.summary**: `total``up``down``lastCheckTime``incidents``window`
| 字段 | 说明 |
| ------------------- | --------------------------------------- |
| `http.url` | 目标 URL |
| `http.method` | HTTP 方法(覆盖 defaults |
| `http.headers` | 请求头(与 defaults.http.headers 合并) |
| `http.body` | 请求体 |
| `http.ignoreSSL` | 忽略 HTTPS 证书校验,默认 `false` |
| `http.maxRedirects` | 最大重定向跟随次数,默认 `0`(不跟随) |
**MetaResponse**: `checkerTypes`(已注册 checker 类型标识符列表)
**Cmd 类型** (`type: cmd`)
**TargetStatus**: `id``name``type`checker 类型,如 http/cmd/db`target`URL、命令摘要或数据库连接摘要`group``interval``latestCheck``stats``currentStreak``recentSamples`
| 字段 | 说明 |
| ---------- | -------------------------------------- |
| `cmd.exec` | 可执行文件名或路径 |
| `cmd.args` | 命令行参数列表 |
| `cmd.env` | 环境变量覆盖(继承进程环境变量并合并) |
| `cmd.cwd` | 工作目录(相对于配置文件所在目录) |
**RecentSample**: `timestamp``durationMs``up`
**DB 类型** (`type: db`)
**CheckResult**: `timestamp``matched``durationMs``statusDetail``failure`
| 字段 | 说明 |
| ---------- | ------------------------------------------------------------- |
| `db.url` | 数据库连接字符串,支持 `postgres://``mysql://``sqlite://` |
| `db.query` | SQL 查询语句(不配置时仅测试连接) |
**CheckFailure**: `kind`error/mismatch`phase``path``message``expected?`(仅 mismatch`actual?`(仅 mismatch
#### expect — 期望校验
**TargetStats**: `totalChecks``upChecks``downChecks``availability`
| 字段 | 适用类型 | 说明 |
| ------------------- | -------- | ---------------------------------------------------------------- |
| `status` | HTTP | 可接受的状态码列表,支持精确码和范围(如 `"2xx"`);默认 `[200]` |
| `exitCode` | Cmd | 可接受的退出码列表;未指定时不校验 |
| `headers` | HTTP | 响应头校验 |
| `maxDurationMs` | 全部 | 最大耗时阈值(毫秒) |
| `body` | HTTP | 响应体校验(数组,可组合使用,见下方) |
| `stdout` / `stderr` | Cmd | 输出校验(数组,每项一个操作符对象) |
| `rowCount` | DB | 查询返回行数校验(操作符对象) |
| `rows` | DB | 查询结果逐行校验(数组,列名→操作符映射) |
**CurrentStreak**: `up``count``capped?`
**body 校验项**(数组中可混合使用):
**TargetMetricsResponse**: `targetId``window``stats``trend`
- `contains` — 响应体包含指定文本
- `regex` — 正则匹配(启动期会拒绝存在 ReDoS 风险的模式)
- `json` — JSONPath 提取值比较(`path` 必填,如 `$.slideshow.title`
- `css` — CSS 选择器提取 HTML 元素(`selector` 必填,`attr` 可选提取属性)
- `xpath` — XPath 提取 XML/HTML 节点(`path` 必填,如 `/html/body/h1/text()`
**TargetMetricsResponse.stats**: `totalChecks``upChecks``downChecks``availability``avgDurationMs``p95DurationMs``p99DurationMs``mttr``longestOutage``incidentCount``currentStreak`
**比较操作符**`equals`(默认)、`contains``match`(正则)、`empty``exists``gte``lte``gt``lt`
**TrendPoint**: `bucketStart``avgDurationMs``minDurationMs``maxDurationMs``availability``totalChecks``upChecks``downChecks`
**大小说明**`maxBodyBytes``maxOutputBytes` 支持 `KB``MB``GB` 单位,也可直接使用数字。
**HistoryResponse**: `items`CheckResult[])、`total``page``pageSize`
**时长格式**`500ms``30s``5m``2h``7d`
### 错误响应
**JSON Schema**:仓库根目录导出 `probe-config.schema.json`,在 YAML 文件顶部添加 `# yaml-language-server: $schema=./probe-config.schema.json` 即可在编辑器中获得提示和校验。
API 错误返回 `ApiErrorResponse` 格式:
```json
{ "error": "描述信息", "status": 400 }
```
| 状态码 | 触发场景 |
| ------ | ------------------------------------------------------------------------------------------ |
| 400 | 参数格式错误(无效 ID、from/to 缺失或格式错误、page/pageSize 非正整数、pageSize 超过 200 |
| 404 | 目标不存在、API 路由未匹配、非 GET 方法请求 API 路由 |
## 运行参数
CLI 只接受一个参数YAML 配置文件路径。
```bash
./dist/dial-server ./probes.yaml
```
> **注意:** 配置校验在启动时执行,非法配置会阻止启动并输出错误信息。除动态键值表(`headers`、`env`)外,未知字段会导致启动失败,请使用 YAML 注释。
## 目标状态判定
单层判定模型,适用于 HTTP 和 Cmd 两种类型
采用单层判定模型:
- **matched**: 是否符合 expect 规则HTTP 未指定 `expect.status` 时默认检查 `[200]`
- **UP** = matched
- **DOWN** = NOT matched
- **UP** = 拨测结果符合 expect 规则
- **DOWN** = 拨测结果不符合 expect 规则
执行失败(网络错误、超时、进程崩溃)和 expect 不匹配都统一为 `matched=false`,通过 `failure.kind` 区分(`"error"` vs `"mismatch"`)。
执行失败(网络错误、超时、进程崩溃)和 expect 不匹配都统一为 DOWN,通过 `failure.kind` 区分原因`"error"` vs `"mismatch"`)。
---
## 开发
> 开发相关文档(项目结构、构建、测试、代码规范等)请参阅 [DEVELOPMENT.md](DEVELOPMENT.md)。
```bash
bun run check # schema:check + typecheck + lint + test
bun run verify # check + build
```
开发相关文档(项目结构、构建、测试、代码规范等)请参阅 [DEVELOPMENT.md](DEVELOPMENT.md)。
## License
MIT

View File

@@ -48,34 +48,8 @@ targets:
- json:
path: "$.slideshow.slides[0].title"
contains: "Wake"
- json:
path: "$.slideshow.slides[0].type"
equals: "all"
- regex: '"title"'
- name: "HTML 页面 — CSS 选择器"
type: http
http:
url: "https://httpbin.org/html"
expect:
body:
- css:
selector: "h1"
contains: "Moby-Dick"
- css:
selector: "body"
exists: true
- name: "HTML 页面 — XPath 提取节点文本"
type: http
http:
url: "https://httpbin.org/html"
expect:
body:
- xpath:
path: "/html/body/h1/text()"
contains: "Melville"
- name: "POST 接口测试"
type: http
http:
@@ -94,61 +68,6 @@ targets:
path: "$.json.version"
gte: 1
- name: "请求头验证"
type: http
http:
url: "https://httpbin.org/headers"
headers:
X-Custom-Header: "dial-server"
expect:
status: [200]
body:
- json:
path: "$.headers.X-Custom-Header"
equals: "dial-server"
- name: "响应头自定义校验"
type: http
http:
url: "https://httpbin.org/response-headers"
headers:
accept: "application/json"
expect:
body:
- json:
path: "$.Content-Type"
equals: "application/json"
- name: "多状态码允许"
type: http
http:
url: "https://httpbin.org/status/200"
expect:
status: [200, 201, 204]
- name: "状态码范围匹配"
type: http
http:
url: "https://httpbin.org/status/204"
expect:
status: ["2xx"]
- name: "自签名证书跳过 SSL"
type: http
http:
url: "https://internal.local/health"
ignoreSSL: true
expect:
status: ["2xx"]
- name: "跟随重定向"
type: http
http:
url: "https://httpbin.org/redirect/1"
maxRedirects: 5
expect:
status: [200]
# ========== Cmd targets ==========
- name: "Bun 版本输出匹配"
@@ -162,55 +81,6 @@ targets:
stdout:
- match: "^\\d+\\.\\d+\\.\\d+"
- name: "自定义文本输出"
type: cmd
cmd:
exec: "bun"
args: ["-e", "console.log('check ok')"]
expect:
stdout:
- equals: "check ok\n"
maxDurationMs: 3000
- name: "脚本执行无 stderr"
type: cmd
cmd:
exec: "bun"
args: ["-e", "process.stdout.write('ok')"]
expect:
exitCode: [0]
stderr:
- empty: true
- name: "日期脚本输出包含年份"
type: cmd
cmd:
exec: "bun"
args: ["-e", "console.log(new Date().getFullYear())"]
expect:
stdout:
- match: "^20\\d{2}\n?$"
- name: "环境变量覆盖"
type: cmd
cmd:
exec: "bun"
args: ["-e", "console.log(process.env.LANG ?? '')"]
env:
LANG: "C"
expect:
stdout:
- contains: "C"
- name: "运行平台非空输出"
type: cmd
cmd:
exec: "bun"
args: ["-e", "console.log(process.platform)"]
expect:
stdout:
- match: ".+"
- name: "多规则 stdout 顺序校验"
type: cmd
interval: "5m"
@@ -243,16 +113,6 @@ targets:
expect:
maxDurationMs: 1000
- name: "SQLite 内存数据库查询行数"
type: db
db:
url: "sqlite://:memory:"
query: "SELECT 1 as cnt"
expect:
maxDurationMs: 1000
rowCount:
gte: 1
- name: "SQLite 内存数据库多列结果校验"
type: db
db: