Files
tamakyi f345626840 fix: 管理入口改为「填链接」而非绑定特定隧道方案,并修掉配置接口的密钥泄露
用户反馈:「你不需要帮我处理 tunnel 这部分,也许我不是通过 tunnel 开放内网呢?
你只需要给我一个填写开放的管理端链接的地方。」

之前把「Cloudflare Tunnel」当成既定方案写进了配置与文档,这是替用户做了假设。
现在改为:项目只提供一个填地址的地方,怎么暴露完全由使用者决定。

- 删除 edge/cloudflare/tunnel.yml 与全部 tunnel 相关文档
- 新增 settings.adminUrl:管理界面里填一个地址,公开面板页脚出现「管理」按钮
  指向它;留空则完全不显示(默认,避免在公网面板暴露管理面位置)
- 只接受 http/https,javascript: 等协议被服务端拒绝(会被渲染成 <a href>)
- 设置落在 local.monitors.json(私有层),只有自己的面板会出现该按钮
- 面板启动时额外拉 /api/monitors 合并 settings:原先只读静态 monitors.json
  (公开层),导致私有层的设置面板看不到;纯静态部署无该接口时沿用公开配置

顺带修掉一个既存漏洞(与本轮改动无关,是引入私有层叠加机制时埋下的):
/api/monitors 无鉴权却回吐合并后的完整配置,包含私有层的 UptimeRobot API Key ——
只要配了 key,任何访客 curl /api/monitors 就能拿到。现在两侧(Node 与 Worker)
都对对外响应剥离 uptimeRobot.apiKey,保留 enabled 等非敏感字段供面板读取;
探针内部同步仍用完整配置,剥离只作用于 HTTP 出口。

测试:verify-worker 36→40 项(新增 [E] 配置接口密钥泄露 4 条)、
verify-security 20→25 项(新增 [D2] 密钥泄露 5 条)。全项目 8 套 314 项通过。
2026-10-14 15:23:22 +08:00

10 KiB
Raw Permalink Blame History

部署方案详解

四种方案按「成本从低到要求、数据从准到轻」排列。所有方案共用同一份 public/ 目录与 public/monitors.json 配置。


方案一:本地运行(Node)

最适合内网自用与开发调试,数据最实时。

node prober/node/index.js          # 默认端口 8787
PORT=9000 node prober/node/index.js # 自定义端口
  • 面板:http://localhost:8787/
  • 状态 API:http://localhost:8787/api/status
  • 改 public/monitors.json 后自动热重载,无需重启
  • 历史心率存于 public/status.json,重启后自动恢复
  • 90 天可用率按天累积在 public/status.json,重启不丢(每次探测先恢复分桶再追加)
  • 面板刷新即重新检测(/api/refresh 等待本轮完成再返回);多标签页同时刷新只触发一轮探测
  • 后台常驻可用 pm2 / systemd / Windows 服务,按需自行封装

适用:监控内网服务(面板浏览器与目标同内网时,浏览器直连模式也能用)。


方案二:GitHub Pages + Actions(零成本公网)

架构:仓库里的 public/ 整体作为 Pages 站点;Actions 每 5 分钟跑一轮 Node 探针,把 public/status.json 提交回仓库,面板以 static 模式读取。

步骤:

  1. 推送代码到 GitHub 仓库(main 分支)
  2. 仓库 Settings → Pages → Build and deployment → Source 选 GitHub Actions
  3. 首次提交时建议带一份占位数据,避免页面空白:
    node prober/node/index.js --once --demo   # 生成演示 status.json
    git add public/status.json && git commit -m "chore: demo status" && git push
    
  4. 之后 pages.yml 负责每次推送自动部署;monitor.yml 每 5 分钟更新一次 status.json(提交信息为 chore: update status snapshot)
  5. 访问 https://<用户名>.github.io/<仓库名>/

注意事项:

  • Actions 计划任务最小粒度 5 分钟,数据最多 5 分钟延迟;心跳图按「检测次数」记录,5 分钟粒度下 48 格 ≈ 4 小时
  • 90 天可用率同样按「检测次数」统计,5 分钟粒度约 288 次/天;每轮 Actions 会把当日分桶续写到 status.json 并提交,因此可用率数据跨运行累积(若某天 Actions 未运行,该日色条会显示「无数据」而不是 0%)
  • 静态快照模式没有服务端,面板无法触发重新检测:此方案下刷新只是重新读取仓库里的 status.json,数据新鲜度完全取决于 Actions 的运行频率(面板页脚会显示「数据源:静态快照」以作区分)
  • 若仓库开启了分支保护,需允许 github-actions[bot] 推送(workflow 已声明 contents: write 权限)
  • 免费额度对公开仓库无限量,私有仓库每月 2000 分钟,足够

方案三:Cloudflare Workers(边缘实时)

架构:Worker 每分钟由 cron 触发,用 fetch 探测 HTTP、用 cloudflare:sockets 探测 TCP,状态写入 KV;同 Worker 伺服 public/ 静态资源与 /api/*。面板以 api 模式实时读取。

# 1. 安装 wrangler(任选其一,不影响本项目运行时)
npm install -g wrangler

# 2. 登录并创建 KV 命名空间
wrangler login
wrangler kv namespace create MONITOR_KV
# 把输出的 id 填到 edge/cloudflare/wrangler.toml 的 [[kv_namespaces]].id

# 3. 部署(wrangler.toml 已指向 ../../public 作为静态资产,monitors.json 随之部署,无需手动导入 KV)
cd edge/cloudflare
wrangler deploy
  • 面板与 API:https://<worker 域名>/ 与 /api/status
  • 手动触发一轮:curl https://<worker 域名>/api/refresh(等待本轮完成后返回最新快照,与本地探针同语义)
  • cron 表达式在 wrangler.toml 的 [triggers].crons 中调整(* * * * * = 每分钟)

接入 UptimeRobot 数据源

Worker 与本地探针共用同一套 UptimeRobot 语义(只读,映射逻辑见 edge/cloudflare/uptimerobot.js)。API Key 用 secret 传入,不要写进 monitors.json(它会随静态资源公开):

wrangler secret put UPTIMEROBOT_API_KEY   # 填 ur... 开头的只读 Key

同时在 public/monitors.json 的 settings 里开启(apiKey 留空,由 secret 提供):

{
  "settings": {
    "dataFilter": "uptimerobot",        // 只展示 UptimeRobot 目标
    "uptimeRobot": { "enabled": true, "apiKey": "" }
  }
}

行为与本地探针一致:

  • probeScope 决定真正发请求的范围(all / config / uptimerobot),未显式设置时跟随 dataFilter
  • UptimeRobot 目标只拉取不探测,采用其状态并补齐本地 daily 分桶
  • 同步失败时保留上次数据并把 sources.uptimerobot.ok 置为 false,绝不因此把目标判为离线
  • UR 侧删除的监控会从面板移除,避免残留僵尸条目

注意事项:

  • TCP 出站连接(cloudflare:sockets)需要 Workers 付费计划($5/月);免费计划下 http 类型探测与面板完全正常,tcp 类型会报错并记为离线
  • banner 类型边缘端不支持(需原始 socket 读取),会记为离线并给出说明文案;这类目标请用本地探针
  • KV 为最终一致存储,刚写入后立即读取可能有秒级延迟,对分钟级巡检无影响
  • 90 天可用率分桶随 state 一起存于 KV,按 UTC 日期 分桶(边缘环境无本地时区);每分钟 cron 约 1440 次/天
  • 免费版 Worker 每日 10 万次请求,面板静态资源走 Assets 不计入 Worker 请求数
  • 中国大陆访问 Worker 默认域名不稳定,可绑定自有域名(需 Cloudflare 账号管理 DNS)

方案四:EdgeOne Pages(国内加速)

EdgeOne Pages 当前以静态托管为主,采用与方案二相同的「静态 + 定时数据」路径,国内访问体验更好。

步骤:

  1. 登录腾讯云 EdgeOne 控制台,进入 Pages → 新建项目 → 导入 Git 仓库(或直接上传 public/ 目录)
  2. 构建配置:无构建步骤,输出目录填 public
  3. 数据更新二选一:
    • A. GitHub Actions 驱动:保持方案二的 monitor.yml 不变,EdgeOne 侧开启「构建时拉取最新提交」或配置 Webhook 触发重新部署
    • B. CI 推送触发:在 monitor.yml 末尾追加一步,调用 EdgeOne 的部署 API / Webhook(按控制台提示填写)
  4. 绑定自定义域名(需要备案的域名走国内加速线路)

说明:EdgeOne Pages 若上线边缘函数(类 Pages Functions),可把 edge/cloudflare/worker.js 的 /api/* 部分平移过去,实现分钟级实时数据;当前初稿以静态路径交付,接口契约完全一致,迁移时面板无需改动。


90 天可用率在各方案下的表现

可用率按「探测次数」加权统计,因此探测频率直接决定精度,也决定色条填充速度:

方案 每次探测间隔 每天样本数 90 天色条填满所需时间
本地 Node 按 monitors.json 的 interval(示例 60s) ≈1440 90 天
Cloudflare Workers cron 每分钟 ≈1440(UTC 日期分桶) 90 天
GitHub Pages + Actions ≥5 分钟(Actions 下限) ≈288 90 天
EdgeOne Pages 同 GitHub Pages ≈288 90 天

注意:分桶数据随 status.json(或 KV state)持久化并滚动保留 90 天,必须由探针持续写入才会累积——静态快照方案下依赖 Actions 按计划运行;Actions 计划任务在仓库长期无活动时可能被 GitHub 自动暂停(后台重新启用即可)。

需要临时提升精度时,可把 interval 调到 30s 或更低(本地/Worker 均支持,注意目标站点的压力与自身限流)。


方案对比与选型建议

维度 本地 Node GH Pages CF Workers EdgeOne Pages
数据延迟 秒级(interval) ≤5 分钟 ≤1 分钟 ≤5 分钟
90 天可用率精度 高(≈1440 次/天) 中(≈288 次/天) 高(≈1440 次/天) 中(≈288 次/天)
TCP 探测 ✅ ✅(Actions 内) 付费版 ✅ / 免费版 ❌ 同 GH Pages
真实状态码 ✅ ✅ ✅ ✅
内网目标 ✅(浏览器同网即可) ❌ ❌ ❌
UptimeRobot 数据源 ✅ ✅ ✅ ❌(需改用自建探测)
通知 / 维护窗口 / 事件 ✅ ❌ ❌ ❌
管理端(2FA) ✅ ❌ ❌ ❌
国内访问速度 本机最快 一般 一般 快
运维成本 需保活进程 零 零 零

建议:个人/演示用 GitHub Pages;生产公网监控用 Cloudflare Workers;服务对象在国内且以 HTTP 监控为主用 EdgeOne Pages;内网资产用 本地 Node。

⚠️ 管理端只存在于「本地 Node」部署形态。 其余形态的部署环境只提供只读面板与探测,没有任何写接口 —— Cloudflare Worker 上访问 /admin 或 /api/admin/* 会得到 403(而非 404,因为那里确实不提供管理能力)。需要管理操作(改配置、维护窗口、通知自检、立即重探)时,在本地 Node 那一侧完成。

原因不是「没做」,而是这些能力依赖本地状态:2FA 档案与会话存在 data/ 目录与进程内存里,而 Workers 无文件系统、会话状态也无法跨 isolate 共享(KV 又是最终一致,无法承载「恢复码一次性」这类语义)。把这个结论说清楚,比在边缘环境里做一个半可靠的管理端更有价值。


远程访问管理端

探针默认只监听 127.0.0.1,外部访问不到。需要时用 settings.admin.allowRemote 打开:

// local.monitors.json
{ "settings": { "admin": { "allowRemote": true } } }

打开后探针监听 0.0.0.0,启动日志会明确警告 —— 提醒你该端口同时伺服公开面板,前置层必须启用 HTTPS。

怎么把流量送到这个端口由你决定:反向代理、隧道、VPN、直连公网 IP 都行,本项目不做任何假设,也不会替你生成某种特定的部署配置。

让面板知道管理端在哪

管理界面里有一项「面板上的管理入口链接」:填上管理端的实际地址后,公开面板页脚会出现一个「管理」按钮指向它;留空则不显示(默认,避免在公网面板上暴露管理面的位置)。地址只接受 http / https。

这样部署形态与面板展示就解耦了:面板部署在 Cloudflare、管理端在别处、访客从面板点进去 —— 中间怎么连通由你的网络决定。