用户诉求:管理端与前端完全分离,本地探针只跑 /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 误以为失败。
107 lines
2.4 KiB
JSON
107 lines
2.4 KiB
JSON
{
|
||
"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
|
||
}
|
||
]
|
||
}
|