OrbitShieldOrbitShield

接口文档

通过开放 API(OpenAPI)以编程方式管理你的站点。所有请求使用 HMAC-SHA256 签名鉴权。

概述

在控制台「API 管理」创建 API 应用,获得一对 APP_ID / APP_SECRET。APP_SECRET 仅在创建/重置时展示一次,请妥善保存。每个应用可设为「只读」或「读写」,其作用域为该应用所属账号下的站点。

API 基础地址

http
https://api.orbitshield.com

请替换为平台提供的 API 域名。所有接口均需 HTTPS。

认证与签名(HMAC-SHA256)

每个请求需附带以下请求头。服务端用你的 APP_SECRET 重算签名比对,并校验时间戳偏差在 ±300 秒内(防重放)。

请求头

X-Api-IdAPP_ID
X-Api-Timestampunix seconds
X-Api-Noncerandom string per request
X-Api-Signhex( HMAC-SHA256(APP_SECRET, stringToSign) )

签名算法

text
stringToSign =
  METHOD    + "\n" +
  path      + "\n" +   // e.g. /api/v1/open/sites
  rawQuery  + "\n" +   // e.g. page=1&pageSize=20  (empty if none)
  timestamp + "\n" +
  nonce     + "\n" +
  hex( sha256(body) )   // body = "" if no request body

X-Api-Sign = hex( HMAC-SHA256(APP_SECRET, stringToSign) )

path 为不含域名与查询串的路径;rawQuery 为原始查询串(无则空);body 为原始请求体(无则空串)。换行符为 \n。

快速开始

1. 在控制台「API 管理」创建应用,拿到 APP_ID / APP_SECRET。

2. 用下面任一语言构造签名并发起请求(示例:列出你的站点)。

Node.js

javascript
import crypto from "node:crypto";

const API_BASE = "https://api.orbitshield.com";
const APP_ID = "ak_xxxxxxxxxxxx";
const APP_SECRET = "your_app_secret";

async function callOpenApi(method, path, { query = "", body = "" } = {}) {
  const ts = Math.floor(Date.now() / 1000).toString();
  const nonce = crypto.randomBytes(8).toString("hex");
  const bodyHash = crypto.createHash("sha256").update(body).digest("hex");
  const stringToSign = [method, path, query, ts, nonce, bodyHash].join("\n");
  const sign = crypto.createHmac("sha256", APP_SECRET).update(stringToSign).digest("hex");

  const url = API_BASE + path + (query ? "?" + query : "");
  const res = await fetch(url, {
    method,
    headers: {
      "X-Api-Id": APP_ID,
      "X-Api-Timestamp": ts,
      "X-Api-Nonce": nonce,
      "X-Api-Sign": sign,
      "Content-Type": "application/json",
    },
    body: body || undefined,
  });
  return res.json();
}

// List sites
console.log(await callOpenApi("GET", "/api/v1/open/sites", { query: "page=1&pageSize=20" }));

Python

python
import time, os, hmac, hashlib, secrets, json, urllib.request

API_BASE = "https://api.orbitshield.com"
APP_ID = "ak_xxxxxxxxxxxx"
APP_SECRET = b"your_app_secret"

def call(method, path, query="", body=""):
    ts = str(int(time.time()))
    nonce = secrets.token_hex(8)
    body_hash = hashlib.sha256(body.encode()).hexdigest()
    string_to_sign = "\n".join([method, path, query, ts, nonce, body_hash])
    sign = hmac.new(APP_SECRET, string_to_sign.encode(), hashlib.sha256).hexdigest()
    url = API_BASE + path + (("?" + query) if query else "")
    req = urllib.request.Request(url, data=body.encode() if body else None, method=method)
    req.add_header("X-Api-Id", APP_ID)
    req.add_header("X-Api-Timestamp", ts)
    req.add_header("X-Api-Nonce", nonce)
    req.add_header("X-Api-Sign", sign)
    req.add_header("Content-Type", "application/json")
    return json.load(urllib.request.urlopen(req))

print(call("GET", "/api/v1/open/sites", "page=1&pageSize=20"))

cURL (bash)

bash
#!/usr/bin/env bash
API_BASE="https://api.orbitshield.com"
APP_ID="ak_xxxxxxxxxxxx"
APP_SECRET="your_app_secret"

METHOD="GET"; PATH_="/api/v1/open/sites"; QUERY="page=1&pageSize=20"; BODY=""
TS=$(date +%s)
NONCE=$(openssl rand -hex 8)
BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 | awk '{print $2}')
STS=$(printf '%s\n%s\n%s\n%s\n%s\n%s' "$METHOD" "$PATH_" "$QUERY" "$TS" "$NONCE" "$BODY_HASH")
SIGN=$(printf '%s' "$STS" | openssl dgst -sha256 -hmac "$APP_SECRET" | awk '{print $2}')

curl -s "$API_BASE$PATH_?$QUERY" \
  -H "X-Api-Id: $APP_ID" \
  -H "X-Api-Timestamp: $TS" \
  -H "X-Api-Nonce: $NONCE" \
  -H "X-Api-Sign: $SIGN"

接口列表

所有接口作用域为应用所属账号的站点;标注「读写」的接口要求应用为读写权限。

方法路径权限说明
GET/api/v1/open/sites只读列出账号下所有站点
GET/api/v1/open/sites/{id}/config只读获取指定站点配置
PUT/api/v1/open/sites/{id}/config读写全量更新站点配置(乐观并发,回传 version)
PATCH/api/v1/open/sites/{id}/config读写局部更新:只提交改动字段,后端按 JSON Merge Patch 深合并
POST/api/v1/open/refresh读写刷新(清除)缓存
POST/api/v1/open/preheat读写预热缓存

响应格式

json
{ "code": 0, "message": "ok", "data": { /* ... */ } }

成功返回 code=0;失败返回非 0 code 与 message。

站点配置参数(SiteConfig)

GET /sites/{id}/config 返回、PUT /sites/{id}/config 提交的都是同一个完整配置对象,含 6 个分组:origin(源站)、cache(缓存)、compress(压缩)、https、security(安全)、advanced(高级)。

重要:更新为「整份覆盖」,请按读-改-写流程

两种方式:① PUT 整份覆盖——先 GET 拿完整配置与 version,改后带 version 整份提交。② PATCH 局部更新(推荐小改动)——只提交改动的字段/段(如 {"https":{"http2":true},"version":N}),后端读当前配置按 JSON Merge Patch 深合并,未提交字段保持不变;数组提供即整体替换,值为 null 表示删除该键。两者都按 version 做乐观并发,冲突返回 409。

origin源站

字段类型说明
schemestring回源协议:http | https | follow(协议跟随)
balancestring负载均衡:roundrobin(轮询) | chash(源地址哈希) | cookie
balanceArgstringbalance=cookie 时的 cookie 名
nodesNodeCfg[]源站节点数组(见下表)
passHoststring回源 Host:pass(透传) | rewrite(改写为 hostValue)
hostValuestringpassHost=rewrite 时的 Host 值
snistringHTTPS 回源 SNI(空=用回源 Host)
rangebool透传 Range(分片/断点续传回源)
timeoutint回源超时(秒)
healthHealthCfg主动健康检查(见下表)
keepalivebool回源长连接

origin.nodes[]源站节点 NodeCfg

字段类型说明
hoststring源站 IP 或域名
portint端口
weightint权重
rolestringmain(主) | backup(备,主故障转移)
linestring线路名(如 主线路/备线路)

origin.health健康检查 HealthCfg

字段类型说明
failsint连续失败几次摘除(0=关)
intervalint探测间隔(秒)
timeoutint单次探测超时(秒)

cache缓存

字段类型说明
enablebool是否开启缓存
rulesCRuleCfg[]缓存规则数组(见下表)
ignoreCacheControlbool忽略源站 Cache-Control,按规则 TTL
ignoreUrlParamsbool缓存键忽略 URL query
httpsSharedboolhttp/https 共享同一缓存键

cache.rules[]缓存规则 CRuleCfg

字段类型说明
typestring匹配类型:suffix(后缀) | dir(目录) | full(完整路径)
contentstring匹配值(如 js|css|png 或 /static/ 或 /a.html)
ttlint缓存时长(秒),0=不缓存
prioint优先级(数字小者优先)

compress压缩

字段类型说明
gzipstring[]开启 gzip 的 MIME 列表;空数组=关闭

httpsHTTPS

字段类型说明
certIdstring绑定证书 ID(证书由证书接口/控制台管理)
forceHttpsbool强制 HTTPS(http 301 跳 https)
http2bool开启 HTTP/2
hstsbool开启 HSTS
minTlsstring最低 TLS 版本:1.0 | 1.1 | 1.2 | 1.3

security安全(各子项为 null=不启用)

字段类型说明
wafobject|nullWAF:{mode: observe|intercept|ban|off, level: normal|strict, ai, scope: smart|all, categories, exceptions}
ccobject|nullCC 防护:{mode: off|default|normal|strict|captcha, threshold, window}
refererobject|null防盗链:{mode: white|black, list[]}
ipRuleobject|nullIP 黑白名单:{mode: white|black, cidrs[]}
regionobject|null区域封禁:{mode: white|black, areas[](国家/省市码)}
originProtectobject|null源站保护:{qps, window}
aclobject|null精准访问控制(多规则集,结构较复杂,建议控制台配置后经 GET 获取)

advanced高级

字段类型说明
websocketbool开启 WebSocket 透传
respHeaders{name,value}[]自定义响应头
reqHeaders{name,value}[]自定义回源请求头
errorPagesobject[]源站错误页:{status, mode: origin|html|url|charity404, html?, url?}
blockPageobject|null安全拦截页:{mode, ...}
redirectsobject[]URL 重定向:{type: prefix|exact|regex, match, target, code:301/302/307/308, keepQuery}
rewritesobject[]URL 重写(透明改回源 URI):{type, match, target}
mobileRedirectobject|null移动端跳转:{enable, url}
dropEtagbool移除响应头 ETag

示例:开启 HTTP/2 + 强制 HTTPS(节选 https 段)

json
"https": {
  "certId": "12",
  "forceHttps": true,
  "http2": true,
  "hsts": true,
  "minTls": "1.2"
}

示例:更新源站(节选 origin 段)

json
"origin": {
  "scheme": "https",
  "balance": "roundrobin",
  "passHost": "rewrite",
  "hostValue": "origin.example.com",
  "sni": "",
  "range": true,
  "timeout": 30,
  "keepalive": true,
  "nodes": [
    { "host": "1.2.3.4", "port": 443, "weight": 1, "role": "main", "line": "主线路" }
  ],
  "health": { "fails": 3, "interval": 10, "timeout": 5 }
}

以上为节选;实际 PUT 请提交 GET 返回的完整对象(含未改动的其它分组)+ version。

错误码

HTTP场景
400参数/请求体格式错误
401缺少或无效签名、时间戳超出 ±300s、APP_ID 无效或应用已禁用
403只读应用调用写接口,或无权限
404站点不存在或不属于该应用账号
409配置版本冲突(已被其他会话修改)