feat: P0 通知(邮件 + MeoW)与 TLS 证书监控,事件时间线与维护窗口
通知(P0-1): - 邮件:自写 SMTP 客户端,零依赖。465 隐式 TLS / 587·25 STARTTLS、 AUTH LOGIN+PLAIN、RFC2047 中文主题、base64 正文、多收件人; 服务器不支持 STARTTLS 时默认拒绝明文(需显式 allowInsecure) - MeoW:按官方文档实现,昵称寻址、msgType/htmlHeight 走查询参数、 以响应体 status 判定成功(HTTP 恒 200)、429/404/403 可读化、 本地限流器遵守 3s/15min/60h - 告警:连续 N 次失败去抖 + 恢复通知 + 同轮合并 + 去抖状态持久化 (data/notify-state.json,gitignore)+ 静默时段(跨零点) - --test-notify 自检命令、--data-dir / SHIROKO_DATA_DIR 证书(P0-2): - 独立 TLS 握手取 notAfter(不动既有 fetch 探测路径),默认 6 小时一次 - 面板徽标 ≤14 天黄 / ≤7 天红,阈值 14/7/3 各告一次、已过期只报最严重一档 - 单个目标可用 tls: false 关闭 事件与维护(P1-1 的后端部分): - data/events.json 追加式事件流(最近 500 条)+ GET /api/events + 快照内附最近 20 条;redactDetails 时脱敏 - 维护窗口:窗口内不计入可用率、不发告警、显示「维护中」徽标 测试抓到并修复的三个真 bug: 1. SMTP 空闲超时无人处理 → 服务器不响应会把探针循环挂死 2. MeoW 限流器把空历史当 0 → 第一条通知永远被自己拦掉 3. 去抖状态在无事轮次不落盘 → 连续失败计数丢失、永远达不到阈值 验证:新增 verify-notify.cjs 39 项(假 SMTP / 假 MeoW / 三轮集成 / 维护窗口 / 真实证书握手),六套合计 154 项全通过。
This commit is contained in:
@@ -45,6 +45,12 @@ node prober/node/index.js --once --demo
|
||||
|
||||
# 自定义快照输出路径(--demo 会覆盖真实快照,需要保留真实数据时用它写到别处)
|
||||
node prober/node/index.js --once --demo --out=dist/status.json
|
||||
|
||||
# 通知渠道自检:给每个启用的渠道发一条测试消息(配好 SMTP / MeoW 后先跑这个)
|
||||
node prober/node/index.js --test-notify
|
||||
|
||||
# 通知状态与事件时间线的存放目录(默认 ./data,也可用环境变量 SHIROKO_DATA_DIR)
|
||||
node prober/node/index.js --data-dir=/var/lib/shiroko-monitor
|
||||
```
|
||||
|
||||
启动后打开 <http://localhost:8787/> 即可看到面板。无需安装任何依赖。
|
||||
@@ -71,7 +77,7 @@ shiroko-monitor/
|
||||
│ ├── app.js # 数据源解析 / 渲染 / 浏览器直连探测 / 主题切换
|
||||
│ └── favicon.svg # 默认主题图标
|
||||
├── prober/node/ # Node 探针(本地 / CI 用)
|
||||
│ ├── index.js # 巡检循环 + 静态服务器 + /api/* + /api/v2/getMonitors + --once/--demo/--out/--config
|
||||
│ ├── index.js # 巡检循环 + 静态服务器 + /api/* + /api/v2/getMonitors + --once/--demo/--out/--config/--test-notify
|
||||
│ └── lib/
|
||||
│ ├── http-probe.js # HTTP 探测(fetch,redirect: manual)
|
||||
│ ├── tcp-probe.js # TCP 探测(net.connect)
|
||||
@@ -119,6 +125,14 @@ shiroko-monitor/
|
||||
"refreshCooldownSec": 5, // 每个来源的触发冷却(秒)
|
||||
"hideTargets": false, // true = 面板不渲染任何地址(对外发布用)
|
||||
"redactDetails": false, // true = 快照 detail 只保留「正常/异常」
|
||||
"notify": { // 通知:故障/恢复/证书到期(SMTP 密码与 MeoW 昵称请放 local.monitors.json)
|
||||
"enabled": false,
|
||||
"failureThreshold": 2,
|
||||
"quietHours": "",
|
||||
"channels": { "email": { "enabled": false }, "meow": { "enabled": false } }
|
||||
},
|
||||
"tls": { "enabled": true, "checkIntervalSec": 21600, "alertDays": [14, 7, 3] },
|
||||
"maintenance": [], // 维护窗口:[{ from, to, id?, note? }]
|
||||
"uptimeRobot": { // UptimeRobot 只读数据源(Key 建议放 local.monitors.json)
|
||||
"enabled": false,
|
||||
"apiKey": "",
|
||||
@@ -352,6 +366,76 @@ UptimeRobot 的探测来自其多个节点,适合作为**异地视角**与自
|
||||
- **浏览器直连模式**:不记录历史,色条显示灰色、「可用率数据积累中」——需要可用率请使用 Node 探针或边缘 Worker
|
||||
- **`--demo`** 会生成 90 天演示分桶(含绿/橙/红各档),仅用于界面预览;若不想覆盖真实快照,用 `--out=<path>` 写到别处
|
||||
|
||||
## 通知、证书与事件
|
||||
|
||||
### 通知(邮件 + MeoW)
|
||||
|
||||
面板只在你打开时展示状态,**通知**让故障主动找你。支持两个渠道:**邮件**(自写 SMTP 客户端,零依赖)与 **MeoW 推送**(按 [官方文档](https://www.chuckfang.com/MeoW/api_doc.html) 实现)。
|
||||
|
||||
```jsonc
|
||||
"notify": {
|
||||
"enabled": true,
|
||||
"failureThreshold": 2, // 连续失败 N 次才告警(去抖,默认 2;1 = 立即)
|
||||
"recoveryNotify": true, // 恢复时也发一条
|
||||
"quietHours": "23:00-07:00", // 静默时段(本地时间,支持跨零点),期间只记录不发送;留空全天
|
||||
"channels": {
|
||||
"email": {
|
||||
"enabled": true,
|
||||
"host": "smtp.example.com", "port": 465, // 465 隐式 TLS;587/25 自动 STARTTLS
|
||||
"user": "[email protected]", "pass": "密码",
|
||||
"from": "[email protected]", "to": ["[email protected]"],
|
||||
"allowInsecure": false // 服务器不支持 STARTTLS 时是否允许明文(默认拒绝)
|
||||
},
|
||||
"meow": {
|
||||
"enabled": true,
|
||||
"nickname": "你的昵称", // MeoW 以昵称寻址、无需 token,因此昵称等同密钥
|
||||
"baseUrl": "https://api.chuckfang.com",
|
||||
"msgType": "text" // text | html(html 可另配 htmlHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
行为要点:
|
||||
|
||||
- **去抖**:连续失败达到阈值才发「故障」;恢复时发「恢复」;同一轮多个目标变化会合并成一条,不刷屏
|
||||
- **重启不重复**:去抖状态持久化在 `data/notify-state.json`(私有目录,已 gitignore),重启/单轮模式都不重发
|
||||
- **静默时段**:支持 `23:00-07:00` 这类跨零点写法;静默期内照常记录事件,只是不发送
|
||||
- **MeoW 限流**:客户端本地也做限流(3 秒 1 条 / 15 每分钟 / 60 每小时),替你挡掉一部分官方 429;429/404/403 都会转成可读的错误原因
|
||||
- **CI 注意**:MeoW 官方明确不建议用多人共享的出口 IP(GitHub Actions 即属此类),CI 场景建议只开邮件渠道
|
||||
- **自检**:`node prober/node/index.js --test-notify` 给每个启用渠道发一条测试消息,确认配置可用
|
||||
|
||||
> SMTP 密码与 MeoW 昵称属于私密信息,请放在 `local.monitors.json`(已 gitignore),不要写进公开的 `monitors.json`。
|
||||
|
||||
### TLS 证书监控
|
||||
|
||||
对 `https://` 目标,探针额外做一次 TLS 握手读取证书有效期(默认 6 小时一次):
|
||||
|
||||
- 面板在名称旁显示徽标:剩余 **≤14 天**黄色、**≤7 天**红色,悬停可见到期日与签发者
|
||||
- 剩余 ≤14 / 7 / 3 天时按阈值告警;同一张证书每个阈值只发一次,换证书后重新计算;**已过期的证书只报最严重的一档**
|
||||
- 单个目标不需要可设 `"tls": false`
|
||||
|
||||
```jsonc
|
||||
"tls": { "enabled": true, "checkIntervalSec": 21600, "alertDays": [14, 7, 3] }
|
||||
```
|
||||
|
||||
### 维护窗口
|
||||
|
||||
计划重启/迁移期间,不想污染可用率统计、也不想被告警轰炸:
|
||||
|
||||
```jsonc
|
||||
"maintenance": [
|
||||
{ "from": "2026-10-14T02:00", "to": "2026-10-14T04:00", "id": "box", "note": "升级迁移" }
|
||||
]
|
||||
```
|
||||
|
||||
- `id` 可省略(对所有目标生效),也支持写成数组
|
||||
- 窗口内:探测照常进行,但**不计入当日可用率**、**不发告警**,面板显示「维护中」徽标;事件时间线照常记录并带 `maintenance: true`
|
||||
|
||||
### 事件时间线
|
||||
|
||||
每次状态翻转(故障 / 恢复 / 证书阈值)都会追加到 `data/events.json`(私有目录,保留最近 500 条),并通过 `GET /api/events` 读取。快照里也会带上最近 20 条供面板使用;`redactDetails` 开启时,快照内事件只保留类型与时间。
|
||||
|
||||
## 部署方案
|
||||
|
||||
| 方案 | 架构 | 数据更新 | 成本 | 适合场景 |
|
||||
@@ -445,6 +529,19 @@ UptimeRobot 的探测来自其多个节点,适合作为**异地视角**与自
|
||||
- [x] 页脚改为展示「数据更新于 … + 下次检查 Ns」,tooltip 内含服务端更新节奏与快照生成时间
|
||||
- [x] `scripts/verify.cjs` 适配纯外部数据源部署(自动跳过不适用的探测类断言),并新增「倒计时对齐服务端」断言
|
||||
|
||||
### v0.10.0 已实现(P0:通知 + 证书)
|
||||
|
||||
- [x] **通知渠道①:邮件** —— 自写极简 SMTP 客户端(**零依赖**,不引 nodemailer):465 隐式 TLS / 587·25 自动 STARTTLS、AUTH LOGIN 与 PLAIN、UTF-8 主题按 RFC2047 编码 + base64 正文、多收件人、5xx 与连接错误可读;服务器不支持 STARTTLS 时**默认拒绝明文发信**(需显式 `allowInsecure`)
|
||||
- [x] **通知渠道②:MeoW 推送** —— 按[官方文档](https://www.chuckfang.com/MeoW/api_doc.html)实现:昵称寻址无 token、`msgType`/`htmlHeight` 走查询参数、**HTTP 恒 200 改为按响应体 status 判定**、429/404/403 转成可读原因、本地限流器遵守 3s/15min/60h
|
||||
- [x] **告警逻辑**:连续 N 次失败才告警(去抖)+ 恢复通知 + 同轮多点变化合并为一条 + **去抖状态持久化**(重启不重复)+ 静默时段(跨零点,只记录不发送)
|
||||
- [x] **事件时间线**:`data/events.json`(保留最近 500 条)+ `GET /api/events` + 快照内附最近 20 条;`redactDetails` 时脱敏
|
||||
- [x] **TLS 证书监控**:对 https 目标单独握手取有效期(默认 6 小时一次),面板徽标(≤14 天黄 / ≤7 天红),阈值 14/7/3 各告一次、已过期只报最严重一档、换证书自动重置
|
||||
- [x] **维护窗口**:窗口内不计入可用率、不发告警、显示「维护中」徽标,事件仍记录
|
||||
- [x] **`--test-notify` 自检命令** 与 `--data-dir` 私有数据目录(默认 `./data`,已 gitignore)
|
||||
- [x] 新增 `scripts/verify-notify.cjs`:**39 项断言**(假 SMTP 服务器 / 假 MeoW 服务器 / 静默时段 / 限流器 / 三轮集成验证去抖与恢复 / 维护窗口抑制 / 真实证书握手)
|
||||
|
||||
> 测试过程中抓到并修掉两个真 bug:① SMTP 空闲超时没有处理,服务器不响应会把探针循环**挂死**;② MeoW 限流器把空历史当成 0,**第一条通知永远被自己拦掉**;③ 去抖状态在「无事发生」的轮次不落盘,导致连续失败计数丢失、永远达不到告警阈值。
|
||||
|
||||
### v0.9.1 已实现
|
||||
|
||||
- [x] **「任何情况下都不需要横向滚动」定为硬要求并落地**:表格改为**流式列宽**(`table-layout: fixed` + 百分比列宽),表头允许换行、单元格裁剪,心率图与可用率条随宽度伸缩;从结构上不可能撑出横向滚动
|
||||
@@ -530,10 +627,11 @@ node prober/node/index.js --once
|
||||
NODE_PATH=<playwright 的 node_modules> node scripts/verify.cjs # 热重载 / 数据一致性 / 刷新语义 / 面板(21 项)
|
||||
NODE_PATH=<playwright 的 node_modules> node scripts/verify-views.cjs # 视图与数据源过滤(10 项)
|
||||
node scripts/verify-security.cjs # banner 探测 / 脱敏 / 私有分层 / 刷新限流(20 项)
|
||||
node scripts/verify-notify.cjs # 通知与证书(39 项:假 SMTP / 假 MeoW / 去抖 / 恢复 / 维护窗口 / 真实证书)
|
||||
UPTIMEROBOT_API_KEY=ur... node scripts/verify-uptimerobot.cjs # UR 映射/兼容接口/失败路径/冒烟(22 项)
|
||||
```
|
||||
|
||||
`verify-security.cjs` 会临时起一个假 SSH 服务与静默端口,验证 banner 探测的在线/不匹配/超时三种判定、默认不回显版本、脱敏与私有配置分层,以及刷新接口的 429/403 行为。`verify-uptimerobot.cjs` 用合成 payload 做无网映射单测,真实 Key 冒烟为可选(未设置环境变量则跳过)。
|
||||
`verify-security.cjs` 会临时起一个假 SSH 服务与静默端口,验证 banner 探测的在线/不匹配/超时三种判定、默认不回显版本、脱敏与私有配置分层,以及刷新接口的 429/403 行为。`verify-uptimerobot.cjs` 用合成 payload 做无网映射单测,真实 Key 冒烟为可选(未设置环境变量则跳过)。`verify-notify.cjs` 会临时起**假 SMTP 服务器与假 MeoW 服务器**,完整走一遍邮件握手与推送协议,并真跑三轮探针验证去抖、恢复、维护窗口抑制与事件落盘;真实证书握手需要外网,无网络时自动跳过(不计失败)。
|
||||
|
||||
代码约定:探针与面板通过 `status.json` / `/api/status` 的同一份 JSON 契约协作;新增探测类型时只需在 `prober/node/lib/` 与 `edge/cloudflare/worker.js` 各实现一个 `probeXxx(monitor)`,返回 `{ status, latency, code, detail }` 即可被面板直接渲染。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user