# DiAL 基于 Bun + TypeScript 的多类型拨测监控工具。通过 YAML 配置文件定义 HTTP 和命令行拨测目标,后端按配置定时并发拨测,结果持久化到本地 SQLite,前端 Dashboard 展示各目标实时状态、可用率、耗时趋势等。 ## 项目结构 ```text src/ server/ app.ts Bun HTTP 路由(API + 静态资源 + SPA fallback) config.ts CLI 参数解析 dev.ts 开发期启动入口 server.ts HTTP server 启动 checker/ types.ts 类型定义 config-loader.ts YAML 配置解析与校验 store.ts SQLite 数据存储 fetcher.ts HTTP 拨测执行 command-runner.ts 命令行拨测执行 size.ts 大小单位解析 engine.ts 调度引擎(按 interval 分组、组内并发) expect/ http.ts HTTP 响应断言 command.ts 命令行输出断言 body.ts HTTP body 断言(JSONPath/XPath/CSS) failure.ts 失败信息类型 shared/ api.ts 前后端共享 TypeScript 类型 web/ Vite + React 前端 Dashboard components/ UI 组件(表格、分组、Drawer、状态条等) constants/ 常量定义(列配置、类型映射、排序/筛选/颜色阈值函数) hooks/ TanStack Query 数据层(useTargetDetail 集成轮询/条件查询) utils/ 前端工具函数 scripts/ 开发、构建和 smoke test 脚本 tests/ Bun test 测试 openspec/ OpenSpec 变更与规格文档 ``` ## 快速开始 ```bash bun install cp probes.example.yaml probes.yaml bun run dev probes.yaml ``` `bun run dev` 会同时启动 Bun 后端和 Vite 前端。开发期请打开 Vite 前端地址 `http://127.0.0.1:5173`。 也可以分别运行: ```bash bun run dev:server probes.yaml bun run dev:web ``` ## 配置文件 程序通过 YAML 配置文件定义所有运行参数: ```yaml server: host: "127.0.0.1" port: 3000 dataDir: "/tmp/probes_data" runtime: maxConcurrentChecks: 20 defaults: interval: "5s" timeout: "10s" http: method: GET maxBodyBytes: "100MB" command: maxOutputBytes: "100MB" 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: "Nginx 进程检查" type: command command: exec: "pgrep" args: ["nginx"] expect: exitCode: [0] stdout: - match: "\\d+" ``` ### 配置说明 - **server**: 服务配置(均可省略,使用默认值) - `host`: 监听地址,默认 `127.0.0.1` - `port`: 监听端口,默认 `3000` - `dataDir`: 数据目录,默认 `./data` - **runtime**: 运行时配置 - `maxConcurrentChecks`: 最大并发拨测数,默认 `20` - **defaults**: 全局默认值(均可省略) - `interval`: 拨测间隔,默认 `30s` - `timeout`: 超时时间,默认 `10s` - `http`: HTTP 类型默认值 - `method`: HTTP 方法,默认 `GET` - `maxBodyBytes`: 响应体最大字节数,默认 `100MB` - `command`: Command 类型默认值 - `maxOutputBytes`: 输出最大字节数,默认 `100MB` - **targets**: 拨测目标列表(必填) - `name`: 目标名称(必填,唯一) - `type`: 目标类型,`http` 或 `command`(必填) - `group`: 分组名称(可选,默认 `"default"`) - `http`: HTTP 拨测配置(type 为 http 时必填) - `url`: 目标 URL - `method`、`headers`、`body`: 请求参数 - `command`: 命令行拨测配置(type 为 command 时必填) - `exec`: 可执行文件名或路径 - `args`: 命令行参数列表 - `env`: 环境变量覆盖(可选,继承进程环境变量并合并覆盖) - `cwd`: 工作目录(可选,相对于配置文件所在目录解析,默认 `.`) - `interval`、`timeout`: 覆盖全局默认值 - `expect`: 期望校验 - `status`: 可接受的状态码列表(HTTP) - `exitCode`: 可接受的退出码列表(Command) - `headers`: 响应头校验(HTTP,支持 `equals`、`contains` 等操作符) - `maxDurationMs`: 最大耗时阈值(毫秒) - `body`: HTTP 响应体校验(数组,可组合使用) - `contains`: 响应体包含的文本 - `match`: 响应体匹配的正则表达式 - `json`: JSONPath 提取值比较(`path` + 比较操作符) - `css`: CSS 选择器提取 HTML 元素比较 - `xpath`: XPath 提取 XML/HTML 节点比较 - `stdout` / `stderr`: Command 输出校验(数组,同 body 格式) - 比较操作符:`equals`(默认)、`contains`、`match`(正则)、`empty`、`exists`、`gte`、`lte`、`gt`、`lt` 大小说明:`maxBodyBytes` 和 `maxOutputBytes` 支持单位 `KB`、`MB`、`GB`,也可直接使用数字(字节数)。 时长格式支持:`30s`、`5m`、`500ms` ## API 端点 | 端点 | 说明 | | ----------------------------------------------------------------- | --------------------------------------- | | `GET /health` | 健康检查 | | `GET /api/summary` | 总览统计(total/up/down/lastCheckTime) | | `GET /api/targets` | 目标列表及最新状态、分组和采样数据 | | `GET /api/targets/:id/history?from=ISO&to=ISO&page=1&pageSize=20` | 指定目标的拨测记录(时间范围 + 分页) | | `GET /api/targets/:id/trend?from=ISO&to=ISO` | 指定目标的按小时聚合趋势 | ### 响应字段 **SummaryResponse**: `total`、`up`、`down`、`lastCheckTime` **TargetStatus**: `id`、`name`、`type`(http/command)、`target`(URL 或命令摘要)、`group`、`interval`、`latestCheck`、`stats`、`recentSamples` **RecentSample**: `timestamp`、`durationMs`、`up` **CheckResult**: `timestamp`、`matched`、`durationMs`、`statusDetail`、`failure` **CheckFailure**: `kind`(error/mismatch)、`phase`、`path`、`expected`、`actual`、`message` **TargetStats**: `totalChecks`、`availability` **TrendPoint**: `hour`、`avgDurationMs`、`availability`、`totalChecks` **HistoryResponse**: `items`(CheckResult[])、`total`、`page`、`pageSize` ### 错误响应 API 错误返回 `ApiErrorResponse` 格式: ```json { "error": "描述信息", "status": 400 } ``` | 状态码 | 触发场景 | | ------ | ----------------------------------------------------------------------- | | 400 | 参数格式错误(无效 ID、from/to 缺失或格式错误、page/pageSize 非正整数) | | 404 | 目标不存在、API 路由未匹配 | | 405 | 非 GET 方法请求 API 路由 | ## 代码质量 ```bash bun run lint bun run format:check bun run format bun run check ``` - `check` 依次运行 `typecheck`、`lint`、`format:check` 和单元测试。 ## 构建 executable ```bash bun run build ``` 构建流程: 1. 运行 `vite build`,输出前端资源到 `dist/web` 2. 生成临时 `.build/static-assets.ts`,嵌入 Vite 产物 3. 生成临时 `.build/server-entry.ts`,作为生产入口 4. 运行 `Bun.build({ compile })`,输出 `dist/dial-server` 运行 executable: ```bash ./dist/dial-server probes.yaml ``` ## 运行参数 CLI 只接受一个参数:YAML 配置文件路径。 ```bash ./dist/dial-server ./probes.yaml ``` ## 测试 ```bash bun run check bun run verify ``` - `check` 适合日常开发,包含类型检查、lint、格式检查和单元测试。 - `verify` 先运行 `check`,再重新构建生产 executable 并运行 smoke test。 ## 前后端边界 前端只通过 HTTP 调用后端,API 路径为 `/api/*`。共享类型放在 `src/shared`,前端不得 import `src/server` 的运行时实现。 ## 目标状态判定 单层判定模型,适用于 HTTP 和 Command 两种类型: - **matched**: 是否符合 expect 规则(无 expect 时默认为 true) - **UP** = matched - **DOWN** = NOT matched 执行失败(网络错误、超时、进程崩溃)和 expect 不匹配都统一为 `matched=false`,通过 `failure.kind` 区分(`"error"` vs `"mismatch"`)。 ## 已知限制 当前不做告警通知、数据自动清理、拨测目标动态增删、认证鉴权和分布式部署。Command 类型拨测不支持 Windows 环境。