# DiAL 基于 Bun + TypeScript 的多类型拨测监控工具。通过 YAML 配置文件定义 HTTP 和命令行拨测目标,后端按配置定时并发拨测,结果持久化到本地 SQLite,前端 Dashboard 展示各目标实时状态、可用率、耗时趋势等,并支持手动、10 秒、30 秒、1 分钟、5 分钟刷新频率切换。 ## 快速开始 ```bash bun install cp probes.example.yaml probes.yaml bun run dev probes.yaml ``` `bun run dev` 启动单进程 fullstack 开发服务器(后端 API + 前端 SPA + HMR),访问 `http://127.0.0.1:3000`。 ## 开发验证 ```bash bun run check # schema:check + typecheck + lint + bun test bun run verify # check + build ``` `verify` 会基于当前源码重新构建生产 executable。原 smoke test 已移除,executable/E2E 验证后续单独补充。 ## 配置文件 程序通过 YAML 配置文件定义所有运行参数: ```yaml # yaml-language-server: $schema=./probe-config.schema.json server: host: "127.0.0.1" port: 3000 dataDir: "/tmp/probes_data" runtime: maxConcurrentChecks: 20 retention: "7d" defaults: interval: "5s" timeout: "10s" http: method: GET maxBodyBytes: "10MB" cmd: maxOutputBytes: "1MB" targets: - name: "Baidu" type: http http: url: "https://www.baidu.com" expect: status: [200] maxDurationMs: 10000 - name: "JSON API 示例" type: http http: url: "https://httpbin.org/json" expect: status: [200] headers: 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: exec: "bun" args: ["-e", "console.log('ok')"] expect: exitCode: [0] stdout: - contains: "ok" ``` ### 配置说明 - **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`(必填) - `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`: 工作目录(可选,相对于配置文件所在目录解析,默认 `.`) - `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 输出校验(数组,每项为一个操作符对象) - 比较操作符:`equals`(默认)、`contains`、`match`(正则,启动期会拒绝存在 ReDoS 风险的模式)、`empty`、`exists`、`gte`、`lte`、`gt`、`lt` 大小说明:`maxBodyBytes` 和 `maxOutputBytes` 支持单位 `KB`、`MB`、`GB`,也可直接使用数字(非负安全整数字节数)。 配置校验:系统启动时会先用 TypeBox 生成的 JSON Schema 契约校验字段类型、必填字段、枚举、数组/对象形状和未知字段,再执行语义 validator 校验 target name 唯一性、URL、正则、JSONPath、XPath、size/duration 解析等规则。非法配置会阻止启动并输出中文错误信息。 未知字段:除 `http.headers`、`defaults.http.headers`、`expect.headers`、`cmd.env` 等动态键值表外,未知字段会导致启动失败。配置备注请使用 YAML 注释,不要添加 `note`、`comment` 等未声明字段。 JSON Schema:仓库根目录导出 `probe-config.schema.json`,可在 YAML 文件顶部添加 `# yaml-language-server: $schema=./probe-config.schema.json` 获取编辑器提示和静态校验。该 schema 由运行期契约 fragments 生成,提交前可用 `bun run schema:check` 检查同步。 时长格式支持:`500ms`、`30s`、`5m`、`2h`、`7d` ## API 端点 | 端点 | 说明 | | ----------------------------------------------------------------- | ------------------------------------------------------------ | | `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`) | ### 响应字段 **DashboardResponse**: `summary`、`targets` **DashboardResponse.summary**: `total`、`up`、`down`、`lastCheckTime`、`incidents`、`window` **MetaResponse**: `checkerTypes`(已注册 checker 类型标识符列表) **TargetStatus**: `id`、`name`、`type`(checker 类型,如 http/cmd)、`target`(URL 或命令摘要)、`group`、`interval`、`latestCheck`、`stats`、`currentStreak`、`recentSamples` **RecentSample**: `timestamp`、`durationMs`、`up` **CheckResult**: `timestamp`、`matched`、`durationMs`、`statusDetail`、`failure` **CheckFailure**: `kind`(error/mismatch)、`phase`、`path`、`message`、`expected?`(仅 mismatch)、`actual?`(仅 mismatch) **TargetStats**: `totalChecks`、`upChecks`、`downChecks`、`availability` **CurrentStreak**: `up`、`count`、`capped?` **TargetMetricsResponse**: `targetId`、`window`、`stats`、`trend` **TargetMetricsResponse.stats**: `totalChecks`、`upChecks`、`downChecks`、`availability`、`avgDurationMs`、`p95DurationMs`、`p99DurationMs`、`mttr`、`longestOutage`、`incidentCount`、`currentStreak` **TrendPoint**: `bucketStart`、`avgDurationMs`、`minDurationMs`、`maxDurationMs`、`availability`、`totalChecks`、`upChecks`、`downChecks` **HistoryResponse**: `items`(CheckResult[])、`total`、`page`、`pageSize` ### 错误响应 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 ``` ## 目标状态判定 单层判定模型,适用于 HTTP 和 Cmd 两种类型: - **matched**: 是否符合 expect 规则(HTTP 未指定 `expect.status` 时默认检查 `[200]`) - **UP** = matched - **DOWN** = NOT matched 执行失败(网络错误、超时、进程崩溃)和 expect 不匹配都统一为 `matched=false`,通过 `failure.kind` 区分(`"error"` vs `"mismatch"`)。 --- > 开发相关文档(项目结构、构建、测试、代码规范等)请参阅 [DEVELOPMENT.md](DEVELOPMENT.md)。