# DiAL
轻量级多类型拨测监控工具
基于 Bun + TypeScript 构建 · YAML 配置驱动 · 内置 Dashboard
--- DiAL 是一个自托管的拨测监控工具,支持 **HTTP**、**命令行**、**数据库**、**TCP**、**UDP** 和 **Ping** 多种拨测类型。通过 YAML 配置文件定义拨测目标,后端定时并发执行拨测并将结果持久化到本地 SQLite,前端 Dashboard 展示各目标的实时状态、可用率和耗时趋势。 **功能亮点:** - 多种拨测类型:HTTP(GET/POST/PUT 等)、Cmd(命令行执行)、DB(PostgreSQL/MySQL/SQLite)、TCP(端口可达性 + Banner 探测)、UDP(自定义 payload 请求-响应)、Ping(ICMP 存活、延迟、丢包率) - 丰富的校验规则:状态码、响应头、JSONPath、CSS 选择器、XPath、正则匹配、数值比较等 - 响应式 Dashboard:实时状态、可用率统计、耗时趋势图、手动/自动刷新 - 多主题支持:系统、明亮、黑暗三种主题模式 - 零外部依赖:数据存储使用 SQLite,无需额外数据库服务 ## 快速开始 **前置条件:** [Bun](https://bun.sh/) >= 1.0 Ping checker 依赖系统 `ping` 命令。精简容器镜像需额外安装,例如 Alpine 可安装 `iputils-ping`。 ```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 开发服务器(`http://127.0.0.1:5173`)和 API 服务器(`http://127.0.0.1:3000`),访问前端地址即可使用 Dashboard。 ## 生产部署 ```bash # 构建 bun run build # 运行 ./dist/dial-server ./probes.yaml ``` 构建产物为独立可执行文件,只需一个 YAML 配置文件即可运行。 ## 配置文件 程序通过 YAML 配置文件定义所有运行参数,完整示例参见 [`probes.example.yaml`](probes.example.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" variables: env_name: "生产" base_url: "https://api.example.com" api_token: "Bearer demo-token" sqlite_url: "sqlite:///path/to/db.sqlite" defaults: interval: "30s" timeout: "10s" http: maxBodyBytes: "10MB" cmd: maxOutputBytes: "1MB" targets: - id: "baidu-home" name: "Baidu" type: http http: url: "https://www.baidu.com" expect: status: [200] maxDurationMs: 10000 - id: "json-api" name: "${env_name} JSON API 示例" type: http http: url: "${base_url}/json" headers: Authorization: "${api_token|Bearer fallback-token}" expect: status: [200] headers: Content-Type: contains: "application/json" body: - json: path: "$.slideshow.title" equals: "Sample Slide Show" - id: "bun-script" name: "Bun 脚本检查" type: cmd cmd: exec: "bun" args: ["-e", "console.log('ok')"] expect: exitCode: [0] stdout: - contains: "ok" - id: "sqlite-active-users" name: "SQLite 数据库检查" type: db db: url: "${sqlite_url}" query: "SELECT COUNT(*) as cnt FROM users WHERE status = 'active'" expect: maxDurationMs: 5000 rowCount: { gte: 1 } rows: - cnt: { gte: 0 } - id: "redis-port" name: "Redis 端口可达" type: tcp tcp: host: "127.0.0.1" port: 6379 expect: maxDurationMs: 3000 - id: "udp-heartbeat" name: "UDP 心跳检测" type: udp udp: host: "127.0.0.1" port: 9000 payload: "PING" expect: response: - contains: "PONG" maxDurationMs: 100 - id: "gateway-ping" name: "网关 ICMP 可达" type: ping ping: host: "10.0.0.1" count: 3 packetSize: 56 expect: alive: true maxPacketLoss: 10 maxAvgLatencyMs: 100 maxMaxLatencyMs: 300 maxDurationMs: 5000 ``` ### 配置说明 #### server — 服务配置(均可省略,使用默认值) | 字段 | 说明 | 默认值 | | --------- | ------------------------------------------ | ----------- | | `host` | 监听地址 | `127.0.0.1` | | `port` | 监听端口 | `3000` | | `dataDir` | 数据目录,相对路径基于配置文件所在目录解析 | `./data` | #### runtime — 运行时配置 | 字段 | 说明 | 默认值 | | --------------------- | ------------------------------------------------ | ------ | | `maxConcurrentChecks` | 最大并发拨测数 | `20` | | `retention` | 历史数据保留时长,支持 `ms`/`s`/`m`/`h`/`d` 单位 | `7d` | #### defaults — 全局默认值(均可省略) | 字段 | 说明 | 默认值 | | -------------------- | -------------------------------------------------- | ------- | | `interval` | 拨测间隔 | `30s` | | `timeout` | 超时时间 | `10s` | | `http.maxBodyBytes` | 响应体最大字节数 | `100MB` | | `http.headers` | 默认请求头(target 中的 headers 会合并覆盖同名头) | — | | `cmd.maxOutputBytes` | 输出最大字节数 | `100MB` | | `cmd.cwd` | 默认工作目录(相对于配置文件所在目录) | `.` | #### variables — 配置变量(可省略) `variables` 是顶层动态键值表,key 必须符合 `[a-zA-Z_][a-zA-Z0-9_]*`,value 仅支持 string、number、boolean。target 中的字符串值可引用变量: - `${key}`:引用 variables 或环境变量 - `${key|default}`:变量和环境变量都不存在时使用默认值,第一个 `|` 后的内容为默认值 - `$${key}`:转义输出字面量 `${key}` 解析优先级为 `variables -> process.env -> 默认值`。字段值完整等于单个变量引用时会保留 number/boolean/string 类型;部分拼接时统一转为字符串。变量替换仅作用于 `targets`,且不会替换 `id` 和 `type` 字段。 #### targets — 拨测目标列表(必填) 每个 target 的通用字段: | 字段 | 说明 | 必填 | | ------------- | ------------------------------------------------------------------------------------ | -------------------- | | `id` | 目标唯一标识,最长 30 字符,支持字母数字、下划线、连字符,不参与变量替换 | 是 | | `name` | 展示名称,最长 30 字符,支持变量替换,可省略或显式 null;前端展示时 null 回退到 `id` | 否 | | `description` | 目标描述,最长 500 字符,支持变量替换,可省略或显式 null,允许空字符串 | 否 | | `type` | 目标类型:`http`、`cmd`、`db`、`tcp`、`ping` | 是 | | `group` | 分组名称 | 否,默认 `"default"` | | `interval` | 覆盖全局拨测间隔 | 否 | | `timeout` | 覆盖全局超时时间 | 否 | **HTTP 类型** (`type: http`) | 字段 | 说明 | | ------------------- | --------------------------------------- | | `http.url` | 目标 URL | | `http.method` | HTTP 方法(覆盖 defaults) | | `http.headers` | 请求头(与 defaults.http.headers 合并) | | `http.body` | 请求体 | | `http.ignoreSSL` | 忽略 HTTPS 证书校验,默认 `false` | | `http.maxRedirects` | 最大重定向跟随次数,默认 `0`(不跟随) | **Cmd 类型** (`type: cmd`) | 字段 | 说明 | | ---------- | -------------------------------------- | | `cmd.exec` | 可执行文件名或路径 | | `cmd.args` | 命令行参数列表 | | `cmd.env` | 环境变量覆盖(继承进程环境变量并合并) | | `cmd.cwd` | 工作目录(相对于配置文件所在目录) | **DB 类型** (`type: db`) | 字段 | 说明 | | ---------- | ------------------------------------------------------------- | | `db.url` | 数据库连接字符串,支持 `postgres://`、`mysql://`、`sqlite://` | | `db.query` | SQL 查询语句(不配置时仅测试连接) | **TCP 类型** (`type: tcp`) | 字段 | 说明 | | ----------------------- | ------------------------------------------------------- | | `tcp.host` | 目标主机地址 | | `tcp.port` | 目标端口(1-65535) | | `tcp.readBanner` | 是否读取服务端 banner,默认 `false` | | `tcp.bannerReadTimeout` | banner 读取超时(毫秒),默认 `2000` | | `tcp.maxBannerBytes` | banner 最大字节数,支持 `KB`/`MB`/`GB` 单位,默认 `4KB` | **Ping 类型** (`type: ping`) | 字段 | 说明 | | ----------------- | ----------------------------------- | | `ping.host` | 目标主机地址 | | `ping.count` | ICMP 包数量,默认 `3`,范围 `1-100` | | `ping.packetSize` | ICMP 包大小(bytes),默认 `56` | Ping checker 通过系统 `ping` 命令执行 ICMP 探测,支持 Linux、macOS 和 Windows 输出解析。 #### expect — 期望校验 | 字段 | 适用类型 | 说明 | | ------------------- | -------- | ---------------------------------------------------------------- | | `status` | HTTP | 可接受的状态码列表,支持精确码和范围(如 `"2xx"`);默认 `[200]` | | `exitCode` | Cmd | 可接受的退出码列表;未指定时不校验 | | `headers` | HTTP | 响应头校验 | | `maxDurationMs` | 全部 | 最大耗时阈值(毫秒) | | `body` | HTTP | 响应体校验(数组,可组合使用,见下方) | | `stdout` / `stderr` | Cmd | 输出校验(数组,每项一个操作符对象) | | `rowCount` | DB | 查询返回行数校验(操作符对象) | | `rows` | DB | 查询结果逐行校验(数组,列名→操作符映射) | | `connected` | TCP | 期望连接结果,`true`(默认)可达或 `false` 期望不可达 | | `banner` | TCP | Banner 文本校验(操作符对象,需开启 `tcp.readBanner`) | | `alive` | Ping | 期望主机可达性,默认 `true` | | `maxPacketLoss` | Ping | 最大丢包率百分比,范围 `0-100` | | `maxAvgLatencyMs` | Ping | 最大平均延迟(毫秒) | | `maxMaxLatencyMs` | Ping | 最大单次延迟(毫秒) | **body 校验项**(数组中可混合使用): - `contains` — 响应体包含指定文本 - `regex` — 正则匹配(启动期会拒绝存在 ReDoS 风险的模式) - `json` — JSONPath 提取值比较(`path` 必填,如 `$.slideshow.title`) - `css` — CSS 选择器提取 HTML 元素(`selector` 必填,`attr` 可选提取属性) - `xpath` — XPath 提取 XML/HTML 节点(`path` 必填,如 `/html/body/h1/text()`) **比较操作符**:`equals`(默认)、`contains`、`match`(正则)、`empty`、`exists`、`gte`、`lte`、`gt`、`lt` **大小说明**:`maxBodyBytes` 和 `maxOutputBytes` 支持 `KB`、`MB`、`GB` 单位,也可直接使用数字。 **时长格式**:`500ms`、`30s`、`5m`、`2h`、`7d` **JSON Schema**:仓库根目录导出 `probe-config.schema.json`,在 YAML 文件顶部添加 `# yaml-language-server: $schema=./probe-config.schema.json` 即可在编辑器中获得提示和校验。 > **注意:** 配置校验在启动时执行,非法配置会阻止启动并输出错误信息。除动态键值表(`headers`、`env`、`variables`)外,未知字段会导致启动失败,请使用 YAML 注释。 ## 目标状态判定 采用单层判定模型: - **UP** = 拨测结果符合 expect 规则 - **DOWN** = 拨测结果不符合 expect 规则 执行失败(网络错误、超时、进程崩溃)和 expect 不匹配都统一为 DOWN,通过 `failure.kind` 区分原因(`"error"` vs `"mismatch"`)。 ## 开发 ```bash bun run check # schema:check + typecheck + lint + test bun run verify # check + build ``` 开发相关文档(项目结构、构建、测试、代码规范等)请参阅 [DEVELOPMENT.md](DEVELOPMENT.md)。 ## License MIT