chore: v0.6.0 baseline — 主题美化前存档
已实现:HTTP/TCP/Banner 探测、90 天可用率、表格/卡片视图、 数据源过滤、UptimeRobot 双向兼容、服务端驱动的刷新节奏、 刷新接口滥用防护与信息泄露控制。
This commit is contained in:
@@ -0,0 +1,436 @@
|
||||
# shiroko-monitor
|
||||
|
||||
轻量级服务监控面板。借鉴 Uptime Kuma 的交互思路,代码完全自研,**零运行时依赖**(Node ≥ 18 直接跑,无 npm install),纯静态前端 + 可插拔探针,支持本地 / GitHub Pages / Cloudflare Workers / EdgeOne Pages 等多种低成本部署。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- **HTTP 可用性探测**:状态码(支持自定义期望值,如把 500 判为正常)、响应延迟、超时控制、不自动跟随重定向(保留 3xx 原始码)
|
||||
- **TCP 端口探测**:连通性判定、连接耗时,区分「连接被拒绝」与「超时」
|
||||
- **Banner 探测(SSH / SMTP / FTP / POP3 / IMAP 等)**:连接后**被动读取**服务端主动发送的标识串,支持 `expectBanner` 规则校验(可做版本合规检查);不做认证、不发任何数据,不会触发目标侧的 fail2ban 封禁。默认只输出协议族摘要,不回显版本号
|
||||
- **90 天可用率**:按天聚合每次探测结果(保留 90 天,自动淘汰旧数据),卡片底部以 90 格色条展示每日可用率(≥99% 绿 / ≥95% 橙 / <95% 红 / 无数据灰),悬停显示当日「可用率(在线次数/总次数)」;顶部汇总整体可用率
|
||||
- **两种面板形态**(顶部一键切换,也可用 `settings.view` / `?view=` 指定):
|
||||
- **表格**:一行一服务的高密度列表(状态/名称/目标/延迟/结果/最近检测/心率/可用率),默认视图
|
||||
- **卡片**:一服务一卡,含心率图与可用率条
|
||||
- **数据源过滤**:`settings.dataFilter` 可选 `both`(默认,两边都展示)/ `config`(只看配置文件里的自建目标)/ `uptimerobot`(只看 UptimeRobot 数据);也可用 `?filter=` 临时切换,顶部统计随展示集合同步
|
||||
- **可配置的自动刷新**:`settings.refreshInterval`(秒)决定面板自动刷新节奏;未显式配置时,「只看 UptimeRobot」模式默认 300 秒(UR 自身 5 分钟才更新一次,刷新更快没有意义)
|
||||
- **监控面板**:
|
||||
- 每个服务的在线 / 离线 / 等待检测状态(绿点脉冲动画)
|
||||
- 响应延迟、结果(状态码 / 端口 / 标识串)、最近检测时间(相对时间 + 悬停显示精确时间)
|
||||
- 最近 48 次检测的心率图(绿色 = 在线,红色 = 离线,柱高 = 延迟)
|
||||
- 分组页签筛选,顶部统计摘要(监控数 / 在线 / 离线 / 平均延迟 / 90 天可用率)
|
||||
- 自动刷新倒计时(基于截止时间,不受浏览器后台标签页定时器节流影响)、手动刷新按钮、暗色模式自适应(`prefers-color-scheme`)
|
||||
- **配置即文件**:`public/monitors.json` 单一配置源,改完即生效(本地探针支持热重载)
|
||||
- **刷新节奏由服务端驱动**:探测由服务端按各目标 `interval` 持续进行,快照里带上「数据最后更新时刻 + 服务端更新节奏」;面板的倒计时对齐到**服务端下次更新时间**,不因访客打开页面而重新计时(旧行为下每次进页面都要重等一整个周期,最坏会看到近两倍周期的旧数据)。自动刷新只读取数据,只有手动点「刷新」才触发一次重新检测
|
||||
- **UptimeRobot 双向兼容**:
|
||||
- *接入*:把 UptimeRobot 已有的监控结果当数据源拉进来,与自建探测并列展示(异地视角),状态、延迟、90 天可用率、心率图全部映射自 UR 官方数据;UR 读取失败时保留上次数据,绝不把服务误判为离线
|
||||
- *输出*:提供 `/api/v2/getMonitors`,字段与状态码编码对齐 UptimeRobot v2,既有脚本/看板可直接消费
|
||||
- **滥用防护与信息泄露控制**:刷新接口仅内网/回环可触发、按来源冷却(429 + Retry-After)、可整体关闭;支持 `hideTargets` / `redactDetails` 与 `local.monitors.json` 私有配置分层,公开配置里可以只有服务名 —— 详见 [docs/SECURITY.md](docs/SECURITY.md)
|
||||
- **三种数据源自动解析**:边缘 API(实时)→ 静态快照 → 浏览器直连,也可用 `?source=` 强制指定
|
||||
|
||||
## 快速开始(本地)
|
||||
|
||||
```bash
|
||||
# 常驻模式:周期探测 + 伺服面板(默认端口 8787)
|
||||
node prober/node/index.js
|
||||
# 或 npm start
|
||||
|
||||
# 单轮探测后退出(供 CI 使用,历史心率与 90 天分桶从已有 status.json 恢复)
|
||||
node prober/node/index.js --once
|
||||
# 或 npm run probe
|
||||
|
||||
# 生成演示数据(含 90 天可用率样例,用于静态部署首版占位)
|
||||
node prober/node/index.js --once --demo
|
||||
# 或 npm run demo
|
||||
|
||||
# 自定义快照输出路径(--demo 会覆盖真实快照,需要保留真实数据时用它写到别处)
|
||||
node prober/node/index.js --once --demo --out=dist/status.json
|
||||
```
|
||||
|
||||
启动后打开 <http://localhost:8787/> 即可看到面板。无需安装任何依赖。
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
shiroko-monitor/
|
||||
├── monitors.json ─────────────── 已移至 public/monitors.json(单一配置源,见下)
|
||||
├── package.json # 零依赖,仅封装常用命令
|
||||
├── LICENSE # MIT
|
||||
├── public/ # 纯静态面板(可整体部署到任意静态托管)
|
||||
│ ├── index.html # 面板页面骨架
|
||||
│ ├── monitors.json # ★ 监控目标配置(改这里)
|
||||
│ ├── status.json # 探针输出的状态快照(面板读取)
|
||||
│ └── assets/
|
||||
│ ├── style.css # 浅色主题 + 暗色自适应
|
||||
│ ├── app.js # 数据源解析 / 渲染 / 浏览器直连探测
|
||||
│ └── favicon.svg
|
||||
├── prober/node/ # Node 探针(本地 / CI 用)
|
||||
│ ├── index.js # 巡检循环 + 静态服务器 + /api/* + /api/v2/getMonitors + --once/--demo/--out/--config
|
||||
│ └── lib/
|
||||
│ ├── http-probe.js # HTTP 探测(fetch,redirect: manual)
|
||||
│ ├── tcp-probe.js # TCP 探测(net.connect)
|
||||
│ ├── banner-probe.js # Banner 探测(被动读取标识串,零认证零交互)
|
||||
│ └── uptimerobot.js # UptimeRobot 只读数据源(API v2 映射)
|
||||
├── scripts/ # 开发验证脚本(需 playwright)
|
||||
│ ├── verify.cjs # 热重载 / 数据一致性 / 刷新语义 / 面板渲染
|
||||
│ ├── verify-views.cjs # 视图渲染、切换持久化与数据源过滤
|
||||
│ ├── verify-security.cjs # banner 探测 / 脱敏 / 私有分层 / 刷新限流
|
||||
│ └── verify-uptimerobot.cjs # UR 映射单测 / 兼容接口 / 失败路径 / 真实 Key 冒烟
|
||||
├── local.monitors.json # 私有配置(不入库):真实地址、API Key
|
||||
├── edge/cloudflare/ # Cloudflare Workers 边缘探针
|
||||
│ ├── worker.js # cron 探针 + KV 存储 + /api/* + 静态伺服
|
||||
│ └── wrangler.toml
|
||||
├── .github/workflows/
|
||||
│ ├── monitor.yml # 每 5 分钟跑一轮探针并提交 status.json
|
||||
│ └── pages.yml # 推送 main 时部署 GitHub Pages
|
||||
└── docs/
|
||||
├── DEPLOYMENT.md # 四种部署方案详细步骤
|
||||
├── SECURITY.md # 刷新滥用防护 / SSH 探测边界 / 信息泄露控制
|
||||
└── screenshot-*.png # 各视图实测截图
|
||||
```
|
||||
|
||||
> 配置源为什么放在 `public/` 下:面板用相对路径 `fetch('monitors.json')`,探针(Node/Worker)、静态托管(Pages)读取的都是同一份文件,零拷贝、零漂移。配置文件不含任何密钥,可安全公开。
|
||||
|
||||
## 配置说明(public/monitors.json)
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"settings": {
|
||||
"title": "Shiroko Monitor", // 面板标题
|
||||
"description": "轻量级服务监控面板",
|
||||
"view": "table", // 默认视图:table | cards
|
||||
"dataFilter": "both", // both | config | uptimerobot(详见「面板形态与数据源」)
|
||||
"refreshInterval": 30, // 面板检查数据的最大间隔(秒,最小 5);实际对齐服务端更新节奏
|
||||
"dataSource": "auto", // auto | api | static | browser
|
||||
"allowRefresh": true, // 是否允许触发强制探测(公网部署建议 false;详见 docs/SECURITY.md)
|
||||
"refreshLocalOnly": true, // 仅内网/回环来源可触发强制探测
|
||||
"refreshCooldownSec": 5, // 每个来源的触发冷却(秒)
|
||||
"hideTargets": false, // true = 面板不渲染任何地址(对外发布用)
|
||||
"redactDetails": false, // true = 快照 detail 只保留「正常/异常」
|
||||
"uptimeRobot": { // UptimeRobot 只读数据源(Key 建议放 local.monitors.json)
|
||||
"enabled": false,
|
||||
"apiKey": "",
|
||||
"pollIntervalSec": 300,
|
||||
"group": "UptimeRobot"
|
||||
}
|
||||
},
|
||||
"monitors": [
|
||||
{
|
||||
"id": "example-com", // 唯一 ID,状态数据按此关联
|
||||
"name": "Example 站点", // 显示名称
|
||||
"group": "公共服务", // 分组名(相同名字归为一组)
|
||||
"type": "http", // http | tcp | banner
|
||||
"url": "https://www.example.com",
|
||||
"method": "GET", // 可选,默认 GET
|
||||
"expectStatus": [200], // 判定为「在线」的状态码列表
|
||||
"interval": 60, // 探测间隔(秒);无人打开面板时的后台节奏
|
||||
"timeout": 10, // 超时(秒)
|
||||
"probeOnRefresh": false // 可选:false = 该目标不参与「刷新即重新检测」,只按自己的 interval 探测
|
||||
},
|
||||
{
|
||||
"id": "cloudflare-dns-tcp",
|
||||
"name": "Cloudflare 443 端口",
|
||||
"group": "基础设施",
|
||||
"type": "tcp", // 只测端口连通性
|
||||
"host": "1.1.1.1",
|
||||
"port": 443,
|
||||
"interval": 60,
|
||||
"timeout": 10
|
||||
},
|
||||
{
|
||||
"id": "ssh-gateway",
|
||||
"name": "跳板机 SSH",
|
||||
"group": "基础设施",
|
||||
"type": "banner", // 被动读取标识串(SSH/SMTP/FTP/POP3/IMAP/HTTP…)
|
||||
"host": "10.0.0.5",
|
||||
"port": 22,
|
||||
"expectBanner": "^SSH-2\\.0-", // 可选:正则策略,不匹配即判离线(可做版本合规检查)
|
||||
"bannerFlags": "i", // 可选:正则 flags,默认 i
|
||||
"revealBanner": false, // 默认 false:只输出「SSH 响应正常」,不回显版本号
|
||||
"interval": 300,
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
添加监控目标 = 在 `monitors` 数组里加一项,保存即可(本地探针自动热重载;静态部署等下一次探针写入)。
|
||||
|
||||
`interval` 与 `refreshInterval` 的关系:
|
||||
|
||||
- `interval`(每个目标):**服务端**的探测节奏,与是否有访客无关;也决定 90 天可用率的样本量
|
||||
- `refreshInterval`(面板):面板去服务端取数据的最长间隔。由于倒计时对齐服务端更新节奏,页面打开期间**不会**额外触发探测;服务端数据比我配置的节奏更新更快时,面板会跟着服务端走
|
||||
- 探测只由服务端驱动 → 打开再多标签页也不会增加对目标的压力(旧版本下每个访客都会触发探测轮次)
|
||||
- 需要立刻看一次实时结果时,手动点面板上的「刷新」(服务端限流/关闭策略见 [docs/SECURITY.md](docs/SECURITY.md))
|
||||
|
||||
> ⚠️ **注意目标限流**:探测本身会消耗目标的配额。例如 GitHub 未认证 API 每小时仅 60 次,按 30s 节奏探测会很快耗尽并返回 403,面板会如实记为离线(那不是面板故障,而是服务真的在拒绝请求)。对这类目标请调大 `interval`,并可设目标级 `probeOnRefresh: false` 让它退出强制检测。仓库默认配置里的 `github-api` 就是这样一个示例目标。
|
||||
|
||||
## 数据源模式
|
||||
|
||||
| 模式 | 触发条件 | 数据新鲜度 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `api` | 同源存在 `/api/status`(Node 服务器 / Cloudflare Worker) | 实时 | 首选 |
|
||||
| `static` | 存在 `status.json`(GitHub Pages / EdgeOne 静态部署) | 取决于探针写入频率 | 次选 |
|
||||
| `browser` | 以上都不可用 | 实时(浏览器直连) | 兜底,限制见下 |
|
||||
|
||||
`auto` 模式按上表顺序自动降级。可用 `?source=api|static|browser` 强制指定,便于调试。
|
||||
|
||||
## UptimeRobot 集成
|
||||
|
||||
### 一、把 UptimeRobot 当数据源(推荐)
|
||||
|
||||
UptimeRobot 的探测来自其多个节点,适合作为**异地视角**与自建探测并列。配置(**API Key 请放 `local.monitors.json`,不要写进公开配置**):
|
||||
|
||||
```jsonc
|
||||
// local.monitors.json(已在 .gitignore 中)
|
||||
{
|
||||
"settings": {
|
||||
"uptimeRobot": { "enabled": true, "apiKey": "ur123456-xxxxxxxxxxxxxxxxxxxxxxxx" }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```jsonc
|
||||
// public/monitors.json 的 settings 里保留开关与默认值
|
||||
"uptimeRobot": {
|
||||
"enabled": false, // 公开配置默认关闭;由私有配置开启
|
||||
"apiKey": "", // 留空;也可用环境变量 UPTIMEROBOT_API_KEY(适合 CI/Secrets)
|
||||
"pollIntervalSec": 300, // 拉取间隔,别低于 UR 的检测间隔(免费版 5 分钟)
|
||||
"group": "UptimeRobot", // 镜像目标在面板中的分组名
|
||||
"includeTypes": [] // 可选,按 UR 类型过滤:1=HTTP 2=Keyword 3=Ping 4=Port
|
||||
}
|
||||
```
|
||||
|
||||
映射关系(全部来自 UR 官方 API,非我们的二次探测):
|
||||
|
||||
| 面板字段 | UptimeRobot 来源 |
|
||||
| --- | --- |
|
||||
| 在线/离线/等待 | `status`:2→在线,8/9→离线,0/1→等待检测 |
|
||||
| 响应延迟 | 最新一条 `response_times`(缺失时用 `average_response_time`) |
|
||||
| 心率图 | `response_times`(最多 48 个采样点,随 `response_times_average=30` 聚合) |
|
||||
| 90 天可用率 | `custom_uptime_ratio`(1/7/30/90 天),逐日色条由本探针同步后自行累积 |
|
||||
| 结果/原因 | 故障时取最近一次故障日志的 `reason.code/detail`,如 `403 Forbidden` |
|
||||
| 类型徽标 | `UR·HTTP` / `UR·PORT` / `UR·KEYWORD`(与自建探测区分) |
|
||||
|
||||
行为约定:
|
||||
|
||||
- **只读**:仅调用 `getMonitors`,不做任何写操作,因此 `ur*` 开头的只读 Key 即可
|
||||
- **不参与本地强制探测**:UR 镜像由 UR 自己探测,本探针只拉结果;面板「刷新」不会去跑它们
|
||||
- **失败不误判**:Key 无效、限流、网络异常时只记录错误(页脚显示「UptimeRobot 同步异常」并给出原因),目标状态保持上一次的值
|
||||
- 速率:一次请求取回全部监控,默认 5 分钟一次(12 次/小时),远低于 UR 的接口限制
|
||||
|
||||
### 二、对外提供 UptimeRobot 兼容接口
|
||||
|
||||
`GET|POST /api/v2/getMonitors` 返回与 UptimeRobot v2 同构的响应,便于已有脚本/看板迁移:
|
||||
|
||||
```jsonc
|
||||
{
|
||||
"stat": "ok",
|
||||
"pagination": { "offset": 0, "limit": 50, "total": 14 },
|
||||
"monitors": [{
|
||||
"id": 536986782, // 由字符串 id 派生的稳定数字 id
|
||||
"friendly_name": "Example 站点",
|
||||
"url": "https://www.example.com",
|
||||
"type": 1, // 1=HTTP/Keyword 4=Port(UR 编码)
|
||||
"status": 2, // 2=在线 9=离线 1=未检测
|
||||
"interval": 60,
|
||||
"custom_uptime_ratio": "97.436-97.436-97.436-97.436", // 1-7-30-90 天,三位小数
|
||||
"average_response_time": 1291,
|
||||
"response_times": [{ "datetime": 1791880740, "value": 429 }]
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
该端点不做鉴权(其内容与公开面板一致);如需鉴权请在其前方加代理。`api_key` 参数会被忽略。
|
||||
|
||||
## 面板形态与数据源
|
||||
|
||||
### 视图
|
||||
|
||||
| 视图 | 形态 | 适合 |
|
||||
| --- | --- | --- |
|
||||
| `table` 表格 | 一行一服务,列含状态/名称/目标/延迟/结果/最近检测/心率/可用率 | **默认**,目标多时可一屏扫完 |
|
||||
| `cards` 卡片 | 一服务一卡 + 心率图 + 90 格可用率条 | 需要更大点击区域或看图细节 |
|
||||
|
||||
指定方式(优先级从高到低):URL 参数 `?view=table|cards` → 浏览器上次选择(localStorage)→ `settings.view` → 表格。
|
||||
|
||||
### 数据源过滤
|
||||
|
||||
| 值 | 含义 |
|
||||
| --- | --- |
|
||||
| `both` | 配置文件里的自建目标 + UptimeRobot 镜像目标都展示(默认) |
|
||||
| `config` | 只看配置文件里的自建探测目标 |
|
||||
| `uptimerobot` | 只看 UptimeRobot 的数据 |
|
||||
|
||||
配置在 `settings.dataFilter`;也可用 URL 临时覆盖(适合做固定链接,如挂墙看板):`?filter=uptimerobot`。
|
||||
过滤只影响**展示与快照输出**,不影响探测——配置文件里的目标始终按各自 `interval` 探测。
|
||||
|
||||
顶部统计(监控数/在线/离线/平均延迟/可用率)按当前展示的集合实时汇总,不会出现「列表 8 行、统计 14 个」的不一致。
|
||||
|
||||
### 自动刷新节奏(服务端驱动)
|
||||
|
||||
```jsonc
|
||||
"refreshInterval": 300 // 秒,最小 5;不配则默认 30,「只看 UptimeRobot」时默认 300
|
||||
```
|
||||
|
||||
面板不再从「你打开页面的那一刻」重新计时,而是**对齐服务端的更新节奏**:
|
||||
|
||||
| 环节 | 由谁驱动 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 数据产出 | **服务端** | 自建目标按各自 `interval` 探测;UptimeRobot 按 `pollIntervalSec` 拉取。与是否有访客无关 |
|
||||
| 面板检查时刻 | 对齐服务端 | 快照带 `dataUpdatedAt`(数据最后变更时刻)与 `updateIntervalSec`(服务端更新节奏),面板在「上次更新 + 一个周期」时去取一次 |
|
||||
| `refreshInterval` | 上限 | 面板检查间隔不会超过它;服务端节奏更快时按服务端节奏走 |
|
||||
| 手动「刷新」 | 你 | 唯一会触发**立即重新检测**的动作(服务端可拒绝/限流,见 docs/SECURITY.md)|
|
||||
| 自愈 | 面板 | 若服务端数据超过 1.5 个周期没更新(如探针刚重启或被休眠),面板会自动触发一次重新检测 |
|
||||
|
||||
效果:无论你什么时候打开页面,倒计时都显示「距服务端下一次更新还有多久」——刚更新完打开是 300s,过了 4 分钟打开就只剩 60s。静态快照与浏览器直连模式没有可对齐的服务端节奏,仍按 `refreshInterval` 轮询。
|
||||
|
||||
## 90 天可用率
|
||||
|
||||
探针每完成一次探测,就把它计入「当日分桶」:`monitors.<id>.daily["YYYY-MM-DD"] = { total, up }`,只保留最近 90 天(超出自动淘汰)。面板据此渲染 90 格色条与百分比。
|
||||
|
||||
- **数据积累**:从探针首次运行当天开始累积,跑满 90 天后色条填满;没有数据的日期显示灰色(悬停提示「无数据」)
|
||||
- **判定口径**:可用率 = 当日在线探测次数 / 当日总探测次数。**提高探测频率会提高统计精度**(60s 间隔约 1440 次/天,GitHub Actions 5 分钟粒度约 288 次/天)
|
||||
- **本地时区**:Node 探针按运行机器的本地日期分桶;Cloudflare Worker 无本地时区概念,按 UTC 日期分桶
|
||||
- **跨重启保留**:分桶随 `status.json` 持久化,重启/CI 单轮模式都会先恢复再追加,不会断档
|
||||
- **浏览器直连模式**:不记录历史,色条显示灰色、「可用率数据积累中」——需要可用率请使用 Node 探针或边缘 Worker
|
||||
- **`--demo`** 会生成 90 天演示分桶(含绿/橙/红各档),仅用于界面预览;若不想覆盖真实快照,用 `--out=<path>` 写到别处
|
||||
|
||||
## 部署方案
|
||||
|
||||
| 方案 | 架构 | 数据更新 | 成本 | 适合场景 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 本地 Node | 探针 + 面板同机伺服 | 实时(按 interval) | 免费 | 内网自用、开发调试 |
|
||||
| GitHub Pages + Actions | 纯静态 + Actions 定时提交 status.json | 每 5 分钟(Actions 下限) | 免费 | 公网演示、个人项目 |
|
||||
| Cloudflare Workers | 边缘函数 cron 探针 + KV | 每 1 分钟 | 免费版可用(TCP 需付费版) | 公网生产、多目标 |
|
||||
| EdgeOne Pages | 纯静态 + 数据更新同 Pages 路径 | 每 5 分钟 | 免费额度 | 国内访问加速 |
|
||||
|
||||
四种方案的详细步骤见 [docs/DEPLOYMENT.md](docs/DEPLOYMENT.md)。
|
||||
|
||||
## 浏览器直连模式的限制(重要)
|
||||
|
||||
浏览器没有原始 TCP 能力,且受同源策略约束,`browser` 模式下:
|
||||
|
||||
- HTTP:优先以 `cors` 模式请求,目标响应带 `Access-Control-Allow-Origin` 时可读到真实状态码;否则降级 `no-cors`,**只能判断可达性,读不到状态码**(显示「跨域可达」)
|
||||
- TCP:以 HTTP 连接行为近似——快速拒绝判为端口关闭;连接后无响应直到超时判为端口开放(启发式,无法区分「开放但静默」与「被防火墙丢弃」)
|
||||
- HTTPS 页面无法探测 HTTP 目标(浏览器混合内容拦截,会判为离线)
|
||||
- 结论:**浏览器直连只作兜底**。要准确的状态码与 TCP 探测,请使用 Node 探针或边缘 Worker 提供 `/api/status`
|
||||
|
||||
## 已实现 vs 路线图
|
||||
|
||||
### v0.1.0 已实现
|
||||
|
||||
- [x] HTTP 探测:状态码 / 延迟 / 自定义期望值 / 超时 / 保留 3xx
|
||||
- [x] TCP 探测:连通性 / 连接耗时 / 拒绝与超时区分
|
||||
- [x] Node 探针:常驻巡检、周期调度、配置热重载、历史心率持久化(重启不断档)、原子写入 status.json
|
||||
- [x] Node 探针附带静态服务器与 `/api/status`、`/api/monitors`、`/api/refresh`
|
||||
- [x] `--once`(CI 单轮)与 `--demo`(演示数据)模式
|
||||
- [x] 面板:状态卡片、心率图、分组页签、统计摘要、相对/绝对时间、自动刷新倒计时、暗色模式
|
||||
- [x] 数据源三级自动降级 + `?source=` 强制指定
|
||||
- [x] Cloudflare Worker 边缘探针(cron + KV + 静态伺服)
|
||||
- [x] GitHub Actions:定时探测 + Pages 部署
|
||||
- [x] 四种部署路径文档
|
||||
|
||||
### v0.2.0 已实现
|
||||
|
||||
- [x] **90 天可用率**:按天分桶聚合、90 天滚动保留、卡片 90 格分级色条 + 悬停明细、顶部整体可用率(Node 与 Worker 双端一致实现)
|
||||
- [x] `--out=<path>` 自定义快照输出路径;`--demo` 覆盖真实快照前给出告警
|
||||
- [x] 修复:配置热重载后 `/api/status` 永久冻结在旧数据(闭包捕获了被替换的旧 state 引用,改为持有器对象)
|
||||
- [x] 修复:慢目标(如 10s 超时)会被每秒的调度 tick 重复探测,形成探测风暴(改为探测前同步占位 + inFlight 标记)
|
||||
- [x] 修复:非法/缺失 `interval` 会算出 `NaN` 导致该目标永久不再被调度(改为回落默认 60s)
|
||||
- [x] 修复:刷新倒计时改按截止时间计算,不再受浏览器后台标签页定时器节流影响
|
||||
- [x] `scripts/verify.cjs` 端到端回归验证(15 项断言,覆盖上述修复)
|
||||
|
||||
### v0.2.1 已实现
|
||||
|
||||
- [x] **刷新即重新检测**:`/api/refresh` 等待本轮探测完成后返回最新快照,面板自动刷新与手动「刷新」按钮都会真正重新探测目标(此前只是重读旧快照,导致「部分卡片停留在几十秒前、手动刷新也没变化」)
|
||||
- [x] 服务端强制轮次去重:多标签页/多客户端同时刷新共享同一轮探测,2s 内的重复请求直接复用快照(避免对目标重复施压);强制轮次先等在途探测收尾,保证不跳过任何目标
|
||||
- [x] `settings.probeOnRefresh`(默认 `true`):关闭后自动刷新退回只读轮询,手动刷新仍然强制检测
|
||||
- [x] 目标级 `probeOnRefresh: false`:单个目标可退出「刷新即检测」(适合有限流的 API),只按自身 `interval` 探测;默认配置新增示例目标说明该用法
|
||||
- [x] Cloudflare Worker 的 `/api/refresh` 同步改为「等待完成再返回」+ 同隔离体去重
|
||||
|
||||
### v0.3.0 已实现
|
||||
|
||||
- [x] **Banner 探测**(`type: banner`):被动读取 SSH/SMTP/FTP/POP3/IMAP 等标识串,`expectBanner` 正则策略校验(可做版本合规)、`revealBanner` 显式开启才回显;不做认证、不发数据,不触发目标封禁
|
||||
- [x] **四种面板形态**:卡片 / 表格 / 大屏(深色 NOC)/ 极简(对外状态页),顶部一键切换 + `?view=` + `settings.view` + localStorage 记忆
|
||||
- [x] **刷新接口滥用防护**:内网/回环来源限制(不信任 X-Forwarded-For)、每来源冷却 429 + Retry-After、轮次去重、总开关 `allowRefresh`;Worker 需 `REFRESH_TOKEN` 才开放
|
||||
- [x] **信息泄露控制**:`local.monitors.json` 私有配置分层(公开配置可只留 id/name/group)、`hideTargets`、`redactDetails`、TCP 详情不再回显地址、banner 默认不回显版本
|
||||
- [x] 私有配置分层 + `--config` / `--local-config`(多实例与测试用)
|
||||
- [x] 修复:客户端中断请求(切页/关标签)后向已断开 socket 写响应会抛出未捕获异常,可能终止探针进程
|
||||
- [x] 修复:非卡片视图的状态点沿用胶囊色(着色规则作用域写死在 `.card` 上)
|
||||
- [x] 修复:首次打开面板时需等强制检测完成才出数据,期间显示空白「等待检测」——改为先用已有快照立即渲染
|
||||
- [x] 新增 `docs/SECURITY.md`(滥用防护 / SSH 探测边界 / 信息泄露控制)与 `scripts/verify-security.cjs`、`scripts/verify-views.cjs`
|
||||
|
||||
### v0.4.0 已实现
|
||||
|
||||
- [x] **UptimeRobot 数据源(只读)**:把 UR 已有监控作为「异地视角」接入面板,状态/延迟/心率/90 天可用率/故障原因全部映射自 UR 官方字段;镜像目标不参与本地强制探测;同步失败只记录错误并保留上次数据
|
||||
- [x] **UptimeRobot 兼容接口**:`/api/v2/getMonitors` 输出与 UR v2 同构(含 `custom_uptime_ratio`、`response_times`、数字 id 与状态码编码)
|
||||
- [x] `stats.memory`:探针自报内存占用(RSS/heapUsed),方便观察「监控器自身」的开销
|
||||
- [x] 配置分层增强:`--config` / `--local-config` 指定配置文件路径;UR API Key 支持私有配置或 `UPTIMEROBOT_API_KEY` 环境变量
|
||||
- [x] 修复:非 http/tcp 类型的目标行渲染为 `undefined:undefined`(改为有 url 用 url、有 host 用 host:port)
|
||||
- [x] 修复:热重载会丢弃 UptimeRobot 镜像目标已累积的逐日分桶
|
||||
- [x] 修复(实测发现):`custom_down_durations` 不能作为请求参数传给 UR v2(返回 `invalid_parameter`),改为只从响应中读取
|
||||
- [x] 新增 `scripts/verify-uptimerobot.cjs`(22 项:映射单测 / 兼容接口 / 失败路径 / 真实 Key 冒烟)
|
||||
|
||||
### v0.5.0 已实现
|
||||
|
||||
- [x] **表格设为默认视图**,移除大屏与极简两种形态(面板只保留表格/卡片,切换器更精简)
|
||||
- [x] **数据源过滤** `settings.dataFilter`:`both` / `config`(仅配置文件)/ `uptimerobot`(仅 UR),支持 `?filter=` 临时覆盖;服务端快照与面板共用同一口径(私有配置覆盖后也不会错位)
|
||||
- [x] **顶部统计按展示集合汇总**:切换过滤后不再出现「列表 8 行、统计 14 个」
|
||||
- [x] **自动刷新间隔可配置**并按数据源自适应:`refreshInterval`(秒);只看 UptimeRobot 时默认 300 秒(UR 自身 5 分钟才更新)
|
||||
- [x] `/api/refresh?scope=external`:只看 UR 的刷新只同步 UR 数据,不跑本地探测轮次;普通刷新也会顺带同步 UR(最小间隔 60 秒,尊重 UR 接口限流)
|
||||
|
||||
### v0.6.0 已实现
|
||||
|
||||
- [x] **刷新节奏改为服务端驱动**:快照新增 `dataUpdatedAt`(数据最后变更时刻)与 `updateIntervalSec`(服务端更新节奏),面板倒计时对齐到服务端下次更新,**不再每次打开页面从整周期重新计时**(旧行为最坏会看到近两倍周期的旧数据;实测:两次打开间隔 25s,倒计时差值 28s)
|
||||
- [x] 自动刷新改为**只读**:探测完全由服务端按 `interval` / `pollIntervalSec` 驱动,访客数量不再影响对目标的压力;只有手动点「刷新」才触发立即重新检测
|
||||
- [x] 新增自愈:服务端数据超过 1.5 个周期未更新时(探针刚重启、机器休眠过)面板自动触发一次重新检测
|
||||
- [x] 移除已无意义的全局 `probeOnRefresh` 开关(目标级 `probeOnRefresh: false` 保留:该目标不参与强制检测);`refreshInterval` 语义澄清为「面板检查数据的最大间隔」
|
||||
- [x] 页脚改为展示「数据更新于 … + 下次检查 Ns」,tooltip 内含服务端更新节奏与快照生成时间
|
||||
- [x] `scripts/verify.cjs` 适配纯外部数据源部署(自动跳过不适用的探测类断言),并新增「倒计时对齐服务端」断言
|
||||
|
||||
### 路线图(后续迭代)
|
||||
|
||||
- [ ] 通知渠道:Webhook / Server 酱 / Telegram / Bark / 邮件
|
||||
- [ ] 故障事件记录:连续失败次数、事件时间线、状态变更历史
|
||||
- [ ] 状态徽章:SVG badge(shields.io 风格)
|
||||
- [ ] 更多探测类型:证书到期天数、响应体关键字、DNS 解析、ICMP(边缘端)、HTTP 重定向链
|
||||
- [ ] 主动协议探测(Redis `PING`、MySQL 握手等):需先设计「发送探测字节」的安全边界与白名单,避免退化成通用交互工具
|
||||
- [ ] 只读分享令牌:给 `hideTargets` 之外的场景做「带令牌才能看到真实名称」的分级
|
||||
- [ ] 多区域探针:多 Worker 部署 + 结果合并,取容错视图
|
||||
- [ ] 配置 Web UI(可选加密编辑),替代手改 JSON
|
||||
- [ ] 面板:更多形态(如时间线视图、地图视图)、明暗主题手动切换、可用率区间可配置(7/30/90 天)
|
||||
- [ ] 状态页 JSON API 的 CORS 开放与缓存策略细化
|
||||
|
||||
## 开发
|
||||
|
||||
```bash
|
||||
# 本地起服务(含探针)
|
||||
node prober/node/index.js
|
||||
|
||||
# 只验证探针逻辑
|
||||
node prober/node/index.js --once
|
||||
|
||||
# UI 自动化实测(需 playwright,仅开发验证用,驱动系统 Chrome)
|
||||
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 项)
|
||||
UPTIMEROBOT_API_KEY=ur... node scripts/verify-uptimerobot.cjs # UR 映射/兼容接口/失败路径/冒烟(22 项)
|
||||
```
|
||||
|
||||
`verify-security.cjs` 会临时起一个假 SSH 服务与静默端口,验证 banner 探测的在线/不匹配/超时三种判定、默认不回显版本、脱敏与私有配置分层,以及刷新接口的 429/403 行为。`verify-uptimerobot.cjs` 用合成 payload 做无网映射单测,真实 Key 冒烟为可选(未设置环境变量则跳过)。
|
||||
|
||||
代码约定:探针与面板通过 `status.json` / `/api/status` 的同一份 JSON 契约协作;新增探测类型时只需在 `prober/node/lib/` 与 `edge/cloudflare/worker.js` 各实现一个 `probeXxx(monitor)`,返回 `{ status, latency, code, detail }` 即可被面板直接渲染。
|
||||
|
||||
## 界面截图
|
||||
|
||||
| 表格(默认视图) | 卡片视图 |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
UptimeRobot 数据源(`?filter=uptimerobot`,截图中含真实监控名与域名,公开发布前可自行删除):`docs/screenshot-uptimerobot.png`
|
||||
|
||||
演示数据(90 天满档)与移动端:`docs/screenshot-demo.png`、`docs/screenshot-mobile.png`
|
||||
|
||||
## 致谢与定位
|
||||
|
||||
交互设计参考 Uptime Kuma(心跳图、90 天可用率条、分组卡片、汇总统计),实现为自研轻量版:不追求 Kuma 的通知/多用户/数据库体系,只保留「配置文件 → 探针 → 状态面板」的最小闭环,并对静态/边缘部署友好。
|
||||
Reference in New Issue
Block a user