Files
shiroko-monitor/docs/DEPLOYMENT.md
T
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

188 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 部署方案详解
四种方案按「成本从低到要求、数据从准到轻」排列。所有方案共用同一份 `public/` 目录与 `public/monitors.json` 配置。
---
## 方案一:本地运行(Node)
最适合内网自用与开发调试,数据最实时。
```bash
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. 首次提交时建议带一份占位数据,避免页面空白:
```bash
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` 模式实时读取。
```bash
# 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`**(它会随静态资源公开):
```bash
wrangler secret put UPTIMEROBOT_API_KEY # 填 ur... 开头的只读 Key
```
同时在 `public/monitors.json` 的 `settings` 里开启(`apiKey` 留空,由 secret 提供):
```jsonc
{
"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` 打开:
```jsonc
// local.monitors.json
{ "settings": { "admin": { "allowRemote": true } } }
```
打开后探针监听 `0.0.0.0`,启动日志会明确警告 —— 提醒你该端口同时伺服公开面板,前置层必须启用 HTTPS。
**怎么把流量送到这个端口由你决定**:反向代理、隧道、VPN、直连公网 IP 都行,本项目不做任何假设,也不会替你生成某种特定的部署配置。
### 让面板知道管理端在哪
管理界面里有一项「**面板上的管理入口链接**」:填上管理端的实际地址后,公开面板页脚会出现一个「管理」按钮指向它;留空则不显示(默认,避免在公网面板上暴露管理面的位置)。地址只接受 `http` / `https`。
这样部署形态与面板展示就解耦了:面板部署在 Cloudflare、管理端在别处、访客从面板点进去 —— 中间怎么连通由你的网络决定。