Files
shiroko-monitor/README.md
T
tamakyi d88ba56156 feat(theme): 砂狼白子主题 —— 真实立绘/头像 + 自绘装饰
素材(第三方,仅个人自用):
- 立绘/头像来自 SchaleDB(schaledb.com),学生 Id 10010 = 砂狼白子
- 版权归 NEXON Games / Yostar(蔚蓝档案),来源与公开部署提示见 README「主题」章节

自绘装饰(SVG/CSS,按公开设定还原):
- 天蓝围巾飘带(顶栏底边)、公路车(左下角 + 加载车轮)
- 光环水印:倾斜椭圆环 + 四方位带圆头短杆(对齐官方造型,此前误画为正圆+三角)
- 倒十字发饰分组标识、迷你光环状态点

其他:主题切换(settings.theme / ?theme= / 顶栏按钮)、立绘位 settings.artwork
修复:dataFilter=uptimerobot 时平均延迟恒为「–」(改为纯外部来源时以其为唯一口径)
2026-10-13 19:29:07 +08:00

36 KiB
Raw Blame History

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
  • 主题:砂狼白子(默认启用,可一键切回默认主题):她的官方立绘与头像、青色光环(含"外圈先亮、内圈后亮"的呼吸动画)、天蓝围巾飘带顶栏、倒十字发饰、公路车轮加载态。素材来源与版权说明见 主题
  • 三种数据源自动解析:边缘 API(实时)→ 静态快照 → 浏览器直连,也可用 ?source= 强制指定

快速开始(本地)

# 常驻模式:周期探测 + 伺服面板(默认端口 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             # 基础样式(浅色 + 暗色自适应)
│       ├── theme-shiroko.css     # 砂狼白子主题(仅在 body[data-theme=shiroko] 下生效)
│       ├── shiroko-portrait.webp # 立绘(SchaleDB 素材,见「主题」章节的版权说明)
│       ├── shiroko-icon.webp     # 方形头像(同上)
│       ├── favicon-shiroko.png   # 白子主题图标(由头像生成)
│       ├── deco-scarf.svg        # 自绘:天蓝围巾飘带
│       ├── deco-bicycle.svg      # 自绘:公路车
│       ├── 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)

{
  "settings": {
    "title": "Shiroko Monitor",     // 面板标题
    "description": "轻量级服务监控面板",
    "view": "table",                // 默认视图:table | cards
    "theme": "shiroko",             // shiroko(砂狼白子)| default
    "artwork": "",                  // 可选:立绘图片路径(相对 public/),如 assets/character.png
    "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)

⚠️ 注意目标限流:探测本身会消耗目标的配额。例如 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,不要写进公开配置):

// local.monitors.json(已在 .gitignore 中)
{
  "settings": {
    "uptimeRobot": { "enabled": true, "apiKey": "ur123456-xxxxxxxxxxxxxxxxxxxxxxxx" }
  }
}
// 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 同构的响应,便于已有脚本/看板迁移:

{
  "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 个」的不一致。

自动刷新节奏(服务端驱动)

"refreshInterval": 300   // 秒,最小 5;不配则默认 30,「只看 UptimeRobot」时默认 300

面板不再从「你打开页面的那一刻」重新计时,而是对齐服务端的更新节奏:

环节 由谁驱动 说明
数据产出 服务端 自建目标按各自 interval 探测;UptimeRobot 按 pollIntervalSec 拉取。与是否有访客无关
面板检查时刻 对齐服务端 快照带 dataUpdatedAt(数据最后变更时刻)与 updateIntervalSec(服务端更新节奏),面板在「上次更新 + 一个周期」时去取一次
refreshInterval 上限 面板检查间隔不会超过它;服务端节奏更快时按服务端节奏走
手动「刷新」 你 唯一会触发立即重新检测的动作(服务端可拒绝/限流,见 docs/SECURITY.md)
自愈 面板 若服务端数据超过 1.5 个周期没更新(如探针刚重启或被休眠),面板会自动触发一次重新检测

效果:无论你什么时候打开页面,倒计时都显示「距服务端下一次更新还有多久」——刚更新完打开是 300s,过了 4 分钟打开就只剩 60s。静态快照与浏览器直连模式没有可对齐的服务端节奏,仍按 refreshInterval 轮询。

主题(砂狼白子)

面板名称叫 shiroko-monitor,主题自然取了《蔚蓝档案》的砂狼白子:她的光环、围巾、狼耳与配色都被做进了界面。

主题里的元素

位置 元素 取自设定
左上 logo / 浏览器图标 她的头像(真实素材)+ 青色光环环(呼吸式外扩动画) 头像为白子的官方方形头像素材
右下角 她的官方立绘(透明底,约 56vh,顶部渐隐;宽屏下表格会自动让位,不遮挡数据) 白子原版(阿拜多斯校服)立绘
顶栏底边 天蓝色围巾飘带:波浪形状 + 5 处褶皱纹路(自绘) 天蓝色围巾是星野送给她的信物
背景 天蓝渐变 + 斜向细排线 + 右下光环水印(自绘:倾斜椭圆环 + 四方位带圆头的短杆) 白子的光环是倾斜的椭圆环,四个方位各有一条带圆头的短杆,整体形似全息瞄准镜准星
状态点 迷你光环(外圈 + 中心点,在线时缓慢脉冲外扩) 光环是基沃托斯学生的标志
分组标题 青色倒十字发饰(旋转 45°,自绘) 她左侧刘海上别的青色十字发夹
刷新按钮加载态 公路车轮(轮圈 + 辐条 + 花鼓)旋转 她最爱的公路车(Bianchi ARIA 原型)
左下角 公路车侧影(自绘) 同上
配色 天蓝 / 青 + 藏蓝 + 灰白;「正常」色改为青绿(#12b0a2) 阿拜多斯藏蓝校服 + 天蓝围巾 + 灰白发色

素材来源与版权

类别 内容 来源
第三方素材(真实立绘/头像) public/assets/shiroko-portrait.webp(540×1024 原图缩至 460 宽)、shiroko-icon.webp(120×120)、favicon-shiroko.png(64×64) SchaleDB(社区数据库):images/student/portrait/10010.webp、images/student/icon/10010.webp;学生 Id 10010 = 砂狼白子(校服原版)
设定资料(光环形状、发饰、围巾、配色、车型) 设计依据 萌娘百科 · 砂狼白子、Blue Archive Wiki、百度百科
自绘装饰(SVG/CSS) 围巾飘带 deco-scarf.svg、公路车 deco-bicycle.svg、光环水印、迷你光环状态点、倒十字图标、车轮加载图标 本仓库原创,按上述公开设定还原

⚠️ 版权提示:立绘与头像素材版权归 NEXON Games / Yostar(《蔚蓝档案》) 所有,此处仅作个人自用面板的风格美化。如果要把本仓库或 GitHub Pages 公开发布,建议删除 public/assets/shiroko-portrait.webp、shiroko-icon.webp、favicon-shiroko.png 并在配置里清空 artwork,或替换为你自己的图片(其余装饰均为自绘,可保留)。

切换与配置

"theme": "shiroko",     // shiroko | default(默认主题);也可用地址栏 ?theme=default 临时覆盖
"artwork": "assets/shiroko-portrait.webp"   // 立绘图片路径(相对 public/),留空则不显示

优先级:?theme= → 浏览器上次选择(localStorage)→ settings.theme。顶栏右侧的光环图标按钮可一键切换。切回默认主题后,立绘、光环、斜纹、围巾飘带、公路车全部消失,回到最初的简洁样式。

换成你自己的图:把图片放进 public/assets/,再把 artwork 指向它即可(透明底 PNG/WebP 效果最好)。文件不存在时静默不显示,不会出现破图。

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。

浏览器直连模式的限制(重要)

浏览器没有原始 TCP 能力,且受同源策略约束,browser 模式下:

  • HTTP:优先以 cors 模式请求,目标响应带 Access-Control-Allow-Origin 时可读到真实状态码;否则降级 no-cors,只能判断可达性,读不到状态码(显示「跨域可达」)
  • TCP:以 HTTP 连接行为近似——快速拒绝判为端口关闭;连接后无响应直到超时判为端口开放(启发式,无法区分「开放但静默」与「被防火墙丢弃」)
  • HTTPS 页面无法探测 HTTP 目标(浏览器混合内容拦截,会判为离线)
  • 结论:浏览器直连只作兜底。要准确的状态码与 TCP 探测,请使用 Node 探针或边缘 Worker 提供 /api/status

已实现 vs 路线图

v0.1.0 已实现

  • HTTP 探测:状态码 / 延迟 / 自定义期望值 / 超时 / 保留 3xx
  • TCP 探测:连通性 / 连接耗时 / 拒绝与超时区分
  • Node 探针:常驻巡检、周期调度、配置热重载、历史心率持久化(重启不断档)、原子写入 status.json
  • Node 探针附带静态服务器与 /api/status、/api/monitors、/api/refresh
  • --once(CI 单轮)与 --demo(演示数据)模式
  • 面板:状态卡片、心率图、分组页签、统计摘要、相对/绝对时间、自动刷新倒计时、暗色模式
  • 数据源三级自动降级 + ?source= 强制指定
  • Cloudflare Worker 边缘探针(cron + KV + 静态伺服)
  • GitHub Actions:定时探测 + Pages 部署
  • 四种部署路径文档

v0.2.0 已实现

  • 90 天可用率:按天分桶聚合、90 天滚动保留、卡片 90 格分级色条 + 悬停明细、顶部整体可用率(Node 与 Worker 双端一致实现)
  • --out=<path> 自定义快照输出路径;--demo 覆盖真实快照前给出告警
  • 修复:配置热重载后 /api/status 永久冻结在旧数据(闭包捕获了被替换的旧 state 引用,改为持有器对象)
  • 修复:慢目标(如 10s 超时)会被每秒的调度 tick 重复探测,形成探测风暴(改为探测前同步占位 + inFlight 标记)
  • 修复:非法/缺失 interval 会算出 NaN 导致该目标永久不再被调度(改为回落默认 60s)
  • 修复:刷新倒计时改按截止时间计算,不再受浏览器后台标签页定时器节流影响
  • scripts/verify.cjs 端到端回归验证(15 项断言,覆盖上述修复)

v0.2.1 已实现

  • 刷新即重新检测:/api/refresh 等待本轮探测完成后返回最新快照,面板自动刷新与手动「刷新」按钮都会真正重新探测目标(此前只是重读旧快照,导致「部分卡片停留在几十秒前、手动刷新也没变化」)
  • 服务端强制轮次去重:多标签页/多客户端同时刷新共享同一轮探测,2s 内的重复请求直接复用快照(避免对目标重复施压);强制轮次先等在途探测收尾,保证不跳过任何目标
  • settings.probeOnRefresh(默认 true):关闭后自动刷新退回只读轮询,手动刷新仍然强制检测
  • 目标级 probeOnRefresh: false:单个目标可退出「刷新即检测」(适合有限流的 API),只按自身 interval 探测;默认配置新增示例目标说明该用法
  • Cloudflare Worker 的 /api/refresh 同步改为「等待完成再返回」+ 同隔离体去重

v0.3.0 已实现

  • Banner 探测(type: banner):被动读取 SSH/SMTP/FTP/POP3/IMAP 等标识串,expectBanner 正则策略校验(可做版本合规)、revealBanner 显式开启才回显;不做认证、不发数据,不触发目标封禁
  • 四种面板形态:卡片 / 表格 / 大屏(深色 NOC)/ 极简(对外状态页),顶部一键切换 + ?view= + settings.view + localStorage 记忆
  • 刷新接口滥用防护:内网/回环来源限制(不信任 X-Forwarded-For)、每来源冷却 429 + Retry-After、轮次去重、总开关 allowRefresh;Worker 需 REFRESH_TOKEN 才开放
  • 信息泄露控制:local.monitors.json 私有配置分层(公开配置可只留 id/name/group)、hideTargets、redactDetails、TCP 详情不再回显地址、banner 默认不回显版本
  • 私有配置分层 + --config / --local-config(多实例与测试用)
  • 修复:客户端中断请求(切页/关标签)后向已断开 socket 写响应会抛出未捕获异常,可能终止探针进程
  • 修复:非卡片视图的状态点沿用胶囊色(着色规则作用域写死在 .card 上)
  • 修复:首次打开面板时需等强制检测完成才出数据,期间显示空白「等待检测」——改为先用已有快照立即渲染
  • 新增 docs/SECURITY.md(滥用防护 / SSH 探测边界 / 信息泄露控制)与 scripts/verify-security.cjs、scripts/verify-views.cjs

v0.4.0 已实现

  • UptimeRobot 数据源(只读):把 UR 已有监控作为「异地视角」接入面板,状态/延迟/心率/90 天可用率/故障原因全部映射自 UR 官方字段;镜像目标不参与本地强制探测;同步失败只记录错误并保留上次数据
  • UptimeRobot 兼容接口:/api/v2/getMonitors 输出与 UR v2 同构(含 custom_uptime_ratio、response_times、数字 id 与状态码编码)
  • stats.memory:探针自报内存占用(RSS/heapUsed),方便观察「监控器自身」的开销
  • 配置分层增强:--config / --local-config 指定配置文件路径;UR API Key 支持私有配置或 UPTIMEROBOT_API_KEY 环境变量
  • 修复:非 http/tcp 类型的目标行渲染为 undefined:undefined(改为有 url 用 url、有 host 用 host:port)
  • 修复:热重载会丢弃 UptimeRobot 镜像目标已累积的逐日分桶
  • 修复(实测发现):custom_down_durations 不能作为请求参数传给 UR v2(返回 invalid_parameter),改为只从响应中读取
  • 新增 scripts/verify-uptimerobot.cjs(22 项:映射单测 / 兼容接口 / 失败路径 / 真实 Key 冒烟)

v0.5.0 已实现

  • 表格设为默认视图,移除大屏与极简两种形态(面板只保留表格/卡片,切换器更精简)
  • 数据源过滤 settings.dataFilter:both / config(仅配置文件)/ uptimerobot(仅 UR),支持 ?filter= 临时覆盖;服务端快照与面板共用同一口径(私有配置覆盖后也不会错位)
  • 顶部统计按展示集合汇总:切换过滤后不再出现「列表 8 行、统计 14 个」
  • 自动刷新间隔可配置并按数据源自适应:refreshInterval(秒);只看 UptimeRobot 时默认 300 秒(UR 自身 5 分钟才更新)
  • /api/refresh?scope=external:只看 UR 的刷新只同步 UR 数据,不跑本地探测轮次;普通刷新也会顺带同步 UR(最小间隔 60 秒,尊重 UR 接口限流)

v0.6.0 已实现

  • 刷新节奏改为服务端驱动:快照新增 dataUpdatedAt(数据最后变更时刻)与 updateIntervalSec(服务端更新节奏),面板倒计时对齐到服务端下次更新,不再每次打开页面从整周期重新计时(旧行为最坏会看到近两倍周期的旧数据;实测:两次打开间隔 25s,倒计时差值 28s)
  • 自动刷新改为只读:探测完全由服务端按 interval / pollIntervalSec 驱动,访客数量不再影响对目标的压力;只有手动点「刷新」才触发立即重新检测
  • 新增自愈:服务端数据超过 1.5 个周期未更新时(探针刚重启、机器休眠过)面板自动触发一次重新检测
  • 移除已无意义的全局 probeOnRefresh 开关(目标级 probeOnRefresh: false 保留:该目标不参与强制检测);refreshInterval 语义澄清为「面板检查数据的最大间隔」
  • 页脚改为展示「数据更新于 … + 下次检查 Ns」,tooltip 内含服务端更新节奏与快照生成时间
  • scripts/verify.cjs 适配纯外部数据源部署(自动跳过不适用的探测类断言),并新增「倒计时对齐服务端」断言

v0.7.0 已实现

  • 砂狼白子主题(默认启用):官方立绘(右下角,宽屏自动为它让位)+ 头像 logo/favicon(光环环呼吸动画)+ 天蓝围巾飘带顶栏 + 倒十字发饰分组标识 + 公路车轮加载态 + 按官方造型还原的倾斜椭圆光环水印;配套青绿主色调
  • 主题切换:settings.theme / ?theme= / 顶栏光环按钮(localStorage 记忆),默认主题完整保留
  • settings.artwork 立绘位,默认指向内置立绘;未配置或文件缺失时静默不显示
  • 修复:dataFilter=uptimerobot(只展示外部数据源)时「平均延迟」恒为「–」——原规则一律排除 UR 延迟,改为「混合来源时排除、纯外部来源时以其为唯一口径」,服务端与面板同步调整

路线图(后续迭代)

  • 通知渠道: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 开放与缓存策略细化

开发

# 本地起服务(含探针)
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 } 即可被面板直接渲染。

界面截图

砂狼白子主题(默认,表格视图) 默认主题
shiroko default
卡片视图(白子主题) 移动端
cards mobile

UptimeRobot 数据源(?filter=uptimerobot,截图中含真实监控名与域名,公开发布前可自行删除):docs/screenshot-uptimerobot.png;演示数据 90 天满档:docs/screenshot-demo.png

致谢与定位

交互设计参考 Uptime Kuma(心跳图、90 天可用率条、分组卡片、汇总统计),实现为自研轻量版:不追求 Kuma 的通知/多用户/数据库体系,只保留「配置文件 → 探针 → 状态面板」的最小闭环,并对静态/边缘部署友好。