Files
shiroko-monitor/public/monitors.json
T
tamakyi 6b0a23c38e feat: v0.12.2 —— 跨域只读支持(面板可部署在别处)
用户诉求:管理端与前端完全分离,本地探针只跑 /admin,前端部署到 GitHub Pages
并硬编码管理地址,探测仍在本地常驻。用户自己搭隧道给本地套 HTTPS。

硬限制(务必先看):GitHub Pages 强制 HTTPS,而浏览器「混合内容」策略禁止
HTTPS 页面 fetch HTTP 资源 —— 这与 CORS 无关,改设置也没用。
因此 settings.dataUrl 必须与面板页面同为 HTTPS。

服务端
- 只读接口(/api/status、/api/monitors、/api/events、/api/v2/getMonitors、
  /api/refresh)按 settings.corsOrigins 白名单回显 Access-Control-Allow-Origin
- /api/admin/* 一律不放行跨域,预检直接 403 —— 外部页面既读不到也改不了
- 绝不使用 '*':白名单里写 '*' 不放行任何来源

前端
- settings.dataUrl 存在时,所有 api/ 相对路径自动改写到该地址
- 数据源标记为「远程探针(跨域)」,页脚可区分
- 管理界面新增两项配置:跨域数据源地址、允许跨域的页面地址(改完立即生效)

关键设计:origin 匹配必须考虑端口
origin 是「协议 + 主机 + 端口」的完整组合。最初白名单存 http://localhost(无端口),
导致本地前端跑在 localhost:5173 时被拒 —— 浏览器实测 Failed to fetch 而 curl 能过。
修法:回环主机(localhost / 127.0.0.1)忽略端口放行,其余要求完整 origin 精确匹配。

测试:verify-security 25→40 项,新增 [D3] CORS 白名单 14 条 + [D4] '*' 写法 1 条。
全项目 8 套 334 项通过。

附记一个测试上的教训:跨域 fetch 时 JS 读不到 Access-Control-Allow-Origin
(CORS 规范规定只有同源能读),所以断言「是否放行」要看 fetch 有没有拿到数据,
不能看响应头 —— 我一开始用 headers.get() 判断,拿到 null 误以为失败。
2026-10-14 16:19:31 +08:00

107 lines
2.4 KiB
JSON
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.
{
"settings": {
"title": "Shiroko Monitor",
"description": "轻量级服务监控面板",
"view": "table",
"theme": "shiroko",
"artwork": "assets/shiroko-figure.webp",
"decoOpacity": 0.78,
"dataFilter": "uptimerobot",
"refreshInterval": 300,
"dataSource": "auto",
"adminUrl": "",
"dataUrl": "",
"corsOrigins": [],
"uptimeRobot": {
"enabled": false,
"apiKey": "",
"pollIntervalSec": 300,
"group": "UptimeRobot"
}
},
"monitors": [
{
"id": "example-com",
"name": "Example 站点",
"group": "公共服务",
"type": "http",
"url": "https://www.example.com",
"method": "GET",
"expectStatus": [200],
"interval": 60,
"timeout": 10
},
{
"id": "baidu",
"name": "百度",
"group": "公共服务",
"type": "http",
"url": "https://www.baidu.com",
"expectStatus": [200],
"interval": 60,
"timeout": 10
},
{
"id": "cloudflare-site",
"name": "Cloudflare 官网",
"group": "公共服务",
"type": "http",
"url": "https://www.cloudflare.com",
"expectStatus": [200],
"interval": 60,
"timeout": 10
},
{
"id": "github-api",
"name": "GitHub API(限流目标示例)",
"group": "公共服务",
"type": "http",
"url": "https://api.github.com",
"expectStatus": [200],
"interval": 300,
"timeout": 10,
"probeOnRefresh": false
},
{
"id": "httpbin-500",
"name": "HTTPBin 500(演示 expectStatus)",
"group": "公共服务",
"type": "http",
"url": "https://httpbin.org/status/500",
"expectStatus": [500],
"interval": 120,
"timeout": 10
},
{
"id": "badssl-expired",
"name": "BadSSL 过期证书(演示离线)",
"group": "公共服务",
"type": "http",
"url": "https://expired.badssl.com/",
"expectStatus": [200],
"interval": 120,
"timeout": 10
},
{
"id": "cloudflare-dns-tcp",
"name": "Cloudflare 443 端口",
"group": "基础设施",
"type": "tcp",
"host": "1.1.1.1",
"port": 443,
"interval": 60,
"timeout": 10
},
{
"id": "google-dns-tcp",
"name": "Google DNS 53 端口",
"group": "基础设施",
"type": "tcp",
"host": "8.8.8.8",
"port": 53,
"interval": 60,
"timeout": 10
}
]
}