Skip to content

Latest commit

 

History

History
1447 lines (1149 loc) · 54.5 KB

File metadata and controls

1447 lines (1149 loc) · 54.5 KB

CF-Server-Monitor 后端 API 文档

面向 CF-Server-Monitor 后端(Cloudflare Workers + D1 + Durable Objects)的完整 REST / WebSocket API 参考。 本文档覆盖所有公开端点、内部端点、鉴权机制、错误码、数据结构与 WebSocket 实时推送协议。

Base URLhttps://<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. 通用规范

0.1 鉴权机制

项目使用 三套并行的鉴权机制,按接口范围区分使用。

A. 探针 Secret(Agent → Worker)

  • 使用位置POST /update
  • 方式:请求体字段 secret
  • :必须等于 Worker 环境变量 API_SECRET
  • 失败返回401 { "error": "Invalid secret", "code": 401 }

B. Basic Auth(管理登录 → JWT)

  • 使用位置POST /admin/apiaction: login
  • 方式:请求体字段 username / password(后端内部组装 Basic base64(user:pass) 进行校验)
  • 校验顺序
    1. site_options.password 已设置为 PBKDF2 格式 → 按 pbkdf2_sha256$iterations$salt$hash 校验
    2. site_options.password 为旧版 32 位 MD5 → 按 MD5 兼容校验,成功后自动升级为 PBKDF2
    3. site_options.password 未设置或为空 → 与 API_SECRET 直接比对
    4. 用户名:若 site_options.username 已设置则用之,否则使用 API_USER_NAME 环境变量,最终回退为 admin
  • 失败返回401 { "error": "Invalid username or password", "code": 401 }

C. JWT Bearer(管理操作 → 后续管理请求)

  • 使用位置:所有非 loginPOST /admin/apiPOST /updateDatabasePOST /clearHistory
  • 方式Authorization: Bearer <token> Header
  • Token 签发HS256 JWT,默认有效期 604800 秒(7 天)
  • 签名密钥(优先级):
    1. site_options.jwt_secret(长度 ≥ 32)
    2. API_SECRET(不够 32 字符时 padEnd'x' 后取前 64 位)
    3. 回退常量:'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。

0.2 Turnstile 人机验证

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-TokenX-Turnstile-Verified 时(用于初始化判断是否需要验证)

验证流程

  1. 首次访问:客户端从 /api/config 拿到 turnstile_site_key
  2. 前端渲染 Turnstile 组件 → 拿到一次性 token
  3. 后续请求在 Header 增加:
    X-Turnstile-Token: <token from cloudflare>
    
  4. Worker 用 site_options.turnstile_secret_key 调用 https://challenges.cloudflare.com/turnstile/v0/siteverify 验证。
  5. 验证成功后,Worker 通过 X-Turnstile-Verified 这个 加密 Header 给客户端发"已验证凭证"(AES-GCM 加密、API_SECRET/TURNSTILE_SECRET_KEY 派生密钥、有效期 3600 秒),后续可省略 X-Turnstile-Token
  6. 客户端也可以把 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 }

0.3 统一响应格式

成功响应

{
  // 业务字段,结构因接口而异
  "success": true,
  ...
}

注:项目里"成功响应"是直接 JSON.stringify 业务对象,没有固定的 code 字段。HTTP 状态码始终为 200

成功响应特例

  • POST /update → 纯文本 OKContent-Type: text/plain
  • WebSocket 升级 → 101 Switching Protocols

错误响应

{
  "error": "human readable message",
  "code": 400
}

code 字段是 HTTP 状态码的镜像,便于前端无需读取 status 即可分流。

0.4 统一错误码

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)

0.5 限流与配额

  • Cloudflare Workers / D1 的硬性限额由 Cloudflare 平台强制(D1:500 万行读 / 10 万行写 / 日;Workers:10 万次请求 / 日)。
  • /admin/api?action=d1_usage 可查询当前账户当日用量与近 24h 用量。

0.6 CORS

环境变量 CORS_ALLOWED_ORIGINS逗号分隔的源白名单,例如:

CORS_ALLOWED_ORIGINS=https://status.example.com,https://admin.example.com
  • 当请求 Origin 命中白名单 → 响应带 Access-Control-Allow-Origin: <origin>Access-Control-Allow-Credentials: trueVary: Origin
  • 预检请求 OPTIONS → 直接返回 204,并回显 Access-Control-Request-Method / Access-Control-Request-Headers,缓存 86400 秒。
  • 未配置或未命中 → 不会下发 CORS Header,浏览器侧会被同源策略拦截。

1. 探针上报接口

1.1 POST /update - 指标上报(Agent 入口)

调用方:服务器侧探针(Bash install.sh / Windows cf-server-monitor.pyw)。 鉴权secret 字段 == env.API_SECRET Turnstile:不参与

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
    OK
    
    Content-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=2
    
    Content-Type: application/x-www-form-urlencoded; charset=utf-8
  • 动态配置的字段范围、规范化及客户端校验规则详见 AGENT_CONFIG.md
  • 失败:
    { "error": "Invalid secret", "code": 401 }
    { "error": "Server not found", "code": 404 }

副作用

  1. metrics_history 只写入本次请求中最新的一个样本,避免 1 秒采集时放大 D1 写入次数。
  2. 触发 Durable Object MetricsBroadcaster 内部广播,统一发送 {type:"batchUpdate", ts, updates:[...]} 格式,前端按样本时间逐个回放。
  3. 写入 request.cf.country(或 cf-ipcountry Header)作为该条记录的 region 字段(统一转大写)。

2. 公开 API(前端/管理端共用)

以下接口除 /api/ws 外,若 site_options.is_public !== 'true'必须携带 JWT(Authorization: Bearer <token>)才能访问。 命中 Turnstile 时需带 X-Turnstile-TokenX-Turnstile-Verified

2.1 GET /api/config - 获取站点配置

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(加密串)。


2.2 GET /api/servers - 获取服务器列表(首页)

Request

  • Method:GET
  • Path:/api/servers
  • Headers(按需):Authorization: Bearer <jwt>X-Turnstile-TokenX-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

2.3 GET /api/server - 获取单台服务器详情

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" } 缺少 id
  • 404 { "error": "Server not found" } 不存在 / 被隐藏(未登录访问时)

2.4 GET /api/history/all - 获取历史指标

Request

  • Method:GET
  • Path:/api/history/all
  • Query:
    • id必填):服务器 UUID
    • hours(可选,默认 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


2.5 GET /api/ws - WebSocket 实时推送

Request

  • Method:GET必须Upgrade: websocket Header)
  • 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 作为过滤列表。该过滤是客户端订阅范围控制,不是服务端鉴权。

服务端 → 客户端消息

  1. 连接成功(Hello)
    { "type": "hello", "ts": 1737638400000, "subscribed": "all" }
  2. 指标更新(统一使用 batchUpdatesubscribe=allsubscribe=<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,通常为 all
  • ids:可选数组,来自 /api/servers 返回的 servers[].idsubscribe=all 时仅推送这些 ID 的更新。最多 500 个,每个 ID 长度 1-64,仅允许字母、数字、._:-

scopeids 格式非法,服务端会关闭 WebSocket 连接(close code 1008)。

服务端确认消息:

{ "type": "subscribed", "ts": 1737638400000, "subscribed": "all", "count": 2 }

失败返回

  • 503 { "error": "WebSocket not enabled", "code": 503 } —— 未绑定 METRICS_BROADCASTER Durable Object
  • 426 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);
      }
    }
  }
};

3. 管理端 API(鉴权)

3.1 POST /admin/api - 管理操作入口

所有管理操作都通过这一个端点 + 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 OKContent-Type: application/json,具体结构见下文各小节。


3.2 action: login - 登录

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" }

3.3 action: get_settings - 读取全部设置

Request

{ "action": "get_settings" }

Response 200

{
  "success": true,
  "settings": { /* Settings 对象,见 5.4 */ },
  "api_secret": "<env.API_SECRET>"
}

api_secret 仅在 get_settings 中返回,方便前端展示/复制。


3.4 action: list - 列出全部服务器(含在线/统计)

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 不同)。


3.5 action: d1_usage - D1 / Workers 用量

Request

{ "action": "d1_usage" }

前置条件site_options.cloudflare_tokensite_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)查询:

  • d1AnalyticsAdaptiveGroupsrowsRead / rowsWritten
  • workersInvocationsAdaptiverequests

3.6 action: save_settings - 保存设置

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_options JSON):site_titlecustom_bgcustom_headcustom_scriptcsp_staticcsp_apidisplay_modetheme_options
  • SITE_FIELDS(写入 site_options JSON):上表除 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 重新加载。


3.7 action: add - 新增服务器

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:默认 Default
  • sort_order:自动 = MAX(sort_order) + 1

3.8 action: edit - 修改服务器信息

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

3.9 action: delete - 删除服务器

Request

{ "action": "delete", "id": "<server UUID>" }

副作用:级联删除该 server 的全部 metrics_history 记录。

Response 200

{ "success": true, "message": "serverDeleted" }

3.10 action: batch_delete - 批量删除

Request

{ "action": "batch_delete", "ids": ["<uuid1>", "<uuid2>", "<uuid3>"] }

Response 200

{ "success": true, "message": "batchDeleted" }

3.11 action: save_order - 保存服务器排序

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" }

4. 系统维护端点

以下端点需 JWT 鉴权(Authorization: Bearer <token>),不参与 Turnstile。

4.1 POST /updateDatabase - 数据库迁移

用于老版本升级时补齐 metrics_historyservers 表的字段、并清理废弃 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 字段(任一步骤抛错时整体失败)。


4.2 POST /clearHistory - 清空历史数据

危险操作:会删除 ``metrics_history/metrics_history_old` 全部数据后重建。

Request

  • Method:POST
  • Path:/clearHistory
  • Headers:Authorization: Bearer <jwt>

Response 200

{ "success": true, "message": "databaseRebuiltSuccess" }

4.3 GET /__do/health - Durable Object 健康检查

Request

  • Method:GET
  • Path:/__do/health
  • Headers:无需鉴权

Response 200

{ "ok": true, "subscribers": 3 }

{ "ok": false, "reason": "DO not bound" }
{ "ok": false, "reason": "<error message>" }

5. 数据结构

5.1 Server 对象

字段 类型 说明
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 站点级开关(仅部分接口附带)

5.2 Metrics 对象(探针上报 payload)

§1.1 metrics 字段表。所有数值字段都是字符串(除了 gpu),方便 Bash 探针组装 JSON。

5.3 History Row 对象

字段 类型 说明
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

5.4 Settings 对象

get_settings 直接返回 site_options JSON 的全部字段(包括 jwt_secretcloudflare_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'
}

5.5 WebSocket 消息

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> }

6. 定时任务 (Cron)

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 → weeklyCleanup
  • 0 12 * * * → checkExpiringServers

7. 错误码速查表

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 未启用

8. 完整 cURL 示例

假设部署在 https://status.example.comAPI_SECRET=abc123,服务器 ID 为 9b2c4d3e-1a2b-4c5d-9e8f-7a6b5c4d3e2f

8.1 探针上报

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"
    }
  }'

8.2 公共:获取配置

curl https://status.example.com/api/config

8.3 公共:首页服务器列表

curl https://status.example.com/api/servers

8.4 公共:单台详情

curl "https://status.example.com/api/server?id=9b2c4d3e-1a2b-4c5d-9e8f-7a6b5c4d3e2f"

8.5 公共:24h 历史

curl "https://status.example.com/api/history/all?id=9b2c4d3e-1a2b-4c5d-9e8f-7a6b5c4d3e2f&hours=24"

8.6 管理:登录

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"}'

8.7 管理:列表(需 JWT)

TOKEN="eyJhbGc..."
curl -X POST https://status.example.com/admin/api \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"action":"list"}'

8.8 管理:添加服务器

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"}'

8.9 管理:编辑

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"}'

8.10 管理:删除

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"}'

8.11 管理:保存设置

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"
    }
  }'

8.12 管理:D1 用量

curl -X POST https://status.example.com/admin/api \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"action":"d1_usage"}'

8.13 系统:数据库迁移

curl -X POST https://status.example.com/updateDatabase \
  -H "Authorization: Bearer $TOKEN"

8.14 健康检查

curl https://status.example.com/__do/health

8.15 WebSocket(使用 wscat / websocat)

# 订阅所有服务器
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"

9. 版本与变更说明

  • v1.x:当前文档对应 src/index.jssrc/handlers/*src/database/schema.js 主线实现。
  • Breaking change/admin/apiGET?action=... 改为 POST {action:...} 模式,Token 校验与 Turnstile 走 Header 通道。
  • CORS:通过 CORS_ALLOWED_ORIGINS 环境变量开启;不配置时所有跨域请求会失败。
  • JWTjwt_secret 推荐配置为 ≥ 32 位的随机字符串;未配置时回退到 API_SECRET 派生,部署后强烈建议显式配置。
  • 数据库升级:升级到新字段(如 loss_*net_rx_monthlyreset_day 等)后请调用 POST /updateDatabase;否则历史接口可能返回 409 DATABASE_UPGRADE_REQUIRED

文档同步:与源码 src/index.jssrc/handlers/{admin,dashboard,frontend,update}.jssrc/durable/MetricsBroadcaster.jssrc/utils/{auth,settings,errors,cors,cache,metrics,common}.jssrc/database/{schema,updateDatabase}.js 一一对应;后续修改任一文件时,请同步更新本文件。