feat: v0.12.1 —— Worker 支持 UptimeRobot + 管理端远程访问 + 前后端分离

起因:核实「管理端在 Cloudflare 上是否有效」时发现一个更要紧的问题 ——
Worker 完全没有 UptimeRobot 同步能力(runChecks 的探测目标只有 config.monitors)。
而实际配置是 dataFilter=uptimerobot,也就是说直接部署到 Worker 面板会是空的:
它探测 8 个本地条目,而真正在监控的 6 个 UR 目标它根本不认识。

① Worker 支持 UptimeRobot(Step 1)
- edge/cloudflare/uptimerobot.js(新增):Workers 版 UR 只读数据源,
  与 prober/node/lib/uptimerobot.js 同一套语义(状态映射/ratios 解析/故障原因)
- runChecks 的探测集合扩展为「自建目标 + UR 镜像目标」;UR 目标只拉取不探测,
  采用其状态并补齐本地 daily 分桶
- probeScope 与 dataFilter 解耦(与本地探针同语义):config 范围不拉 UR,
  uptimerobot 范围不探测本地
- 同步失败时标记 sources.uptimerobot.ok=false 并保留上次数据,
  绝不把目标判为离线或从面板消失;UR 侧删除的监控自动清理
- 快照新增 probeScope;UR 目标带 meta(面板区分自建/外部)与 uptimeRatios

② 管理端远程访问(Step 2)
- settings.admin.allowRemote:探针默认只监听 127.0.0.1,显式打开才监听全网卡,
  启动日志就此告警。这是 Cloudflare Tunnel 的安全前提(已实测两种行为)
- edge/cloudflare/tunnel.yml(新增):命名隧道配置,只放行
  ^/(admin|api/admin/.*|assets/.*)$,其余 404 —— 不把公开面板一起挂到公网

③ 前后端分离(Step 3)
- Worker 侧 /admin 与 /api/admin/* 显式返回 403 + 指向文档的提示,
  而非默默落到静态资源变成 404,确保边缘侧没有任何可写面

测试:新增 scripts/verify-worker.cjs(36 项)—— 把 Worker 以 data: URL 动态载入以
绕开 cloudflare:sockets,注入假 KV/静态资源/UptimeRobot 服务器,
不需要 wrangler 也不需要联网。全项目 8 套 310 项全部通过。

过程中抓到两个真 bug:
1. UR 同步失败的降级路径引用了不含 meta 的旧对象,导致 name/url 为 undefined、
   UR 目标从面板上消失
2. stats.total 按 config.monitors.length 计数,在 probeScope=uptimerobot 时
   与实际参与的目标数不符

另有一条测试夹具教训:内存版 KV 必须实现 get(key, 'json') 的类型参数
(真实 Workers KV 会自动反序列化),否则 state.monitors 恒为 undefined。
This commit is contained in:
tamakyi
2026-10-14 14:51:28 +08:00
parent 752fad412b
commit d2aa0a1163
11 changed files with 1013 additions and 331 deletions
+42
View File
@@ -0,0 +1,42 @@
# Cloudflare Tunnel:让管理端从外网访问
#
# 场景:探针跑在内网/家用小主机上(没有公网入口),但你需要在手机或外网笔记本上
# 打开 /admin 做管理操作。
#
# 原理:cloudflared 在你的机器上**主动向外**建立到 Cloudflare 的隧道(出站连接,
# 不需要公网 IP、不需要开放任何入站端口),Cloudflare 再把指定子域名回源到它。
# 因此内网无需任何端口映射。
#
# 用法(命名隧道,推荐):
# 1. 安装 cloudflared
# 2. 在 Cloudflare Zero Trust 后台创建命名隧道,取它的 token
# 3. 打开探针的远程管理开关(默认关闭):
# 在 local.monitors.json 里加 "settings": { "admin": { "allowRemote": true } }
# 4. 跑:cloudflared tunnel --no-autoupdate run --token <你的 token>
#
# 一次性(快速隧道)命令,仅适合临时验证:
# cloudflared tunnel --url http://localhost:8787
# 它会打印一个随机 trycloudflare.com 地址;不要用于正式环境(无鉴权、无 SLA)。
#
# 前提:探针必须处于「已绑定 2FA」的状态。Tunnel 只是把端口暴露出去,
# 真正拦住未授权访问的是管理端的 TOTP + 会话 + 失败限流。
tunnel: <你的隧道 ID 或名字>
credentials-file: /etc/cloudflared/<TUNNEL_ID>.json
# Tunnel 只回源管理端这一条路径,面板与公开 API 不通过隧道暴露。
# ingress 的第一条是 catch-all,必须放在最后。
ingress:
# 管理后台页面与接口:都指向同一个本地端口(管理端自身会校验 2FA 与会话)
- hostname: admin.example.com
path: ^/(admin|api/admin/.*|assets/.*)$
service: http://localhost:8787
# 其余一律拒绝:不要把公开面板与 /api/status 一起暴露出去
- service: http_status:404
# 探针侧的探测与通知日志仍打在 stdout,不受隧道影响。
# 若用 systemd 常驻,建议:
# ExecStart=/usr/local/bin/cloudflared tunnel run --no-autoupdate --token-file /etc/cloudflared/token
# Restart=on-failure
# RestartSec=5s