面向 CF-Server-Monitor 后端(Cloudflare Workers + D1 + Durable Objects)的完整 REST / WebSocket API 参考。 本文档覆盖所有公开端点、内部端点、鉴权机制、错误码、数据结构与 WebSocket 实时推送协议。
Base URL:
https://<your-worker-domain>(部署后由 Cloudflare Workers 提供)统一响应头:
Content-Type: application/json; charset=utf-8(除特别说明外)- CORS:当
CORS_ALLOWED_ORIGINS环境变量配置了允许的源时,会附带Access-Control-Allow-Origin / Allow-Credentials / Vary: Origin。X-Cache: HIT | MISS:仅出现在/api/history/all响应中。
- 0. 通用规范
- 1. 探针上报接口
- 2. 公开 API(前端/管理端共用)
- 3. 管理端 API(鉴权)
- 3.1
POST /admin/api- 管理操作入口 - 3.2
action: login- 登录 - 3.3
action: get_settings- 读取全部设置 - 3.4
action: list- 列出全部服务器(含在线/统计) - 3.5
action: d1_usage- D1 / Workers 用量 - 3.6
action: save_settings- 保存设置 - 3.7
action: add- 新增服务器 - 3.8
action: edit- 修改服务器信息 - 3.9
action: delete- 删除服务器 - 3.10
action: batch_delete- 批量删除 - 3.11
action: save_order- 保存服务器排序
- 3.1
- 4. 系统维护端点
- 5. 数据结构
- 6. 定时任务 (Cron)
- 7. 错误码速查表
- 8. 完整 cURL 示例
- 9. 版本与变更说明
项目使用 三套并行的鉴权机制,按接口范围区分使用。
- 使用位置:
POST /update - 方式:请求体字段
secret - 值:必须等于 Worker 环境变量
API_SECRET - 失败返回:
401 { "error": "Invalid secret", "code": 401 }
- 使用位置:
POST /admin/api的action: login - 方式:请求体字段
username/password(后端内部组装Basic base64(user:pass)进行校验) - 校验顺序:
- 若
site_options.password已设置为 PBKDF2 格式 → 按pbkdf2_sha256$iterations$salt$hash校验 - 若
site_options.password为旧版 32 位 MD5 → 按 MD5 兼容校验,成功后自动升级为 PBKDF2 - 若
site_options.password未设置或为空 → 与API_SECRET直接比对 - 用户名:若
site_options.username已设置则用之,否则使用API_USER_NAME环境变量,最终回退为admin
- 若
- 失败返回:
401 { "error": "Invalid username or password", "code": 401 }
- 使用位置:所有非
login的POST /admin/api、POST /updateDatabase、POST /clearHistory - 方式:
Authorization: Bearer <token>Header - Token 签发:
HS256JWT,默认有效期 604800 秒(7 天) - 签名密钥(优先级):
site_options.jwt_secret(长度 ≥ 32)API_SECRET(不够 32 字符时padEnd补'x'后取前 64 位)- 回退常量:
'default_jwt_secret_for_server_monitor'
- Payload 字段:
{ "sub": "admin", "iat": <unix>, "exp": <unix + 604800> } - 失败返回:
401 { "error": "Unauthorized", "code": 401 }
缓存提示:管理端登录成功后,前端应将
token存于localStorage,并对所有非登录的admin/api请求自动加上Authorization: Bearer <token>Header。
当 site_options.turnstile_enabled === 'true' 时,所有 /api/* 与 /admin/api 公共接口(除了下方 bypass 列表)都需要先验证 Cloudflare Turnstile Token。
Bypass 列表(无需 Turnstile):
/admin/api(/admin/api走另一套 Turnstile:见action: login)/api/ws(WebSocket 升级)/api/config在 不携带X-Turnstile-Token与X-Turnstile-Verified时(用于初始化判断是否需要验证)
验证流程:
- 首次访问:客户端从
/api/config拿到turnstile_site_key。 - 前端渲染 Turnstile 组件 → 拿到一次性
token。 - 后续请求在 Header 增加:
X-Turnstile-Token: <token from cloudflare> - Worker 用
site_options.turnstile_secret_key调用https://challenges.cloudflare.com/turnstile/v0/siteverify验证。 - 验证成功后,Worker 通过
X-Turnstile-Verified这个 加密 Header 给客户端发"已验证凭证"(AES-GCM 加密、API_SECRET/TURNSTILE_SECRET_KEY派生密钥、有效期 3600 秒),后续可省略X-Turnstile-Token。 - 客户端也可以把
X-Turnstile-Verified再次带回,Worker 会优先验证该 Header(验证有效期)。
相关请求/响应 Header:
| Header | 方向 | 含义 |
|---|---|---|
X-Turnstile-Token |
Client → Server | 当次 Turnstile token(明文) |
X-Turnstile-Verified |
双向 | AES-GCM 加密的 { expires: <unix+3600>, verified: true, timestamp: <ms> },base64 字符串 |
失败返回:403 { "error": "Turnstile verification failed", "code": 403 }
成功响应:
{
// 业务字段,结构因接口而异
"success": true,
...
}注:项目里"成功响应"是直接
JSON.stringify业务对象,没有固定的code字段。HTTP 状态码始终为200。
成功响应特例:
POST /update→ 纯文本OK(Content-Type: text/plain)- WebSocket 升级 →
101 Switching Protocols
错误响应:
{
"error": "human readable message",
"code": 400
}
code字段是 HTTP 状态码的镜像,便于前端无需读取 status 即可分流。
| code | 含义 | 常见场景 |
|---|---|---|
| 400 | Bad Request | 参数缺失/类型错/UUID 不合法/未知 action |
| 401 | Unauthorized | 缺/错 token、账号密码错、站点非公开且未登录 |
| 403 | Forbidden | Turnstile 验证失败 |
| 404 | Not Found | 服务器 ID 不存在 |
| 409 | Conflict | DATABASE_UPGRADE_REQUIRED,需先调用 /updateDatabase |
| 500 | Internal Server Error | DB 异常等未捕获错误 |
| 503 | Service Unavailable | WebSocket 不可用(未绑定 DO) |
- Cloudflare Workers / D1 的硬性限额由 Cloudflare 平台强制(D1:500 万行读 / 10 万行写 / 日;Workers:10 万次请求 / 日)。
/admin/api?action=d1_usage可查询当前账户当日用量与近 24h 用量。
环境变量 CORS_ALLOWED_ORIGINS,逗号分隔的源白名单,例如:
CORS_ALLOWED_ORIGINS=https://status.example.com,https://admin.example.com
- 当请求
Origin命中白名单 → 响应带Access-Control-Allow-Origin: <origin>、Access-Control-Allow-Credentials: true、Vary: Origin。 - 预检请求
OPTIONS→ 直接返回204,并回显Access-Control-Request-Method/Access-Control-Request-Headers,缓存 86400 秒。 - 未配置或未命中 → 不会下发 CORS Header,浏览器侧会被同源策略拦截。
调用方:服务器侧探针(Bash install.sh / Windows cf-server-monitor.pyw)。 鉴权:
secret字段 ==env.API_SECRETTurnstile:不参与
Request
-
Method:
POST -
Path:
/update -
Headers:
Content-Type: application/json X-Agent-Version: <探针版本号> X-Agent-Config-Schema: 2 X-Agent-Config-Md5: <最后成功应用的配置 MD5,首次为 none>动态配置请求头为新版探针使用的可选字段;未携带时保持旧版响应协议。
-
Body(JSON):
{ "id": "9b2c4d3e-1a2b-4c5d-9e8f-7a6b5c4d3e2f", "secret": "<API_SECRET>", "metrics": { "cpu": "12.34", "ram_total": "8192", "ram_used": "3700", "swap_total": "2048", "swap_used": "100", "disk_total": "102400", "disk_used": "32000", "load_avg": "0.10 0.20 0.30", "boot_time": "1700000000000", "net_rx": "12345678", "net_tx": "87654321", "net_rx_monthly": "1073741824", "net_tx_monthly": "536870912", "net_in_speed": "1024", "net_out_speed": "512", "os": "Ubuntu 22.04", "arch": "x86_64", "cpu_info": "Intel(R) Xeon(R) CPU", "cpu_cores": "4", "gpu": 12.5, "gpu_info": "NVIDIA GeForce RTX 3060", "processes": "256", "tcp_conn": "32", "udp_conn": "4", "ip_v4": "1", "ip_v6": "1", "ping_ct": "23", "ping_cu": "25", "ping_cm": "30", "ping_bd": "40", "loss_ct": "0", "loss_cu": "0", "loss_cm": "0", "loss_bd": "0" } }新版探针也可以一次上报多个采集样本,后端兼容旧的单条
metrics格式。批量格式示例:{ "id": "9b2c4d3e-1a2b-4c5d-9e8f-7a6b5c4d3e2f", "secret": "<API_SECRET>", "metrics": { "...": "latest metrics, kept for compatibility" }, "samples": [ { "ts": 1737638340000, "metrics": { "...": "metrics at this timestamp" } }, { "ts": 1737638341000, "metrics": { "...": "metrics at this timestamp" } } ], "collect_interval": 1, "report_interval": 60 }
字段说明(metrics):
| 字段 | 类型 | 单位 | 必填 | 说明 |
|---|---|---|---|---|
cpu |
string | % | 是 | CPU 占用率,保留 2 位小数 |
ram_total |
string | MB | 是 | 内存总容量 |
ram_used |
string | MB | 是 | 内存已用 |
swap_total |
string | MB | 是 | Swap 总容量 |
swap_used |
string | MB | 是 | Swap 已用 |
disk_total |
string | MB | 是 | 磁盘总容量 |
disk_used |
string | MB | 是 | 磁盘已用 |
load_avg |
string | - | 是 | 三个浮点,空格分隔 |
boot_time |
string | 毫秒 | 是 | 系统启动时间(Unix ms) |
net_rx |
string | 字节 | 是 | 累计接收字节 |
net_tx |
string | 字节 | 是 | 累计发送字节 |
net_rx_monthly |
string | 字节 | 是 | 当月累计下行 |
net_tx_monthly |
string | 字节 | 是 | 当月累计上行 |
net_in_speed |
string | B/s | 是 | 实时下行速度 |
net_out_speed |
string | B/s | 是 | 实时上行速度 |
os |
string | - | 是 | 操作系统 |
arch |
string | - | 是 | 系统架构 |
cpu_info |
string | - | 是 | CPU 型号 |
cpu_cores |
string | - | 是 | 逻辑核心数 |
gpu |
number|null | % | 否 | GPU 占用(Linux 探针可从 nvidia-smi / rocm-smi 读取) |
gpu_info |
string|null | - | 否 | GPU 型号 |
processes |
string | - | 是 | 进程数 |
tcp_conn |
string | - | 是 | TCP 活跃连接数 |
udp_conn |
string | - | 是 | UDP 套接字数 |
ip_v4 |
string | - | 是 | 1/0,IPv4 可达性 |
ip_v6 |
string | - | 是 | 1/0,IPv6 可达性 |
ping_ct |
string | ms | 否 | 电信节点延时,空字符串表示未取到 |
ping_cu |
string | ms | 否 | 联通节点延时 |
ping_cm |
string | ms | 否 | 移动节点延时 |
ping_bd |
string | ms | 否 | BGP 节点延时 |
loss_ct |
string | % | 否 | 电信丢包率 |
loss_cu |
string | % | 否 | 联通丢包率 |
loss_cm |
string | % | 否 | 移动丢包率 |
loss_bd |
string | % | 否 | BGP 丢包率 |
Response
- 旧版探针(未携带
X-Agent-Config-Schema: 2):返回200 OK:(OKContent-Type: text/plain) - 新版探针且配置 MD5 一致:返回
204 No Content,不包含响应体。 - 新版探针且配置 MD5 不一致:返回
200 OK,响应头携带新的X-Agent-Config-Md5,响应体为按字段名排序的完整 QueryParam 配置:(collect_interval=0&report_interval=60&reset_day=1&schema_version=2Content-Type: application/x-www-form-urlencoded; charset=utf-8) - 动态配置的字段范围、规范化及客户端校验规则详见 AGENT_CONFIG.md。
- 失败:
{ "error": "Invalid secret", "code": 401 } { "error": "Server not found", "code": 404 }
副作用
metrics_history只写入本次请求中最新的一个样本,避免 1 秒采集时放大 D1 写入次数。- 触发 Durable Object
MetricsBroadcaster内部广播,统一发送{type:"batchUpdate", ts, updates:[...]}格式,前端按样本时间逐个回放。 - 写入
request.cf.country(或cf-ipcountryHeader)作为该条记录的region字段(统一转大写)。
以下接口除
/api/ws外,若site_options.is_public !== 'true'则必须携带 JWT(Authorization: Bearer <token>)才能访问。 命中 Turnstile 时需带X-Turnstile-Token或X-Turnstile-Verified。
Request
- Method:
GET - Path:
/api/config - Headers(可选):
X-Turnstile-Token: <token> # 当携带时,验证后会回写 X-Turnstile-Verified X-Turnstile-Verified: <encrypted>
Response 200 OK
{
"version": "V2.7.11 Beta",
"turnstile_enabled": true,
"turnstile_site_key": "1x00000000000000000000AA",
"verified": false,
"turnstile_verified": "BASE64_AES_GCM_ENCRYPTED_STRING_OR_NULL",
"last_workers_version": "V2.7.11 Beta",
"last_agent_version": "1.3.0",
"csp_static": "https://unpkg.com,https://cdn.jsdelivr.net",
"csp_api": "https://api.example.com",
"theme_options": {
"a": 1,
"b": 2
},
"show_long_history": true
}| 字段 | 类型 | 说明 |
|---|---|---|
version |
string | 当前部署自身 Workers 版本 |
turnstile_enabled |
boolean | 站点是否启用人机验证 |
turnstile_site_key |
string | Turnstile 前端公钥;前端拿到后渲染 widget |
verified |
boolean | 当前请求是否携带了有效的 X-Turnstile-Verified |
turnstile_verified |
string|null | 当次验证成功后回写给客户端的"已验证凭证",客户端应回存并在 1 小时内复用 |
last_workers_version |
string|null | 登录时返回远程最新 Workers 版本;来源为 GitHub version.json,后端缓存 5 分钟 |
last_agent_version |
string|null | 登录时返回远程最新 Agent 版本;来源为 GitHub version.json,后端缓存 5 分钟 |
csp_static |
string | 静态资源 CSP 白名单;匿名请求也会返回 |
csp_api |
string | API CSP 白名单;匿名请求也会返回 |
theme_options |
object | 第三方主题自定义配置;未配置时为空对象,匿名请求也会返回 |
show_long_history |
boolean | 是否允许查看超过 1 小时的历史曲线(未登录用户强制 1 小时上限) |
X-Turnstile-Token携带且验证成功时,响应头会同步设置X-Turnstile-Verified(加密串)。
Request
- Method:
GET - Path:
/api/servers - Headers(按需):
Authorization: Bearer <jwt>、X-Turnstile-Token或X-Turnstile-Verified
Response 200 OK
{
"servers": [ /* Server[],见 5.1 */ ],
"stats": {
"total": 10,
"online": 8,
"offline": 2,
"globalSpeedIn": 1234.5,
"globalSpeedOut": 567.8,
"globalNetTx": 1234567890,
"globalNetRx": 9876543210
},
"regionStats": { "US": 3, "JP": 2, "CN": 5 },
"sysConfig": {
"show_price": true,
"show_expire": true,
"show_tf": true,
"site_title": "My Server Monitor"
}
}| 字段 | 说明 |
|---|---|
servers |
已合并最新指标的服务器列表(按 sort_order ASC),未登录用户自动过滤 is_hidden = '1' |
stats |
聚合统计:在线阈值 300 秒(5 分钟无上报视为离线) |
regionStats |
按 ISO 区域码(大写)统计的服务器数 |
sysConfig |
当前站点开关,前端据此显示对应 UI |
Request
- Method:
GET - Path:
/api/server - Query:
id(必填):服务器 UUID
- Headers(按需):同
/api/servers
Response 200 OK
{
"id": "9b2c...",
"name": "HK-01",
"server_group": "HK",
"price": "¥30/月",
"expire_date": "2026-12-31",
"traffic_limit": "1TB",
"traffic_calc_type": "total",
"reset_day": 1,
"collect_interval": 1,
"report_interval": 60,
"is_hidden": "0",
"sort_order": 0,
"cpu": 12.34,
"load_avg": "0.10 0.20 0.30",
"net_in_speed": 1024,
"net_out_speed": 512,
"net_rx": 12345678,
"net_tx": 87654321,
"net_rx_monthly": 1073741824,
"net_tx_monthly": 536870912,
"processes": 256,
"tcp_conn": 32,
"udp_conn": 4,
"ping_ct": 23,
"ping_cu": 25,
"ping_cm": 30,
"ping_bd": 40,
"loss_ct": 0,
"loss_cu": 0,
"loss_cm": 0,
"loss_bd": 0,
"ram_total": 8192,
"ram_used": 3700,
"swap_total": 2048,
"swap_used": 100,
"disk_total": 102400,
"disk_used": 32000,
"cpu_cores": 4,
"cpu_info": "Intel(R) Xeon(R) CPU",
"gpu": 12.5,
"gpu_info": "NVIDIA GeForce RTX 3060",
"arch": "x86_64",
"os": "Ubuntu 22.04",
"region": "HK",
"ip_v4": "1",
"ip_v6": "1",
"boot_time": "1700000000000",
"last_updated": 1737638400000,
"timestamp": 1737638400000,
"sysConfig": { "show_long_history": true }
}失败返回:
400 { "error": "Missing ID" }缺少id404 { "error": "Server not found" }不存在 / 被隐藏(未登录访问时)
Request
- Method:
GET - Path:
/api/history/all - Query:
id(必填):服务器 UUIDhours(可选,默认 24):浮点,最大 168(7 天)
- Headers(按需):同
/api/servers
Response 200 OK
{
"columns": ["timestamp", "cpu", "gpu", "gpu_info", "..."],
"rows": [
{ "timestamp": 1737600000000, "cpu": 12.3, ... },
{ "timestamp": 1737600600000, "cpu": 13.1, ... }
]
}采样间隔(自动)
| hours 范围 | 采样点间隔 |
|---|---|
| > 168 | 实际取 168h(7 天),步长 80 分钟 |
| 96 ~ 168 | 60 分钟 |
| 48 ~ 96 | 40 分钟 |
| 24 ~ 48 | 15 分钟 |
| 12 ~ 24 | 10 分钟 |
| 6 ~ 12 | 5 分钟 |
| 1 ~ 6 | 1 分钟 |
| ≤ 1 | 10 秒 |
历史查询使用
ROW_NUMBER() OVER (PARTITION BY ts/interval ORDER BY ts)取每个采样窗口的第一条。
跨月查询:当 cutoff 早于当月 1 号且存在 metrics_history_old 表时,自动 UNION ALL 两张表。
缓存:命中内存缓存时返回 X-Cache: HIT,反之 MISS。TTL 取决于 hours:
| hours | TTL |
|---|---|
| ≥ 120 | 10 分钟 |
| ≥ 60 | 5 分钟 |
| ≥ 30 | 3 分钟 |
| < 30 | 1 分钟 |
未登录限制:hours > 1 时强制 401。
数据库升级提示:当 D1 缺少新字段时返回:
HTTP 409
{ "code": "DATABASE_UPGRADE_REQUIRED" }此时应先调用 POST /updateDatabase。
Request
- Method:
GET(必须带Upgrade: websocketHeader) - Path:
/api/ws - Query:
subscribe(可选,默认all):all→ 订阅所有服务器的最新指标(批量合并推送,每 5 秒一次)<serverId>→ 只订阅指定服务器(实时推送)
Response 101 Switching Protocols(WebSocket 握手)
握手 Header 要求:
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: <base64>
Sec-WebSocket-Version: 13
推送策略:
| 订阅类型 | 推送方式 | 消息类型 | 说明 |
|---|---|---|---|
subscribe=all |
批量合并,每 5 秒一次 | batchUpdate |
减少消息数量,降低前端渲染压力 |
subscribe=<serverId> |
实时推送 | batchUpdate |
单台服务器详情页,低延迟,统一消息格式 |
subscribe=all默认不推送任何服务器更新。客户端应先调用/api/servers获取当前可见服务器列表,再通过 WebSocket 通道发送subscribe消息,使用servers[].id作为过滤列表。该过滤是客户端订阅范围控制,不是服务端鉴权。
服务端 → 客户端消息:
- 连接成功(Hello)
{ "type": "hello", "ts": 1737638400000, "subscribed": "all" } - 指标更新(统一使用
batchUpdate,subscribe=all和subscribe=<serverId>均支持){ "type": "batchUpdate", "ts": 1737638400000, "updates": [ { "serverId": "9b2c...", "samples": [ { "ts": 1737638398000, "data": { /* Server 对象 */ } }, { "ts": 1737638399000, "data": { /* Server 对象 */ } } ] }, { "serverId": "a1f3...", "samples": [ { "ts": 1737638398500, "data": { /* Server 对象 */ } } ] } ] }
客户端 → 服务端消息(可选):
{ "type": "subscribe", "scope": "all", "ids": ["server-001", "server-002"] }
{ "type": "ping" } // → 服务端回 { "type": "pong", "ts": ... }
{ "type": "pong" } // 静默忽略subscribe 消息用于更新当前 WebSocket 的订阅范围:
scope:可选,默认沿用 URL 中的subscribe,通常为allids:可选数组,来自/api/servers返回的servers[].id;subscribe=all时仅推送这些 ID 的更新。最多 500 个,每个 ID 长度 1-64,仅允许字母、数字、.、_、:、-
若 scope 或 ids 格式非法,服务端会关闭 WebSocket 连接(close code 1008)。
服务端确认消息:
{ "type": "subscribed", "ts": 1737638400000, "subscribed": "all", "count": 2 }失败返回:
503 { "error": "WebSocket not enabled", "code": 503 }—— 未绑定METRICS_BROADCASTERDurable Object426 Expected WebSocket upgrade request—— 缺少Upgrade: websocket头
前端使用示例(subscribe=all,批量推送):
const { servers } = await (await fetch('/api/servers')).json();
const ids = servers.map(s => s.id);
const ws = new WebSocket('wss://status.example.com/api/ws?subscribe=all');
ws.onopen = () => {
ws.send(JSON.stringify({ type: 'subscribe', scope: 'all', ids }));
};
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data);
if (msg.type === 'batchUpdate') {
for (const u of msg.updates) {
// 更新对应 serverId 的卡片
updateServer(u.serverId, u.data);
}
}
};前端使用示例(subscribe=serverId,实时推送):
const ws = new WebSocket('wss://status.example.com/api/ws?subscribe=server-001');
ws.onmessage = (ev) => {
const msg = JSON.parse(ev.data);
if (msg.type === 'batchUpdate') {
for (const u of msg.updates) {
for (const s of u.samples) {
updateServer(u.serverId, s.data);
}
}
}
};所有管理操作都通过这一个端点 +
action字段路由。
Request
- Method:
POST - Path:
/admin/api - Headers(除
login外必填):Content-Type: application/json Authorization: Bearer <jwt> - Body(JSON):
{ "action": "<one of: login|get_settings|list|d1_usage|save_settings|add|delete|batch_delete|edit|save_order>", ...payload }
Turnstile:
- 仅
action: login启用 Turnstile 验证(请求头X-Turnstile-Token) - 其他 action:不走 Turnstile 流程(白名单 bypass)
Response:统一 200 OK,Content-Type: application/json,具体结构见下文各小节。
Request
{
"action": "login",
"username": "admin",
"password": "<plain text>"
}Header:X-Turnstile-Token: <token>(当 site_options.turnstile_enabled === 'true' 时必填)
Response 200
{
"success": true,
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJhZG1pbiIsImlhdCI6MTczNzYzODQwMCwiZXhwIjoxNzM4MjQzMjAwfQ.signature",
"message": "loginSuccessful"
}Response 失败
400 { "error": "Missing username or password" }401 { "error": "Invalid username or password" }403 { "error": "Turnstile verification failed" }
Request
{ "action": "get_settings" }Response 200
{
"success": true,
"settings": { /* Settings 对象,见 5.4 */ },
"api_secret": "<env.API_SECRET>"
}
api_secret仅在get_settings中返回,方便前端展示/复制。
Request
{ "action": "list" }Response 200
{
"success": true,
"servers": [ /* Server[],包含 is_hidden、is_online 等所有字段 */ ],
"stats": {
"total": 10,
"online": 8,
"offline": 2,
"total_cpu": 96.3,
"total_ram": 360.5,
"total_disk": 280.1,
"total_net_in": 12345.6,
"total_net_out": 7890.1,
"avg_cpu": "12.04",
"avg_ram": "45.06",
"avg_disk": "35.01"
}
}| 字段 | 说明 |
|---|---|
is_online |
true = 最近 5 分钟内有上报 |
last_updated |
最近一次上报时间戳(毫秒) |
stats.avg_* |
仅按在线服务器平均,保留 2 位小数(字符串) |
注意:本接口包含
is_hidden=1的服务器(与/api/servers不同)。
Request
{ "action": "d1_usage" }前置条件:site_options.cloudflare_token 与 site_options.cloudflare_account_id 必须已配置。
Response 200
{
"success": true,
"usage": {
"today": {
"date": "2025-12-31",
"rowsRead": 12345,
"rowsWritten": 678,
"readLimit": 5000000,
"writeLimit": 100000,
"readRemaining": 4987655,
"writeRemaining": 99322,
"workersRequests": 1234,
"workersRequestLimit": 100000,
"workersRequestRemaining": 98766,
"databaseCount": 1,
"accountId": "<cloudflare_account_id>"
},
"last24Hours": {
"date": "2025-12-30 ~ 2025-12-31",
"rowsRead": 23456,
"rowsWritten": 789,
"readLimit": 5000000,
"writeLimit": 100000,
"workersRequests": 2345,
"workersRequestLimit": 100000,
"databaseCount": 1,
"accountId": "<cloudflare_account_id>"
}
}
}Response 失败
400 { "error": "请先配置 Cloudflare Token" }/400 { "error": "请先配置 Cloudflare 用户 ID / Account ID" }400 { "error": "<Cloudflare GraphQL 错误信息>" }
通过 Cloudflare GraphQL API(
https://api.cloudflare.com/client/v4/graphql)查询:
d1AnalyticsAdaptiveGroups(rowsRead/rowsWritten)workersInvocationsAdaptive(requests)
Request
{
"action": "save_settings",
"settings": {
"site_title": "My Server Monitor",
"custom_bg": "https://...",
"custom_head": "<style>...</style>",
"custom_script": "console.log('hi');",
"appearance_options": {
"theme_options": {
"a": 1,
"b": 2
}
},
"is_public": "true",
"show_price": "true",
"show_expire": "true",
"show_tf": "true",
"show_long_history": "true",
"tg_notify": "false",
"tg_bot_token": "",
"tg_chat_id": "",
"turnstile_enabled": "false",
"turnstile_site_key": "",
"turnstile_secret_key": "",
"jwt_secret": "",
"username": "admin",
"password": "<plain text, will be PBKDF2-hashed before save>",
"cloudflare_account_id": "",
"cloudflare_token": "",
"custom_ct": "gd-ct-dualstack.ip.zstaticcdn.com",
"custom_cu": "gd-cu-dualstack.ip.zstaticcdn.com",
"custom_cm": "gd-cm-dualstack.ip.zstaticcdn.com",
"custom_bd": "lf3-ips.zstaticcdn.com",
"expire_reminder": "false"
}
}字段分类:
APPEARANCE_FIELDS(写入appearance_optionsJSON):site_title、custom_bg、custom_head、custom_script、csp_static、csp_api、display_mode、theme_optionsSITE_FIELDS(写入site_optionsJSON):上表除 appearance 之外的全部- 任何未列出的字段会被忽略
特殊处理:
password:以明文传入;后端用 PBKDF2-HMAC-SHA-256(50,000 iterations、16 字节 salt、32 字节 hash)计算后保存为pbkdf2_sha256$50000$<salt hex>$<hash hex>;如传空字符串则不更新密码;旧版 32 位 MD5 哈希仍可登录并会在成功登录后自动升级
Response 200
{ "success": true, "message": "updateSuccess" }副作用:清空
site_options内存缓存,下一次请求会从 DB 重新加载。
Request
{ "action": "add", "name": "New Server", "server_group": "Default" }Response 200
{
"success": true,
"id": "<newly generated UUID v4>",
"message": "serverAdded"
}约束:
name:1 ~ 100 字符,否则400 { "error": "服务器名称无效" }server_group:默认Defaultsort_order:自动 =MAX(sort_order) + 1
Request
{
"action": "edit",
"id": "<server UUID>",
"name": "HK-01", // 可选,1~100 字符
"server_group": "HK", // 默认 "Default"
"price": "¥30/月", // 字符串
"expire_date": "2026-12-31",
"traffic_limit": "1TB",
"traffic_calc_type": "total", // total | ...
"reset_day": 1, // 1 ~ 31
"collect_interval": 1, // 秒
"report_interval": 60, // 秒
"is_hidden": "0" // "0" | "1"
}Response 200
{ "success": true, "message": "serverUpdated" }Response 失败
400 { "error": "服务器 ID 无效" }—— UUID 格式错500 { "error": "Update failed. Please go to Database Management and click \"Upgrade Database\" to migrate the new field." }—— DB 缺字段,请先/updateDatabase
Request
{ "action": "delete", "id": "<server UUID>" }副作用:级联删除该 server 的全部 metrics_history 记录。
Response 200
{ "success": true, "message": "serverDeleted" }Request
{ "action": "batch_delete", "ids": ["<uuid1>", "<uuid2>", "<uuid3>"] }Response 200
{ "success": true, "message": "batchDeleted" }Request
{ "action": "save_order", "orders": ["<uuid1>", "<uuid2>", "<uuid3>"] }说明:
orders[i]表示该 UUID 排序后应为第i位(sort_order = i)- 服务端会逐条
UPDATE sort_order = ? WHERE id = ?
Response 200
{ "success": true, "message": "sortOrderSaved" }以下端点需 JWT 鉴权(
Authorization: Bearer <token>),不参与 Turnstile。
用于老版本升级时补齐
metrics_history与servers表的字段、并清理废弃 settings。
Request
- Method:
POST - Path:
/updateDatabase - Headers:
Authorization: Bearer <jwt>
Response 200
{
"success": true,
"message": "databaseUpgradeSuccess",
"results": [
{ "name": "metrics_history load -> load_avg 迁移", "success": true, "migrated": 1234, "message": "..." },
{ "name": "servers 表列更新", "success": true, "added": 5 },
{ "name": "servers 表多余字段清理", "success": true, "cleaned": 30, "message": "..." },
{ "name": "metrics_history 表列更新", "success": true, "added": 14 },
{ "name": "metrics_history 写入优化", "success": true, "optimized": 0, "message": "..." },
{ "name": "废弃 settings key 清理", "success": true, "cleaned": 0 },
{ "name": "删除弃用的 metrics_aggregated 表", "success": true, "dropped": 0, "message": "..." }
]
}失败返回:500 + error 字段(任一步骤抛错时整体失败)。
危险操作:会删除 ``metrics_history
/metrics_history_old` 全部数据后重建。
Request
- Method:
POST - Path:
/clearHistory - Headers:
Authorization: Bearer <jwt>
Response 200
{ "success": true, "message": "databaseRebuiltSuccess" }Request
- Method:
GET - Path:
/__do/health - Headers:无需鉴权
Response 200
{ "ok": true, "subscribers": 3 }或
{ "ok": false, "reason": "DO not bound" }
{ "ok": false, "reason": "<error message>" }| 字段 | 类型 | 说明 |
|---|---|---|
id |
string (UUID) | 主键 |
name |
string | 显示名 |
server_group |
string | 分组 |
price |
string | 价格文本(自由格式) |
expire_date |
string | 到期日 YYYY-MM-DD |
traffic_limit |
string | 流量上限文本 |
traffic_calc_type |
string | total / 其他 |
reset_day |
number | 流量重置日 1~31 |
collect_interval |
number | 采集间隔(秒) |
report_interval |
number | 上报间隔(秒) |
is_hidden |
string "0"/"1" |
是否在前台隐藏 |
sort_order |
number | 排序值(越小越靠前) |
cpu |
number | 最新 CPU%(来自最新指标) |
load_avg |
string | "x x x" |
net_in_speed |
number | B/s |
net_out_speed |
number | B/s |
net_rx |
number | 累计下行字节 |
net_tx |
number | 累计上行字节 |
net_rx_monthly |
number | 当月累计下行字节 |
net_tx_monthly |
number | 当月累计上行字节 |
processes |
number | 进程数 |
tcp_conn |
number | TCP 连接数 |
udp_conn |
number | UDP 套接字数 |
ping_ct / ping_cu / ping_cm / ping_bd |
number|null | 各运营商延时 (ms) |
loss_ct / loss_cu / loss_cm / loss_bd |
number|null | 各运营商丢包率 (%) |
ram_total / ram_used |
number | MB |
swap_total / swap_used |
number | MB |
disk_total / disk_used |
number | MB |
cpu_cores |
number | 逻辑核心数 |
cpu_info |
string | CPU 型号 |
gpu |
number|null | GPU 占用% |
gpu_info |
string | GPU 型号 |
arch |
string | 架构 |
os |
string | OS 名称 |
agent_version |
string | 最新一次上报的探针版本号 |
region |
string | 区域代码(大写,兼容 ISO 国家码) |
ip_v4 |
string "0"/"1" |
IPv4 可达性 |
ip_v6 |
string "0"/"1" |
IPv6 可达性 |
boot_time |
string | 启动时间(毫秒) |
last_updated / timestamp |
number | 上报时间戳(毫秒) |
is_online |
boolean | 5 分钟内是否有上报(仅 list 接口计算) |
sysConfig |
object | 站点级开关(仅部分接口附带) |
见 §1.1 metrics 字段表。所有数值字段都是字符串(除了
gpu),方便 Bash 探针组装 JSON。
| 字段 | 类型 | 说明 |
|---|---|---|
timestamp |
number (ms) | 采样时间 |
| 其余字段 | 视查询 columns 而定 | 当前 /api/history/all 固定返回:cpu, gpu, gpu_info, disk_total, disk_used, processes, net_in_speed, net_out_speed, tcp_conn, udp_conn, ping_ct, ping_cu, ping_cm, ping_bd, loss_ct, loss_cu, loss_cm, loss_bd, swap_total, swap_used, load_avg |
get_settings直接返回site_optionsJSON 的全部字段(包括jwt_secret、cloudflare_token等敏感字段!请妥善保存并通过 HTTPS 调用)。
{
site_title: string,
custom_bg: string,
custom_head: string, // 注入到 </head> 之前
custom_script: string, // 注入到 </body> 之前
display_mode: 'bar' | 'ring' | 'table',
theme_options: Record<string, unknown>,
is_public: 'true' | 'false',
show_price: 'true' | 'false',
show_expire: 'true' | 'false',
show_tf: 'true' | 'false',
show_long_history: 'true' | 'false',
tg_notify: 'true' | 'false',
tg_bot_token: string,
tg_chat_id: string,
turnstile_enabled: 'true' | 'false',
turnstile_site_key: string,
turnstile_secret_key: string,
jwt_secret: string, // 长度 ≥ 32 才会被用于签 JWT
username: string,
password: string, // PBKDF2 哈希值;旧版 MD5 哈希会在成功登录后自动升级
cloudflare_account_id: string,
cloudflare_token: string,
custom_ct: string, // 电信测速节点 host[:port]
custom_cu: string, // 联通 host[:port]
custom_cm: string, // 移动 host[:port]
custom_bd: string, // BGP host[:port]
expire_reminder: 'true' | 'false'
}type |
方向 | Payload |
|---|---|---|
hello |
S → C | { ts: number, subscribed: string } |
subscribe |
C → S | { scope: string, ids: string[] } |
subscribed |
S → C | { ts: number, subscribed: string, count: number } |
ping |
S → C | { ts: number } |
pong |
双向 | { ts: number } |
update |
S → C | { serverId: string, ts: number, data: <Server> } |
Worker 同时注册了 cron 触发器(scheduled handler),可在 wrangler.toml 配置:
| Cron | 行为 | 备注 |
|---|---|---|
*/1 * * * * |
每分钟:检测离线节点 | checkOfflineNodes(通知) |
0 * * * * |
每小时:根据 UTC 日期分支 | 见下表 |
| 每周日 0 点:表轮换 | weeklyCleanup(删除旧表、重命名 metrics_history → metrics_history_old、创建新表) |
|
| 每天 12 点:服务器到期检测 | checkExpiringServers |
DEBUG 模式(env.DEBUG=1)下额外提供:
0 0 * * 0→ weeklyCleanup0 12 * * *→ checkExpiringServers
| code | 名称 | 触发条件 |
|---|---|---|
| 400 | Bad Request | 缺参数 / 非法 UUID / 未知 action / 缺 Cloudflare 配置 |
| 401 | Unauthorized | JWT 失败 / Basic 失败 / 站点非公开未登录 / 探针 secret 错 |
| 403 | Forbidden | Turnstile 失败 |
| 404 | Not Found | 服务器不存在 / WebSocket DO 未绑定 |
| 409 | Conflict | DATABASE_UPGRADE_REQUIRED(D1 缺字段) |
| 500 | Internal Server Error | 未捕获异常 / DB 抛错 |
| 503 | Service Unavailable | WebSocket 未启用 |
假设部署在
https://status.example.com,API_SECRET=abc123,服务器 ID 为9b2c4d3e-1a2b-4c5d-9e8f-7a6b5c4d3e2f。
curl -X POST https://status.example.com/update \
-H "Content-Type: application/json" \
-d '{
"id":"9b2c4d3e-1a2b-4c5d-9e8f-7a6b5c4d3e2f",
"secret":"abc123",
"metrics":{
"cpu":"12.34","ram_total":"8192","ram_used":"3700",
"swap_total":"2048","swap_used":"100",
"disk_total":"102400","disk_used":"32000",
"load_avg":"0.10 0.20 0.30","boot_time":"1700000000000",
"net_rx":"12345678","net_tx":"87654321",
"net_rx_monthly":"1073741824","net_tx_monthly":"536870912",
"net_in_speed":"1024","net_out_speed":"512",
"os":"Ubuntu 22.04","arch":"x86_64","cpu_info":"Intel Xeon","cpu_cores":"4",
"processes":"256","tcp_conn":"32","udp_conn":"4",
"ip_v4":"1","ip_v6":"1",
"ping_ct":"23","ping_cu":"25","ping_cm":"30","ping_bd":"40"
}
}'curl https://status.example.com/api/configcurl https://status.example.com/api/serverscurl "https://status.example.com/api/server?id=9b2c4d3e-1a2b-4c5d-9e8f-7a6b5c4d3e2f"curl "https://status.example.com/api/history/all?id=9b2c4d3e-1a2b-4c5d-9e8f-7a6b5c4d3e2f&hours=24"curl -X POST https://status.example.com/admin/api \
-H "Content-Type: application/json" \
-H "X-Turnstile-Token: <token>" \
-d '{"action":"login","username":"admin","password":"abc123"}'TOKEN="eyJhbGc..."
curl -X POST https://status.example.com/admin/api \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"action":"list"}'curl -X POST https://status.example.com/admin/api \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"action":"add","name":"HK-02","server_group":"HK"}'curl -X POST https://status.example.com/admin/api \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"action":"edit","id":"9b2c4d3e-1a2b-4c5d-9e8f-7a6b5c4d3e2f","price":"¥35/月","expire_date":"2027-01-01"}'curl -X POST https://status.example.com/admin/api \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"action":"delete","id":"9b2c4d3e-1a2b-4c5d-9e8f-7a6b5c4d3e2f"}'curl -X POST https://status.example.com/admin/api \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{
"action":"save_settings",
"settings":{
"site_title":"My Status",
"is_public":"true",
"show_long_history":"true",
"turnstile_enabled":"true",
"turnstile_site_key":"1x00000000000000000000AA",
"turnstile_secret_key":"1x0000000000000000000000000000000AA"
}
}'curl -X POST https://status.example.com/admin/api \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TOKEN" \
-d '{"action":"d1_usage"}'curl -X POST https://status.example.com/updateDatabase \
-H "Authorization: Bearer $TOKEN"curl https://status.example.com/__do/health# 订阅所有服务器
wscat -c "wss://status.example.com/api/ws?subscribe=all"
# 建连后发送:{"type":"subscribe","scope":"all","ids":["server-id"]}
# 订阅指定服务器
wscat -c "wss://status.example.com/api/ws?subscribe=9b2c4d3e-1a2b-4c5d-9e8f-7a6b5c4d3e2f"- v1.x:当前文档对应
src/index.js、src/handlers/*、src/database/schema.js主线实现。 - Breaking change:
/admin/api由GET?action=...改为POST {action:...}模式,Token 校验与 Turnstile 走 Header 通道。 - CORS:通过
CORS_ALLOWED_ORIGINS环境变量开启;不配置时所有跨域请求会失败。 - JWT:
jwt_secret推荐配置为 ≥ 32 位的随机字符串;未配置时回退到API_SECRET派生,部署后强烈建议显式配置。 - 数据库升级:升级到新字段(如
loss_*、net_rx_monthly、reset_day等)后请调用POST /updateDatabase;否则历史接口可能返回409 DATABASE_UPGRADE_REQUIRED。
文档同步:与源码
src/index.js、src/handlers/{admin,dashboard,frontend,update}.js、src/durable/MetricsBroadcaster.js、src/utils/{auth,settings,errors,cors,cache,metrics,common}.js、src/database/{schema,updateDatabase}.js一一对应;后续修改任一文件时,请同步更新本文件。