Edgeweir
参考

API 与端点

控制台 HTTP 端点、公开 API 的认证与错误格式,以及节点通道概要。

端点

以下路径位于 Web 端口(PORT,默认 3000),仅 ROLE=app、all 提供。

路径认证用途
/api/v1/*x-api-key公开 API(OpenAPI);请求中的 Cookie 被剥离
/api/v1/openapi.json无OpenAPI 文档
/rpc/*会话 Cookie + x-csrf-token: orpcWeb UI 专用(oRPC);请求中的 x-api-key 被剥离
/api/auth/*按端点better-auth 白名单端点,其余返回 404
/healthz无健康检查
/install.sh无节点安装脚本
/downloads/*无发布文件镜像;未设置 EDGEWEIR_DOWNLOADS_DIR 时返回 404
其他路径无Web UI

/api/*、/rpc/*、/downloads/*、/install.sh、/healthz 下未匹配的请求返回 404 与 {"error":"not found"},不回退到 Web UI。

公开 API

/api/v1 与 /rpc 由 packages/contract 中的同一份 oRPC 契约生成。OpenAPI 文档位于 /api/v1/openapi.json,其 servers 为 <EDGEWEIR_PUBLIC_URL>/api/v1;系统设置 的「OpenAPI」链接到该文档。

curl -fsS https://cdn-admin.example.com/api/v1/openapi.json

认证

  • 请求头 x-api-key: <key>:AccessKey(ewk_ 开头)或服务账号 key(ews_ 开头)。
  • AccessKey 以运营者(初始化向导创建的唯一账户)身份调用,可调用的过程由其权限范围决定,见 AccessKey。服务账号 key 只能调用服务账号中列出的过程。
  • 密钥无效、已吊销或缺失:401。
  • 每个 AccessKey 连续最多 600 次请求:距上一次请求超过 60 秒时计数清零。超出返回 429 API_KEY_RATE_LIMITED,data.retryAfterSeconds 为需等待的秒数。持续轮询用服务账号 key,服务账号 key 不计数。
  • 无需密钥的过程(OpenAPI 中 security: []):GET /system/status、POST /system/setup。
  • GET /me 返回调用方:{ user: { id, name, email, twoFactorEnabled }, serviceAccount };使用 AccessKey 时 serviceAccount 为 null。

AccessKey

在 个人设置 → AccessKey(用户菜单)中管理;也可在已登录的会话中经 /rpc 调用 accessKeys.*。

操作位置说明
创建填写「名称」(最长 64 字符),选择「权限范围」,点击「创建」默认「读写」。密钥只显示一次。只能在已登录的控制台会话中创建;经 /api/v1 用密钥调用 POST /access-keys 返回 403 ACCESS_KEY_SESSION_REQUIRED。
查看密钥列表;GET /api/v1/access-keys前缀、权限范围、状态、最后使用时间
吊销「吊销密钥」;DELETE /api/v1/access-keys/{id}吊销后使用该密钥的请求返回 401;列表中保留并标记「已吊销」
权限范围可调用的过程
只读GET 过程,以及 POST /rules/validate;其他方法返回 403 ACCESS_KEY_READ_ONLY
读写全部过程,创建 AccessKey 除外

未设置权限范围的密钥按读写处理。创建与吊销写入审计日志(api_key.create、api_key.revoke);经 AccessKey 执行的操作在审计日志中的操作者类型为 api_key。better-auth 的 /api/auth/api-key/* 端点不开放,返回 404。

服务账号

服务账号是供集成调用 /api/v1 的机器身份。它不能登录:没有密码、passkey 或会话,只有 key。在 系统设置 → 服务账号 中管理。

操作说明
新建、编辑名称(最长 64 字符,唯一)、scope、启用状态。停用后该账号的全部 key 返回 401
新建 keykey 以 ews_ 开头,只显示一次;只保存 SHA-256。列表显示前缀与最后使用时间(精度 1 分钟)
吊销 key吊销后使用该 key 的请求返回 401
删除同时删除全部 key

以上变更写入审计日志(service_account.create、service_account.update、service_account.delete、service_account.key_create、service_account.key_revoke);服务账号执行的操作在审计日志中的操作者类型为 service_account。服务账号 key 只在 /api/v1 生效。

服务账号只能调用下表列出的过程,每个过程需要对应的 scope:

过程端点scope
system.statusGET /system/status—
account.meGET /me—
dns.catalogGET /dns/catalog—
settings.getGET /settingssystem:read
clusters.list、clusters.getGET /clusters、GET /clusters/{id}clusters:read
sites.list、sites.getGET /sites、GET /sites/{id}sites:read
sites.launchGET /sites/{id}/launchsites:read
sites.setEnabledPUT /sites/{id}/enabledsites:write
dns.siteTargetGET /sites/{siteId}/cnamesites:read
usage.list、usage.changesGET /usage、GET /usage/changesusage:read
情况响应
缺少 scope403 SCOPE_REQUIRED,data.scope 为所需的 scope
过程不在上表403 SERVICE_ACCOUNT_FORBIDDEN
key 无效、已吊销,或账号已停用401
修改需要集群活动节点缺少的能力409 NODE_CAPABILITY_REQUIRED,见节点能力

GET /me 对服务账号返回 serviceAccount: { id, name, scopes },user 为服务账号的 id 与名称(email 为空,twoFactorEnabled 为 false)。scope 只用于服务账号;AccessKey 使用只读 / 读写权限范围。

幂等键

/api/v1 的 POST、PUT、PATCH 接受请求头 Idempotency-Key:1–255 个可打印 ASCII 字符,可写成 RFC 8941 字符串("key")或原样。

情况响应
第一次请求正常执行,保存方法、路径(含查询串)、请求体 SHA-256 与最终响应
同一调用方、同一 key、同一请求返回保存的响应(状态码与响应体),带 Idempotent-Replayed: true
同一 key、不同的方法、路径或请求体422 IDEMPOTENCY_KEY_MISMATCH
第一次请求仍在执行409 IDEMPOTENCY_IN_PROGRESS
key 格式无效400 IDEMPOTENCY_KEY_INVALID
响应里有只显示一次的凭据:POST /access-keys、POST /service-accounts/{id}/keys、POST /enrollment-tokens、POST /probe-tokens400 IDEMPOTENCY_KEY_UNSUPPORTED,不执行;不带该请求头重新发送
  • key 按调用方区分:全部 AccessKey 共用一份,每个服务账号单独一份。
  • 保存 24 小时,每小时清理过期记录。
  • 5xx 响应不保存,调用方可以用同一个 key 重试;401 与 429(未进入过程)也不保存。4xx 响应保存,重放返回同一错误。
  • 执行中的记录 10 分钟后视为中断(控制台实例崩溃),下一个请求接管并重新执行。
  • GET 与 DELETE 忽略该请求头。

乐观并发

以下写操作接受可选的 expectedUpdatedAt(ISO 8601,调用方读取时的 updatedAt)。值不一致时返回 409 UPDATED_AT_MISMATCH,data.updatedAt 为当前值。

过程资源的 updatedAt
sites.setEnabled网站
clusters.setRolloutPolicy集群金丝雀策略的 policyUpdatedAt(发布与金丝雀进度不改变它;传入当时读取的 updatedAt 也可,前提是此后没有任何变化)
l4Apps.update、l4Apps.setEnabledL4 应用

网站或 L4 应用已经是请求的启停状态时,sites.setEnabled、l4Apps.setEnabled 直接返回当前状态,不比较 expectedUpdatedAt。

站点启停

PUT /sites/{id}/enabled(过程 sites.setEnabled),请求体 {"enabled":false}。运营者(会话或读写 AccessKey)与 sites:write 服务账号可以调用。

  • 停用的网站不下发到节点,节点对其域名的 HTTP 请求返回 503(X-Edgeweir-Error: site-disabled),HTTPS 请求在 TLS 握手时失败;DNS 记录保留;证书续期继续,HTTP-01 挑战照常应答。
  • 状态有变化时生成新的配置版本(原因码 site_enabled、site_disabled)并写审计(site.enable、site.disable);没有变化时返回当前状态,不生成版本、不写审计。
  • 对停用的网站清缓存或预热:409 SITE_DISABLED。
  • 响应为 { site, revision };site.enabled 为当前状态。

网站生效与上线检查

网站(sites.list、sites.get 与各写操作返回的 site)带 delivery:

字段说明
statepending:没有在线节点运行该网站;partial:部分在线节点运行旧版本或数据面异常;live:全部在线节点运行最新版本;disabled:已停用
totalNodes网站所在集群的在线活动节点数
servingNodes其中已应用的配置含该网站(任意版本)的节点数;停用的网站为仍在运行它的节点数
currentNodes其中运行该网站最新版本(含金丝雀候选版本)且数据面正常的节点数
canary集群的配置金丝雀让非金丝雀节点在窗口内继续运行该网站的旧版本时为 { endsAt, autoPromote }(窗口结束时间;autoPromote 为 false 时等待手动晋升),否则为 null

新建网站(POST /sites)时 name 可省略,默认为第一个域名(超过 100 字符时截断)。

GET /sites/{id}/launch(过程 sites.launch)当场解析网站的每个域名,耗时可达数秒:

字段说明
addresses集群的边缘地址(A / AAAA 记录的值):在线活动节点的主调度地址,节点配置了调度地址时用配置的地址,否则用节点上报的公网地址;IPv4 在前
domains[]name(网站上的写法,泛域名为 *.example.com)、probe(实际解析的名称;泛域名解析其下的固定名称 edgeweir-check.example.com)、pointing
domains[].pointingok:解析到的全部地址都属于集群的活动节点(配置的地址或上报的公网地址,含备用地址与离线节点);elsewhere:有地址不属于;unresolved:没有 A / AAAA 记录;unknown:解析失败(超时等),或集群节点没有已知地址
certificatestate:none(网站没有证书)、covered(证书链覆盖全部域名)、uncovered(不覆盖 uncovered 中的域名)、issuing(ACME 签发排队或进行中)、failed(上次签发失败,error 为失败代码)、expired;另有 id、name、uncovered、error
delivery同上

节点能力

修改后的配置需要集群中某个活动节点缺少的能力(节点在 supportedFeatures 中上报,如 challenge-v1、modsecurity-v1)时:

调用方结果
运营者(会话或 AccessKey)保存并发布;缺少能力的节点保留原配置,在 集群与节点 中显示「需要升级」
服务账号409 NODE_CAPABILITY_REQUIRED,data.features 为缺少的能力(逗号分隔),data.nodes 为缺少能力的节点(前 5 个,逗号分隔,更多时以 +N 结尾);修改不保存

控制台的自动任务发布配置时与服务账号受同样的限制。升级节点见节点升级。

封禁

过程端点
bans.listGET /bans
bans.createPOST /bans
bans.deleteDELETE /bans/{id}
settings.bans、settings.setBansGET、PUT /settings/bans

服务账号不能调用这些过程(403 SERVICE_ACCOUNT_FORBIDDEN);只读 AccessKey 只能调用 GET。范围 scope:site 针对一个网站,platform 针对全部网站(界面显示为「全局」)。

请求字段
POST /bansscope(site / platform)、siteId(site 必须带,platform 不能带)、cidr(IP 地址或 CIDR)、reason(abuse、attack、scanner、spam、other)、durationSeconds(60–604800)
GET /bans查询参数 scope、siteId、source(manual / auto)、address(IP 或 CIDR,列出覆盖它或在它之内的封禁;无效时 400 BAN_INVALID_CIDR)、page、pageSize(1–100,默认 50)
PUT /settings/bansmaxTotal(100–100000,默认 10000)、shareAutoBans(默认 true)

列表响应 { items, total },只含有效的封禁(未到期、未解封),按创建时间倒序。封禁字段:

字段说明
id、scope、cidrcidr 为规范化的 CIDR,如 203.0.113.7/32
reason、sourcesource 为 manual 或 auto;自动封禁的 reason 为 cc_ip_rate
siteId、siteNameplatform 封禁为 null
node、trigger自动封禁的来源节点 { id, name } 与触发条件 { metric, observed, threshold, windowSeconds };手动封禁为 null
createdBy手动封禁的操作者 { type, id, name }
createdAt、expiresAtISO 8601
seq封禁变化序号(十进制字符串)
distributed是否下发到节点;未共享的自动封禁为 false
unappliedNodes上报未能保存该封禁的在线节点数
  • 同一范围、网站与地址已有有效的手动封禁时,create 更新 reason 与到期时间并返回同一 id,审计 ban.update;否则审计 ban.create。delete 解除任一有效封禁(手动或自动),审计 ban.delete。
  • maxTotal 是有效手动封禁的上限,网站与全局封禁合计;续期不计入新增。shareAutoBans 决定自动封禁是否下发到同一集群的其他节点。setBans 审计 system.bans_update。
  • 节点的封禁状态:GET /nodes/{id} 的 banStatus(appliedSequence、entries、capacity、unappliedIds、unapplied、kernelEntries、autoEvicted、reportedAt),节点未上报时为 null;节点能力见 supportedFeatures(bans-v1、kernel-ban-v1)。
错误代码状态场景
BAN_INVALID_CIDR400不是 IP 地址或 CIDR
BAN_PREFIX_TOO_SHORT400前缀短于 /16(IPv4)或 /48(IPv6);data.min 为下限
BAN_EXPIRY_OUT_OF_RANGE400durationSeconds 不在 60–604800
BAN_PROTECTED_ADDRESS400包含节点地址、回环或未指定地址,或与「放行」类 IP 名单重叠;data.address 为冲突的地址
BAN_PLATFORM_LIMIT409有效的手动封禁达到 maxTotal;data.limit
BAN_NOT_FOUND404封禁不存在、已到期或已解封
SITE_NOT_FOUND404siteId 对应的网站不存在
curl -fsS -X POST -H "x-api-key: $EDGEWEIR_API_KEY" -H 'content-type: application/json' \
  -d '{"scope":"platform","cidr":"203.0.113.0/24","reason":"attack","durationSeconds":86400}' \
  https://cdn-admin.example.com/api/v1/bans

行为见 封禁。

挑战与 CC 防护

过程端点
protection.getGET /sites/{id}/protection
protection.updatePATCH /sites/{id}/protection
security.stateGET /sites/{id}/security
security.eventsGET /sites/{id}/security/events
settings.protection、settings.setProtectionGET、PUT /settings/protection
settings.ccTemplate、settings.setCcTemplateGET、PUT /settings/cc-template

服务账号不能调用这些过程(403 SERVICE_ACCOUNT_FORBIDDEN);只读 AccessKey 只能调用 GET。挑战类型与 CC 级别取值:cookie302、js、pow、captcha(级别另有 normal)。

请求字段
PATCH /sites/{id}/protection只修改给出的字段:underAttack、underAttackChallenge、passTtlSeconds(300–86400)、powDifficulty(8–24)、powHighDifficulty(8–26,不低于 powDifficulty)、logJa4、cc(部分字段,合并到已保存的策略)
ccenabled、followTemplate、maxLevel、highPowInsteadOfCaptcha、windowSeconds(5–60)、siteQps、urlQps、ipQps(0–1000000,0 关闭该条件)、ipBanSeconds(60–86400)、originErrorPercent(0–100)、originErrorMinRequests、escalateAfterSeconds(1–3600)、cooldownSeconds(1–86400)
GET /sites/{id}/security查询参数 hours(1–168,默认 24)
GET /sites/{id}/security/events查询参数 kind(site_level / path_level / ip_banned)、page、pageSize(1–100,默认 50)
PUT /settings/protectionunderAttack(全局 Under Attack)、underAttackChallenge、eventRetentionDays(7–365,默认 30)
PUT /settings/cc-templatecc 中除 enabled、followTemplate 外的全部字段

响应:

过程内容
protection.get、protection.update上述字段,另有 siteId、cc(跟随模板时阈值为模板值)、ccTemplate(当前 CC 模板)、effectiveCc(节点使用的阈值,策略关闭时为 null)、platformUnderAttack(全局 Under Attack 是否开启)、updatedAt
security.statenodes:集群中每个活动节点的 { id, name, online, level, escalatedPaths, reportedAt };topIps、topPaths:近 hours 小时事件中的 { value, count }(各最多 10 个,近似值);hours
security.events{ items, total },按发生时间倒序;事件字段 id、node({ id, name },节点删除后为 null)、occurredAt、kind、level、previousLevel、path、address、metric、observed、threshold、topIps、topPaths
  • 修改发布网站所在集群的配置版本(原因 site_protection_updated),审计 site.protection_update;全局 Under Attack 变化时发布所有集群(platform_protection_updated),审计 system.protection_update;修改 CC 模板发布有网站跟随模板的集群(cc_template_updated),审计 system.cc_template_update。挑战密钥每日轮换发布 challenge_keys_rotated。
  • 挑战、Under Attack 与 CC 需要节点能力 challenge-v1,logJa4 另需 ja4-v1;集群有活动节点缺少时见节点能力。
错误代码状态场景
PROTECTION_POW_DIFFICULTY400powHighDifficulty 低于 powDifficulty;data.min 为最小允许值
SITE_NOT_FOUND404网站不存在
curl -fsS -X PATCH -H "x-api-key: $EDGEWEIR_API_KEY" -H 'content-type: application/json' \
  -d '{"underAttack":true,"underAttackChallenge":"pow","cc":{"enabled":true,"followTemplate":true}}' \
  https://cdn-admin.example.com/api/v1/sites/<网站 ID>/protection

行为见 挑战与 CC 防护。

压缩与 OWASP CRS

过程端点
https.get、https.updateGET、PUT /sites/{id}/https
https.checkGET /sites/{id}/https/check
sites.featuresGET /sites/{id}/features
waf.getGET /sites/{id}/waf
waf.updatePATCH /sites/{id}/waf
waf.topRulesGET /sites/{id}/waf/rules

服务账号不能调用这些过程(403 SERVICE_ACCOUNT_FORBIDDEN);只读 AccessKey 只能调用 GET。

请求字段
PUT /sites/{id}/httpssettings 替换网站的全部 HTTPS 设置,缺省字段取默认值;先 GET 再修改。压缩字段:brotli、brotliLevel(1–11,默认 6)、brotliMinLength、brotliTypes,zstd、zstdLevel(1–19,默认 3)、zstdMinLength、zstdTypes,gzip、gzipMinLength、gzipTypes;最小长度 1–1048576(默认 256),类型为 MIME 类型数组(最多 32 个)
PATCH /sites/{id}/waf只修改给出的字段:mode(off / detect / block)、paranoiaLevel(1–4)、anomalyThreshold(1–1000)、excludedRuleIds(900000–999999,不重复,最多 200 个;初始化与拦截判定规则 901xxx、949xxx、959xxx、980xxx 返回 400 WAF_RULE_NOT_EXCLUDABLE,data.ids 列出它们)、requestBodyLimit(0–134217728 字节)
GET /sites/{id}/waf/rules查询参数 range(1h / 6h / 24h / 7d / 30d,默认 24h)、limit(1–50,默认 10)
GET /sites/{id}/https/check查询参数 ca(letsencrypt / zerossl,默认 letsencrypt):检查其 CAA 许可的 CA

响应:

过程内容
sites.featuresbrotli、zstd、crs,各为 { available, reason };集群有活动节点缺少 brotli-v1 / zstd-v1 / modsecurity-v1 时 available 为 false、reason 为 nodes,否则 reason 为 null
waf.get、waf.updatesiteId、上述字段(excludedRuleIds 升序)、updatedAt(从未保存时为 null,此时为默认值:off、1、5、[]、131072)
waf.topRules{ approximate: true, items: [{ ruleId, requests }] },按命中次数倒序;只含检测规则,不含 901xxx、949xxx、959xxx、980xxx
https.checkrequest:一键启用 HTTPS 发送的申请(name、names、email、challenge、dnsCredentialId);blockers:全部阻碍,每项为 code 与参数:nodes_offline(cluster)、nodes_lack_http01(nodes)、dns_not_pointing(name、pointing:unresolved / elsewhere)、dns_credential_missing(names)、dns_credential_failed(credential、error:API 错误代码)、caa_forbidden(name);certificates:已签发、未过期且覆盖网站全部域名的证书 { id, name }
  • https.update 发布网站所在集群(原因 certificate_updated),审计 site.https_update;waf.update 发布(site_waf_updated),审计 site.waf_update。
  • POST /certificates/request 的 bindSiteId:证书签发后绑定到该网站(forceHttps 等其他设置不变),发布网站所在集群,审计 site.https_update(操作者 system);names 须覆盖网站全部域名(400 CERTIFICATE_DOMAIN_MISMATCH),每个网站同时只有一个这样的申请(409 CERTIFICATE_BUSY)。证书的 bindSiteId 在签发前为该网站 ID,签发后为 null。
  • available 为 false 时仍可经 API 开启,见节点能力。
  • 网站不存在:404 SITE_NOT_FOUND。
curl -fsS -X PATCH -H "x-api-key: $EDGEWEIR_API_KEY" -H 'content-type: application/json' \
  -d '{"mode":"block","paranoiaLevel":1,"excludedRuleIds":[920350]}' \
  https://cdn-admin.example.com/api/v1/sites/<网站 ID>/waf
curl -fsS -H "x-api-key: $EDGEWEIR_API_KEY" \
  'https://cdn-admin.example.com/api/v1/sites/<网站 ID>/waf/rules?range=1h'

行为见 HTTPS 与证书 与 OWASP CRS 托管规则。

清缓存、预热、源站与错误页

过程端点
cacheTasks.create、cacheTasks.get、cacheTasks.listPOST /cache-tasks、GET /cache-tasks/{id}、GET /cache-tasks
sites.purgeAllPOST /sites/{id}/purge:全站刷新,等同 POST /cache-tasks 的 {"type":"site","siteIds":["<网站 ID>"]},返回任务(不再发布配置版本、不再修改 cacheGeneration)
sites.updatePATCH /sites/{id}(originSettings、cacheSettings)
sites.originHealthGET /sites/{id}/origin-health
errorPages.getGET /sites/{id}/error-pages
errorPages.updatePUT /sites/{id}/error-pages
settings.errorPages、settings.setErrorPagesGET、PUT /settings/error-pages

服务账号不能调用这些过程(403 SERVICE_ACCOUNT_FORBIDDEN);只读 AccessKey 只能调用 GET。

请求字段
POST /cache-taskstype:url、prefix、site、prefetch、host、tag、sitemap。host:hosts(最多 500 个主机名,不带端口或通配符);tag:siteIds(1–100)与 tags(1–500,去首尾空格后按小写保存,每个 1–128 字节可打印 ASCII,不含逗号);sitemap:urls 恰好一个站点地图 URL、maxUrls(1–10000,默认 1000);prefetch 与 sitemap:variants(desktop / mobile,默认 ["desktop"])
PATCH /sites/{id}originSettings.activeHealthCheck:enabled、path、method(GET / HEAD)、expectedStatusMin、expectedStatusMax、host、intervalSeconds(5–300)、timeoutSeconds(1–60,不超过间隔)、healthyThreshold、unhealthyThreshold(1–10);originSettings.sessionAffinity:enabled、ttlSeconds(60–604800);originSettings.protocol(http1 / http2)、originSettings.grpc(只能在 http2 下为 true,否则 400 ORIGIN_GRPC_REQUIRES_HTTP2);cacheSettings.keepCacheTag。省略这五项时保持原值;originSettings、cacheSettings 的其他字段仍整体替换,先 GET 再修改
POST /sites、PATCH /sites/{id}origins[].hostHeader:空(跟随请求),或主机名、IP,可带端口:IPv6 带端口时写成 [2001:db8::1]:8443,不带端口时不加方括号;最长 259 字节,不含空白、引号、/、\。节点不接受的值返回 400 ORIGIN_HOST_HEADER_INVALID;已保存的值照常读出
PUT /sites/{id}/error-pagespages:[{ status, template }],status 为 403、429、502、503、504,各至多一个,template 1–65536 字节(UTF-8);interceptOriginErrors;可选 expectedUpdatedAt。整体替换。模板含 {{time}} 或 {{path}} 时配置要求节点能力 rules-v3
PUT /settings/error-pagesunknownHost、siteDisabled:模板,空字符串表示内置页面,每个最多 65536 字节;含 {{time}} 或 {{path}} 时所有集群的配置要求节点能力 rules-v3

响应:

过程内容
cacheTasks.*任务对象增加 variants(清缓存任务为 [])与 maxUrls(站点地图任务以外为 null);targets 为 Host、规范化后的标签或站点地图 URL;节点结果的错误码增加 sitemap_failed、sitemap_empty
sites.originHealth每个源站的 nodes 按节点和来源各一项,增加 source(passive / active);downNodes 统计有任一来源不健康的在线节点,每个节点计一次
sites.features增加 activeHealthCheck、sessionAffinity、originHttp2、errorPages、purgeByTag、prefetchVariants
errorPages.get、errorPages.updatesiteId、pages(按状态码排序)、interceptOriginErrors、updatedAt(从未保存时为 null)
logs.query、logs.export查询参数 requestId(精确匹配,最长 128);日志条目增加 requestId,CSV 增加 requestId 列
  • errorPages.update 发布网站所在集群(原因 site_error_pages_updated),审计 site.error_pages_update;settings.setErrorPages 发布所有集群(error_pages_updated),审计 system.error_pages_update。
错误代码状态场景
ORIGIN_HOST_HEADER_INVALID400源站的 hostHeader 不是节点接受的主机名或 IP(见上);data.hostHeader
CACHE_TASK_HOST_INVALID400Host 带端口、通配符或不是合法主机名;data.hosts
CACHE_TASK_TAG_INVALID400标签不符合规则;data.tags
CACHE_TASK_HOST_UNKNOWN400Host 或站点地图的 Host 不属于任何网站;data.hosts
NODE_CAPABILITY_REQUIRED409任务:集群内有活动节点缺少 purge-tag-v1(Host、标签)或 prefetch-v2(移动端、站点地图);data.features、data.nodes
ERROR_PAGE_TOO_LARGE400模板超过 65536 字节;data.status、data.limit(平台模板的 status 为 404 或 503)
curl -fsS -X POST -H "x-api-key: $EDGEWEIR_API_KEY" -H 'content-type: application/json' \
  -d '{"type":"tag","siteIds":["<网站 ID>"],"tags":["product-42"]}' \
  https://cdn-admin.example.com/api/v1/cache-tasks
curl -fsS -X POST -H "x-api-key: $EDGEWEIR_API_KEY" -H 'content-type: application/json' \
  -d '{"type":"sitemap","urls":["https://www.example.com/sitemap.xml"],"maxUrls":2000,"variants":["desktop","mobile"]}' \
  https://cdn-admin.example.com/api/v1/cache-tasks

行为见 源站与缓存 与 错误页。

缓存区、PURGE 方法、内容设置与维护模式

过程端点
clusters.setCachePUT /clusters/{id}/cache
nodes.setCachePUT /nodes/{id}/cache
maintenance.get、maintenance.updateGET、PUT /sites/{id}/maintenance

服务账号不能调用这些过程(403 SERVICE_ACCOUNT_FORBIDDEN);只读 AccessKey 只能调用 GET。

请求字段
PUT /clusters/{id}/cachemaxSizeGb(1–65536)、inactiveDays(1–90);发布集群(原因 cluster_cache_updated),审计 cluster.cache_update
PUT /nodes/{id}/cachemaxSizeGb(1–65536,null 跟随集群);发布节点所在集群(node_cache_updated),审计 node.cache_update
PUT /sites/{id}/maintenanceenabled、template(0–65536 字节,空为内置维护页)、retryAfterSeconds(0–86400)、allowedCidrs(最多 64 个,按 IP 名单的规则规范化:IPv4 映射前缀保存为 IPv4,短于 /96 的映射前缀被拒)、allowedPathPrefixes(以 / 开头,不含 ?、# 与控制字符,每个最长 1024 字节 UTF-8,最多 32 个)、可选 expectedUpdatedAt(不一致时 409 UPDATED_AT_MISMATCH);发布(site_maintenance_updated),审计 site.maintenance_update
POST /sites、PATCH /sites/{id}originSettings.tries(1–5,默认 3)、originSettings.statusRetry(默认 true);cacheSettings.cacheKey.query 增加 exclude,此时 queryParams 为去掉的参数,名称可以以 * 结尾;cacheSettings.xCache(默认 true);cacheSettings.purgeMethod:{ enabled, key? },key 为 16–256 个可打印字符,只写,省略时保留已保存的密钥,开启时没有密钥返回 400 PURGE_KEY_REQUIRED;cacheRules[].cacheSetCookie(默认 false);contentSettings:charset({ name, force, uppercase },name 为 off、utf-8、gbk、gb18030、gb2312、big5、iso-8859-1、shift_jis、euc-kr)、requestBodyLimit(字节,0–10737418240,默认 104857600,0 不限)。PATCH 省略 xCache、purgeMethod、originSettings.tries、originSettings.statusRetry 时保持原值
PUT /sites/{id}/https增加 gzipLevel(0–9,0 为节点默认)、compressMaxLength(字节,0 不限)
PUT /sites/{id}/error-pagesstatus 增加 400、401、404、405、410、500 与 "4xx"、"5xx";每页增加 redirectUrl(与 template 二选一,见跳转页面)与 responseStatus(200–599,0 不改;跳转页面只能为 0)
PUT /sites/{id}/rules配置动作增加 requestBodyLimit(字节,0–10737418240)

响应:

过程内容
clusters.list、clusters.get增加 cache: { maxSizeGb, inactiveDays }
nodes.list、nodes.get增加 cache: { maxSizeGb, usage }:maxSizeGb 为节点自己的容量(null 跟随集群),usage 为最近一次上报的 { usedBytes, maxBytes, measuredAt }(尚未上报时为 null)
sites.getcacheSettings.purgeMethod 为 { enabled, keySet },不返回密钥;增加 contentSettings
sites.features增加 siteContent
maintenance.get、maintenance.updatesiteId、上述字段、updatedAt(从未保存时为 null)
cacheTasks.*source 增加 purge_method(PURGE 请求创建,createdByName 为节点名)

用到新字段的配置需要节点能力 site-content-v1(节点自己的缓存容量需要 cache-zone-v1),见节点能力。行为见源站与缓存与错误页。

规则与批量重定向

过程端点
rules.get、rules.saveGET、PUT /sites/{id}/rules
platformRules.get、platformRules.save(全局规则)GET、PUT /platform-rules
rules.validatePOST /rules/validate
rules.topLogged(记录命中)GET /sites/{id}/rules/logged
bulkRedirects.getGET /sites/{id}/bulk-redirects
bulkRedirects.savePUT /sites/{id}/bulk-redirects

服务账号不能调用这些过程(403 SERVICE_ACCOUNT_FORBIDDEN);只读 AccessKey 只能调用 GET 与 POST /rules/validate。

请求字段
PUT /sites/{id}/rules、PUT /platform-rulesrules:整体替换,网站最多 64 条、平台最多 32 条;每条 id(可选;不是该网站或平台已有规则的 id 时重新生成,可直接保存从别处读取的规则)、name(1–100 字符)、phase、expression(最长 4096 字符)、enabled(默认 false:省略时规则保存为停用)、action。phase:request-transform、redirect、config、waf-custom、ratelimit、cache、origin、response-transform、compression
action(kind: "redirect")value(静态目标)与 target(值表达式)恰好填一个;statusCode(301、302、303、307、308,默认 301);preserveQuery(默认 false);setQuery([{ name, value, expression }],最多 16 个,名称不重复);removeQuery(参数名,最多 16 个,不能与 setQuery 重名)。参数名 [A-Za-z0-9._~-]{1,64},value 为可打印 ASCII,最长 256 字符;expression(值表达式,默认 "")不为空时 value 须为空,节点按请求求值后百分号编码
action(kind: "rewrite")同 redirect,没有 statusCode;preserveQuery 默认 true
action(kind: "request_header"、"response_header")header(1–64 个 token 字符,转为小写,不能是受保护头)、value(静态值,最长 4096 字符,不含控制字符)、expression(值表达式,默认 "",不为空时 value 须为空)、remove(默认 false,开启时 value 与 expression 为空);response_header 另有 append(默认 false,在已有同名头之外再加一行,不能与 remove 同时开启)。表达式算出的值超过 4096 字节或含控制字符时节点跳过该动作
action(kind: "config")至少一项。cacheBypass、forceHttps、gzip(布尔);只在 config 阶段:brotli、zstd、websocket、underAttack、ccEnabled(布尔),ccMaxLevel(cookie302、js、pow、captcha),originConnectTimeoutMs(100–120000),originSendTimeoutMs、originReadTimeoutMs(100–3600000),logSampleRate(0–10000,万分比)。省略的字段不覆盖
action(kind: "origin")origin 阶段。originGroup(网站的源站组,空为默认组;全局规则只能为空)、hostHeader(写法同源站的 hostHeader,空不覆盖)、sni(主机名,空不覆盖)、port(0–65535,0 不覆盖),至少修改一项
action(kind: "compression")compression 阶段。algorithms:zstd、br、gzip 中不重复的若干个,按优先顺序;[] 不压缩
POST /sites、PATCH /sites/{id}cacheRules[] 增加 expression(cache 阶段的条件,最长 16384 字符;为空时由 pathPrefixes、paths、extensions 生成;不为空时这三项为空或等于它的构建器形式)与 browserTtlSeconds(0–31536000,0 保留源站的 Cache-Control);origins[] 增加 group([a-z0-9_-]{0,32},空为默认组,至少一个源站在默认组)
PUT /sites/{id}/bulk-redirectsredirects:整体替换,最多 5000 条,source 不重复;每条 source(/路径 或 域名/路径,2–512 字节,不含空白、? 与控制字符,域名小写)、target(静态重定向目标,最长 1024 字节)、statusCode(默认 301)、preserveQuery(默认 false)
POST /rules/validateexpression(最长 16384 字符)、phase、kind:condition(默认,规则条件)、value(phase 阶段的值表达式:重定向目标、改写路径、报头值或查询参数值)、cacheRule(缓存规则条件,忽略 phase)
GET /sites/{id}/rules/loggedrange(1h、6h、24h(默认)、7d、30d)、limit(1–50,默认 10)

响应:

过程内容
rules.get、rules.save、platformRules.*规则数组,按保存顺序,带 id
rules.topLogged{ approximate: true, items: [{ ruleId, name, platform, requests }], unsupportedNodes }:网站的「记录」规则与全局「记录」规则命中的请求数,多的在前;name 为规则当前名称,规则已删除时为 null;platform 为全局规则;unsupportedNodes 为网站所在集群中不上报命中(缺少节点能力 rule-log-v1)的活动节点数
bulkRedirects.*[{ source, target, statusCode, preserveQuery }],按保存顺序
sites.get;sites.create、sites.update 的 sitecacheRules[] 总带 expression("true" 匹配所有请求);条件是构建器形状时 pathPrefixes、paths、extensions 为其结构化形式,否则为空;另有 browserTtlSeconds。origins[] 带 group
rules.validate{ valid, position, message, code?, params? };无效时 position 为出错的字符位置(从 0 计),message 为英文原因,code 为稳定的原因代码(如 unknown_field、ordered_comparison、expected_token),params 为原因中的值(如 expected_token 的 token)
sites.features增加 rulesV2、rulesV3;reason 为 nodes 时集群有活动节点缺少 rules-v2 或 rules-v3
  • rules.save 发布网站所在集群(原因 rules_updated),审计 site.rules_update;platformRules.save 发布所有集群,审计 platform.rules_update;bulkRedirects.save 发布网站所在集群(rules_updated),审计 site.bulk_redirects_update(条目数)。
  • 用到函数、新字段、值表达式、查询参数编辑、origin 或 compression 动作、config 阶段的新字段、gzip: true、非构建器形状的缓存规则条件、browserTtlSeconds、批量重定向或非默认源站组的配置要求节点能力 rules-v2。
  • 用到 rules-v3 字段(http.request.cookies[…]、http.request.uri.args[…]、http.referer、http.user_agent、http.request.version、http.request.scheme、http.request.id、http.request.timestamp.sec、edge.server_port、ip.geoip.as_name、http.response.cache_status)、函数(url_encode、base64_encode、base64_decode、md5、sha1、sha256、substring、to_string)、wildcard / strict wildcard、报头或查询参数的 expression、append、statusCode: 303,或错误页模板含 {{time}}、{{path}} 的配置要求节点能力 rules-v3。批量重定向的 statusCode 仍为 301、302、307、308。
  • rules.validate 的 code 增加 cookie_name、argument_name、integer_argument(params 为 min、max)。
错误代码状态场景
ORIGIN_HOST_HEADER_INVALID400origin 动作的 hostHeader 不是节点接受的主机名或 IP;data.hostHeader
RULE_INVALID400origin 动作选择了网站没有的源站组,或全局规则选择源站组;sites.update 移除仍被规则选择的源站组;已保存的规则或缓存规则条件无法编译
BULK_REDIRECT_HOST_UNKNOWN400域名/路径 来源的域名不是网站的域名(网站泛域名下一级的子域名可以);data.hosts(逗号分隔,最多 5 个)
IP_LIST_REFERENCE_UNKNOWN404规则或缓存规则条件引用的 IP 名单不存在;data.lists 为名单名称(前 5 个)
NODE_CAPABILITY_REQUIRED409集群内有活动节点缺少 rules-v2 或 rules-v3(服务账号与后台任务发布时);data.features、data.nodes
SITE_NOT_FOUND404网站不存在或不在调用方范围内
curl -fsS -X PUT -H "x-api-key: $EDGEWEIR_API_KEY" -H 'content-type: application/json' \
  -d '{"redirects":[{"source":"/old","target":"/new","statusCode":301,"preserveQuery":true}]}' \
  https://cdn-admin.example.com/api/v1/sites/<网站 ID>/bulk-redirects
curl -fsS -X POST -H "x-api-key: $EDGEWEIR_API_KEY" -H 'content-type: application/json' \
  -d '{"expression":"regex_replace(http.request.uri.path, \"^/old/\", \"/new/\")","phase":"redirect","kind":"value"}' \
  https://cdn-admin.example.com/api/v1/rules/validate

行为见 规则、IP 名单与 GeoIP 与 源站与缓存。

用量

每个网站、每个 UTC 5 分钟窗口 [windowStart, windowEnd) 一条记录,数值为该窗口内全部节点上报的分钟统计之和。

过程端点查询参数
usage.listGET /usagefrom、to(按 5 分钟对齐,UTC,半开区间)、siteId、cursor、limit(1–5000,默认 1000)
usage.changesGET /usage/changesafterSeq(默认 "0")、limit(1–5000,默认 1000)

记录字段:

字段说明
id<siteId>.<windowStart 的 Unix 秒>,同一网站同一窗口永远相同
siteId网站删除后记录仍保留
windowStart、windowEndISO 8601
requests、bytesSent、bytesReceived十进制整数字符串(出站、入站字节),超过 2^53 时仍精确
revision从 1 开始;重算后数值变化时加 1
seq全局单调递增(十进制字符串,可有间隔);创建或修订时分配
updatedAt最近一次写入时间
  • usage.list 按(窗口,网站)排序,响应 { items, nextCursor, completeUntil };nextCursor 为 null 时没有下一页。from、to 未对齐或 to 不晚于 from:400 USAGE_RANGE_INVALID;游标无效:400 USAGE_CURSOR_INVALID。
  • usage.changes 按 seq 返回 afterSeq 之后创建或修订的记录,响应 { items, lastSeq, completeUntil };下次以 lastSeq 作为 afterSeq。被修订的记录会再次出现。
  • 没有流量的窗口没有记录。
  • 窗口结束后每分钟重算一次;迟到数据改变数值时 revision 加 1 并分配新的 seq,数值不变时两者都不变。同一统计批次重复上报不改变结果。
  • 保留期默认 100 天,系统设置 → 用量 可调(35–400 天)。

completeUntil(ISO 8601 或 null):结束时间不晚于它的窗口,已包含当时所有活动节点的数据。

规则说明
节点水位节点在全部统计批次确认后上报 complete_until:最近一次成功取出统计时所在分钟的开始,之前的分钟都已上报。控制台不可达时节点照常把统计存入本地缓冲,节点退出前先存下包括当前分钟在内的统计;缓冲超出上限丢弃过统计时,水位停在丢弃的最早一分钟,24 小时后恢复
参与的节点状态为启用、且在离线阈值内上报过心跳的节点。阈值默认 60 分钟,系统设置 → 用量 可调(5–1440 分钟)
计算参与节点水位的最小值;从未上报水位的节点(旧版本节点)按注册时间计;落后超过离线阈值的节点按「当前时间减阈值」计(与离线节点一样,它之后补报的数据按修订处理);尚未重算的窗口不计入;向下取整到 5 分钟
单调只前移不后退。离线超过阈值的节点恢复后,它补报的更早窗口的数据按修订处理(revision 加 1)
停用或删除的节点不参与

集群与概览

过程端点说明
clusters.list、clusters.getGET /clusters、GET /clusters/{id}集群与其概要
clusters.rolloutGET /clusters/{id}/rollout配置金丝雀的策略、当前发布与金丝雀节点
clusters.rollbackPreviewGET /clusters/{id}/rollback-preview回滚到查询参数 revision 会发布的变化;按回滚的规则拒绝,不写入
overview.getGET /overview概览:集群、节点与网站数,最近发布,待处理事项
settings.nodeChannelCheckGET /settings/node-channel-check控制台对自己的节点通道地址做的 TLS 握手检查

clusters:read 服务账号可以调用 clusters.list、clusters.get;其他过程服务账号不能调用(403 SERVICE_ACCOUNT_FORBIDDEN)。都是 GET,只读 AccessKey 可以调用。

过程响应字段
clusters.list、clusters.getliveNodeCount:在线的已启用节点数;appliedNodeCount:其中运行目标版本的节点数(金丝雀进行中时,金丝雀节点的目标为候选版本,其他节点为稳定版本)
clusters.rolloutcandidateChanges:发布进行中时为 { sites: { added, changed, removed }, reasons },sites 为候选版本相对稳定版本新增、修改、移除的网站([{ id, name }]),reasons 为稳定版本之后发布的版本(与 GET /clusters/{id}/revisions 的元素相同,原因不重复);没有进行中的发布时为 null
clusters.rollbackPreview{ revision, currentRevision, unchanged, sites: { added, changed, removed } }:currentRevision 为集群最新版本(没有时为 null);unchanged 为 true 时内容与最新版本相同;sites 为相对最新版本的变化
overview.getattention:[{ kind, clusterId, clusterName, revision, at, count, version }],按下列 kind 的顺序排列,没有事项时为 []。kind:nodes_unhealthy、dns_failed、dns_blocked、upgrade_failed、canary_rolled_back、canary_awaiting_promotion、canary_running、nodes_lagging、nodes_no_address。revision:DNS 版本或候选版本;at:canary_running 的窗口结束时间、canary_rolled_back 的回滚时间;count:节点数;version:upgrade_failed 的目标版本。不适用的字段为 null、0 或空字符串
settings.nodeChannelCheck{ url, result, checkedAt }:url 为当前的节点通道地址;result 为 ok(证书链含节点通道 CA)、unreachable(3 秒内没有完成握手)、mismatch(出示了其他证书链,或地址不是 https)或 refused(系统设置中保存的地址解析到出站策略不允许的特殊用途地址,不连接)。结果缓存 30 秒,只作提示
错误代码状态场景
ROLLBACK_RESOURCE_UNAVAILABLE409rollbackPreview 与 rollback:所选版本引用的网站、域名、证书或 IP 名单已删除或不可用,或证书已过期
REVISION_NOT_FOUND404集群没有该版本
CLUSTER_NOT_FOUND404集群不存在

行为见集群与系统与接入节点。

节点通道地址

过程端点说明
settings.nodeChannelGET /settings/node-channel{ url, effectiveUrl, source }:url 为系统设置中保存的地址(未保存时为空字符串);effectiveUrl 为安装命令使用的地址;source 为 setting、environment(EDGEWEIR_NODE_API_URL)或 default
settings.setNodeChannelPUT /settings/node-channel请求体 { url }:https://主机[:端口],不含路径、查询参数、片段与账号,否则 400;保存为 origin 形式。空字符串清除保存的地址。响应同 settings.nodeChannel。立即生效,节点通道证书加入新地址的名称;写审计 system.node_channel_update

服务账号不能调用(403 SERVICE_ACCOUNT_FORBIDDEN);只读 AccessKey 只能调用 settings.nodeChannel。已注册的节点继续使用注册时的地址,见 节点通道地址与证书。

节点升级

过程端点说明
upgrades.releaseGET /node-releases/{version}发布源中该版本各架构的发布物
upgrades.latestVersionGET /node-upgrades/latest-version{ version }:发布源的最新版本,无法得知时为 null;缓存 10 分钟
upgrades.listGET /node-upgrades升级任务;查询参数 clusterId
upgrades.createPOST /node-upgrades{ version, nodeGroupId }:version 开头的 v 会被去掉,nodeGroupId 为先升级的节点组
upgrades.promotePOST /node-upgrades/{id}/promote推进剩余节点
upgrades.cancelPOST /node-upgrades/{id}/cancel取消「等待试运行组」与「待执行」的节点任务

服务账号不能调用这些过程(403 SERVICE_ACCOUNT_FORBIDDEN);只读 AccessKey 只能调用 GET。

错误代码状态场景
UPGRADE_RELEASE_UNAVAILABLE502发布源中读不到该版本的清单,或清单中没有支持的归档
UPGRADE_NODES_UNAVAILABLE409集群中有已启用节点不满足升级前提;data.nodes 列出这些节点(最多 10 个,其余为「+N」)
UPGRADE_CANARY_EMPTY409所选节点组没有已启用节点
UPGRADE_TOO_MANY_NODES409集群的已启用节点超过 data.limit(1000)
UPGRADE_BUSY409节点已有未完成的升级,或取消时有节点正在升级;data.nodes 列出这些节点
UPGRADE_FINISHED409取消已结束的升级任务
UPGRADE_NOT_READY409试运行组尚未满足推进条件
UPGRADE_NOT_FOUND404升级任务不存在

前提、状态与节点侧校验见节点升级。

区域探针、调度地址与智能调度

过程端点说明
probes.listGET /probes全部探针
probes.createTokenPOST /probe-tokens一次性探针注册令牌
probes.updatePATCH /probes/{id}改名、停用或启用
probes.deleteDELETE /probes/{id}删除探针并吊销其证书
probes.resultsGET /probe-results最新探测结果
settings.probes、settings.setProbesGET、PUT /settings/probes探测设置
nodes.setAddressesPUT /nodes/{id}/addresses节点的调度地址与级别
nodes.setProbePUT /nodes/{id}/probe节点兼任探针
scheduling.listGET /scheduling/rules调度规则;查询参数 clusterId(可选)
scheduling.createPOST /scheduling/rules新建规则,返回 201
scheduling.updatePATCH /scheduling/rules/{id}只修改给出的字段
scheduling.deleteDELETE /scheduling/rules/{id}删除规则;生效中的动作恢复
scheduling.previewGET /clusters/{clusterId}/scheduling/preview按当前指标预览全部规则,不写入

服务账号不能调用这些过程(403 SERVICE_ACCOUNT_FORBIDDEN);只读 AccessKey 只能调用 GET。POST /probe-tokens 不接受 Idempotency-Key(见幂等键)。

请求字段
POST /probe-tokensname(1–64 字符)、regionId、ttlMinutes(5–10080,默认 60)
PATCH /probes/{id}name(1–64 字符)、enabled,均可选。停用时删除该探针的结果
GET /probe-results查询参数 probeId(探针 ID,或兼任探针的节点 ID)、nodeId(被探测的节点),均可选
PUT /settings/probes整体替换:intervalSeconds(5–60)、timeoutMs(500–10000,不超过 intervalSeconds × 1000)、attempts(1–10)、lossPercent(1–100)、ipDownSeconds、ipUpSeconds(5–3600)
PUT /nodes/{id}/addressesaddresses:整体替换,至多 8 个 { address, level };address 为单个单播 IP(可为私网地址),不重复;level 为 0(主)、1(备 1)、2(备 2),非空时须有 level 0;[] 恢复为上报地址。发布集群的 DNS 版本(原因 manual)
PUT /nodes/{id}/probeenabled;关闭时删除该节点的探测结果
POST /scheduling/rulesclusterId、lineName(集群 DNS 绑定中的线路名称,null 为全部线路,默认 null;backup_group 必须有)、name(1–100 字符)、enabled(默认 true)、match(all 默认 / any)、conditions、action(remove_node、backup_group、backup_ip)、holdSeconds、recoverSeconds(0–86400,默认 300)
conditions[]1–8 个:metric(cpu_percent、load1、memory_percent、egress_mbps、connections、probe_loss_percent、probe_latency_ms)、aggregate(avg 默认 / max / min)、comparator(gt、ge、lt、le)、threshold(0–10¹²)、durationSeconds(0–3600,默认 0)、regionId(只用于 probe_loss_percent、probe_latency_ms,默认 null)
PATCH /scheduling/rules/{id}POST 中除 clusterId 外的字段,均可选。enabled: false,或 lineName、match、action、conditions 与当前不同时,生效中的动作先恢复,状态重新开始;只修改 name、holdSeconds、recoverSeconds 时不影响。lineName 只在修改 lineName 或 action 时检查
过程响应
probes.list、probes.update探针:id、name、regionId、regionName、regionCode、enabled、online、lastSeenAt、enrolledAt、hostname、agentVersion、os、arch、certNotAfter、targets、lastRound({ checkedAt, results, failed, lossPercent, avgRttMs },没有结果时为 null)、createdAt
probes.createTokentokenId、token(ewp_…,只返回一次)、expiresAt、serverUrl(节点通道)、caSha256、command(docker run 启动命令)
probes.delete、scheduling.delete{ ok: true }
probes.results按节点、地址、端口、探测方排序,至多 5000 条:proberKind(probe / node)、proberId、proberName、regionId、regionName、nodeId、nodeName、address、port、method(tcp / http / https)、sent、lost、lossPercent、rttMs(成功尝试的中位数,全部丢失时为 0)、error(timeout、refused、reset、tls、status、unreachable)、checkedAt
settings.probes、settings.setProbes探测设置;从未保存时为默认值 10、3000、3、50、30、60
nodes.* 返回的节点增加 probeEnabled;metrics({ cpuPercent, load1, load5, load15, memoryUsedBytes, memoryTotalBytes, egressBps, activeConnections, reportedAt },最近一次心跳没有指标(节点缺少 metrics-v1)时为 null);schedulingAddresses([{ address, level, source, reachable }],source 为 reported 或 configured);schedulingLevel(DNS 当前使用的级别);remoteAddress(节点注册与最近一次心跳连接的源地址,经代理时为代理的地址);dnsIssue(没有调度地址时为 no_public_address,否则 null);authError(自最近一次心跳以来节点通道拒绝该节点自己证书的原因,如 CERT_HAS_EXPIRED,否则 null)
scheduling.list、scheduling.create、scheduling.update规则:id、clusterId、请求中的字段(条件补齐默认值)、activeNodes([{ nodeId, nodeName, since }],生效中与恢复中的节点)、createdAt、updatedAt
scheduling.preview{ clusterId, evaluatedAt, rules }。每条规则:ruleId、ruleName、enabled、lineName、match、action、nodes。每个节点:nodeId、nodeName、state(idle、pending、active、recovering)、conditions(条件字段与 value(没有数据为 null)、holds、heldSeconds、satisfied)、matches、inEffect、wouldActivate、wouldRecover、activeSince、recoveringSince、recoversAt

DNS 绑定与记录的新增字段:

请求或响应字段
PUT /clusters/{clusterId}/dns 的 binding.lines[]增加 resolutionLine(default 默认、telecom、unicom、mobile、edu、overseas)、backupNodeGroupIds(本集群的节点组,至多 4 个,不重复,不含本线路的节点组,默认 [])、minHealthyIps(1–64,默认 1)。GET 对此前保存的线路返回默认值
GET /clusters/{clusterId}/dns 与 GET /clusters/{clusterId}/dns/export 的 records[]增加 line:记录的解析线路;默认线路的记录没有此字段
DNS 版本(revision、blocked、GET /clusters/{clusterId}/dns/revisions 等)reason 为 manual、health、rollback、force 或 scheduling;增加 reasonParams:scheduling 为 ruleId、rule、nodeId、node、action、event(activated / recovered),其他原因为 {}
GET /dns/catalogcapabilities.lines 由布尔值改为服务商支持的解析线路数组
错误代码状态场景
DNS_LINE_UNSUPPORTED400绑定线路的 resolutionLine 不在服务商账号支持的线路中;data.line
REGION_IN_USE409删除仍有探针的区域(regions.delete);data.probes 为探针数
PROBE_NOT_FOUND404探针不存在;probes.results 的 probeId 既不是探针也不是节点
NODE_REGION_REQUIRED409nodes.setProbe 开启时,节点的节点组没有区域
NODE_ADDRESS_INVALID400调度地址不是单个单播 IP(CIDR、主机名、回环、链路本地、组播等)或重复;data.address
SCHEDULING_RULE_NOT_FOUND404规则不存在
SCHEDULING_RULE_INVALID400backup_group 没有 lineName,或 lineName 不在集群的 DNS 绑定中
REGION_NOT_FOUND404probes.createToken 的 regionId 或条件的 regionId 不存在
NODE_NOT_FOUND404节点不存在,含 probes.results 的 nodeId
CLUSTER_NOT_FOUND404规则或预览的集群不存在
BAD_REQUEST400输入校验失败,例如 timeoutMs 超过探测间隔、调度地址没有 level 0、非探测指标带 regionId

行为见区域探针与智能调度与DNS 调度与告警。

监听端口与访客 IP

过程端点说明
clusters.listenPortsGET /clusters/{clusterId}/listen-ports集群的附加 HTTP / HTTPS 端口与缺少 edge-ports-v1 的节点
clusters.setListenPortsPUT /clusters/{clusterId}/listen-ports整体替换附加端口,发布配置版本
clusters.clientIpGET /clusters/{clusterId}/client-ip访客 IP 设置与缺少 client-ip-v1 的节点
clusters.setClientIpPUT /clusters/{clusterId}/client-ip替换访客 IP 设置,发布配置版本

服务账号不能调用这些过程(403 SERVICE_ACCOUNT_FORBIDDEN);只读 AccessKey 只能调用 GET。

请求字段
PUT /clusters/{clusterId}/listen-portshttpPorts、httpsPorts:各最多 16 个 1–65535 的端口,不含 80、443,去重排序;同一端口不能同时出现在两者中(LISTEN_PORT_CONFLICT),不能在端口池内(LISTEN_PORT_IN_POOL),移除仍被网站使用的端口返回 LISTEN_PORT_IN_USE
PUT /clusters/{clusterId}/client-ipsettings:mode(direct / proxy_protocol / header,默认 direct);header 模式必填 trustedCidrs(1–64 个 IP 或 CIDR,规范化、去重排序;不接受 IPv4 映射的 IPv6)与 header(小写报头名,[a-z0-9-],1–64 字符,不能是 hop-by-hop、Host、Cookie、Authorization、X-Request-Id 或 X-Edgeweir-*);direct 模式可选 dropForwardedFor

网站的端口经 PATCH /sites/{id} 的 ports({ http, https })修改,POST /sites 可选同一字段(省略为 80 与 443);网站响应带 ports。错误码:SITE_PORT_UNAVAILABLE、SITE_PORTS_EMPTY、SITE_HTTPS_PORT_NEEDS_CERTIFICATE。PUT /sites/{id}/https 的设置新增 redirectStatus(301、302、303、307、308,默认 301)、redirectPort(443 或网站的 HTTPS 端口,默认 443,HTTPS_REDIRECT_PORT_INVALID)与 redirectExcludedDomains(网站的域名,最多 50 个,HTTPS_REDIRECT_DOMAIN_INVALID)。GET /sites/{id}/features 新增 edgePorts、clientIp;GET /clusters 的集群新增 clientIpMode。

端口池与 L4 应用

过程端点说明
clusters.portPoolsGET /clusters/{clusterId}/port-pools集群的端口池、保留端口与缺少 l4-v1、l4-v2 的节点
clusters.setPortPoolsPUT /clusters/{clusterId}/port-pools整体替换端口池;不发布配置版本
l4Apps.listGET /l4-appsL4 应用,按端口、协议排序;查询参数 clusterId(可选)
l4Apps.getGET /l4-apps/{id}一个应用
l4Apps.createPOST /l4-apps新建应用,返回 201
l4Apps.updatePATCH /l4-apps/{id}只修改给出的字段
l4Apps.setEnabledPUT /l4-apps/{id}/enabled停用或启用
l4Apps.deleteDELETE /l4-apps/{id}删除应用、源站与统计
l4Apps.statsGET /l4-apps/{id}/stats按分钟的统计

服务账号不能调用这些过程(403 SERVICE_ACCOUNT_FORBIDDEN);只读 AccessKey 只能调用 GET。

请求字段
PUT /clusters/{clusterId}/port-poolspools:整体替换,最多 64 个 { protocol, from, to };protocol 为 tcp、udp 或 both,from、to 为 1024–65535,from 不大于 to
POST /l4-appsclusterId、name(1–100 字符,去掉首尾空格)、protocol(tcp / udp)、port(1024–65535)、origins;可选:portEnd(端口段的结束端口,大于 port,最多 1000 个端口,默认 null)、originPortMode(fixed / same,默认 fixed)、certificateId(只用于 TCP,节点终结 TLS,默认 null)、tlsMinimumVersion(1.2 / 1.3,默认 1.2)、enabled(默认 true)、acceptProxyProtocol(默认 false)、proxyProtocolVersion(0–2,0 不发送,默认 0)、maxFails(1–100,默认 3)、failTimeoutSeconds(1–3600,默认 30)、connectTimeoutMs(100–60000,默认 5000)、idleTimeoutSeconds(1–86400,省略时 TCP 为 600、UDP 为 30)、allowListIds、blockListIds(IP 名单 ID,各最多 16 个,去重,默认 [])、maxConnections(0–10000000)、newConnectionsPerSecond(0–1000000),后两项 0 表示不限,默认 0
origins[]1–32 个 { address, port, weight, backup }:address 为主机名或 IP,规则同网站源站;port 1–65535,originPortMode 为 same 时省略(保存为 0);weight 1–100,默认 1;backup 默认 false。至少一个源站的 backup 为 false
PATCH /l4-apps/{id}POST 中除 clusterId、enabled 外的字段,均可选;origins 整体替换,地址与端口不变的源站保留 ID(节点上的被动健康状态随之保留);修改 protocol 不改变 idleTimeoutSeconds;可选 expectedUpdatedAt
PUT /l4-apps/{id}/enabledenabled;可选 expectedUpdatedAt
GET /l4-apps/{id}/stats查询参数 from、to(ISO 8601),from 早于 to,范围最长 7 天
过程响应
clusters.portPools、clusters.setPortPoolsclusterId;pools(按起始端口、协议排序);reservedPorts(集群 HTTP / HTTPS 监听的端口,含附加端口,不能进入端口池);nodesWithoutL4([{ id, name }],集群中不上报 l4-v1 的活动节点);nodesWithoutL4V2(不上报 l4-v2 的活动节点)
l4Apps.list、l4Apps.get 与其他过程返回的应用id、clusterId、clusterName、name、protocol、port、enabled、acceptProxyProtocol、proxyProtocolVersion、portEnd、originPortMode、certificateId、certificateName、tlsMinimumVersion、origins([{ id, address, port, weight, backup }],按保存顺序)、maxFails、failTimeoutSeconds、connectTimeoutMs、idleTimeoutSeconds、allowListIds、blockListIds、maxConnections、newConnectionsPerSecond、dnsTarget、dnsLines、createdAt、updatedAt
dnsTarget客户端连接的 CNAME <应用 ID>.<集群域名>,只在应用启用时发布;集群 DNS 为「不管理」时为 null
dnsLines每条绑定线路 { name, target }:开启线路别名时 target 为 <线路>.<应用 ID>.<集群域名>,否则为 <线路>.<集群域名>
l4Apps.create、l4Apps.update、l4Apps.setEnabled{ app, revision }
l4Apps.delete{ revision }
l4Apps.statsappId、from、to;bucketSeconds:范围不超过 1 天为 60,不超过 5 天为 300,否则 3600;points:从 from 所在的桶起每桶一项,最早在前,空桶为 0;totals;nodes:每个上报节点 { nodeId, nodeName, … },连接数多的在前
统计计数connections(接受的连接或 UDP 会话)、refused(被 IP 名单或上限拒绝)、peakConcurrent、bytesReceived(来自客户端)、bytesSent(发往客户端)。peakConcurrent 在 points、totals 中为每分钟各节点峰值之和在桶或范围内的最大值,在 nodes 中为该节点自己的最大值
  • create、update 发布集群的配置版本(原因 l4_app_created、l4_app_updated),审计 l4_app.create、l4_app.update;setEnabled 状态变化时发布(l4_app_updated),审计 l4_app.enable、l4_app.disable,状态不变时返回最新版本,不发布、不写审计;delete 发布(l4_app_deleted),审计 l4_app.delete。setPortPools 审计 cluster.port_pools_update。
  • 配置中有启用的应用时要求节点能力 l4-v1,见节点能力。
错误代码状态场景
L4_APP_NOT_FOUND404应用不存在
L4_APP_LIMIT409集群已有 256 个应用(含停用的);data.limit
L4_PORT_OUTSIDE_POOL400端口不在集群该协议的端口池内;data.port
L4_PORT_IN_USE409同一集群同一协议的端口已有应用(含停用的),或新的端口池会把应用的端口留在池外;data.apps(名称 (端口/协议),逗号分隔)
L4_PORT_RESERVED400端口池或应用端口是集群 HTTP / HTTPS 监听的端口;data.port
L4_PORT_POOL_OVERLAP400同一协议的端口池重叠,both 与 tcp、udp 都重叠;data.pools(起始-结束/协议,逗号分隔)
L4_PROXY_PROTOCOL_UNSUPPORTED400UDP 应用设置了 acceptProxyProtocol 或非 0 的 proxyProtocolVersion
IP_LIST_NOT_FOUND404allowListIds 或 blockListIds 中的名单不存在
IP_LIST_IN_USE409DELETE /ip-lists/{id} 删除仍被规则、缓存规则条件或 L4 应用引用的名单;data.users 列出前 5 个引用者:规则名(网站规则带网站名,如 拦截 (shop))、缓存规则所在的网站名、L4 应用名
ORIGIN_ADDRESS_FORBIDDEN400源站为特殊用途地址且不在源站地址允许清单内;data.address、data.range
UPDATED_AT_MISMATCH409expectedUpdatedAt 不是当前值
CLUSTER_NOT_FOUND404集群不存在
BAD_REQUEST400输入校验失败,例如端口低于 1024、from 大于 to、源站全为备用、统计范围超过 7 天
curl -fsS -X PUT -H "x-api-key: $EDGEWEIR_API_KEY" -H 'content-type: application/json' \
  -d '{"pools":[{"protocol":"both","from":20000,"to":20100}]}' \
  https://cdn-admin.example.com/api/v1/clusters/<集群 ID>/port-pools
curl -fsS -X POST -H "x-api-key: $EDGEWEIR_API_KEY" -H 'content-type: application/json' \
  -d '{"clusterId":"<集群 ID>","name":"game","protocol":"tcp","port":20000,"origins":[{"address":"game-origin.example.com","port":7000}],"proxyProtocolVersion":2}' \
  https://cdn-admin.example.com/api/v1/l4-apps

行为见四层转发。

示例

列出网站(过程 sites.list,GET /api/v1/sites):

curl -fsS -H "x-api-key: $EDGEWEIR_API_KEY" \
  "https://cdn-admin.example.com/api/v1/sites?page=1&pageSize=20"
查询参数说明
search匹配网站名称或任一域名,最长 100 字符
clusterId集群 UUID
page页码,默认 1
pageSize每页条数,1–100,默认 20

响应:{"items":[…],"total":<总数>}。

错误格式

{
  "defined": false,
  "code": "ACCESS_KEY_READ_ONLY",
  "status": 403,
  "message": "access key is read only",
  "data": {}
}
字段说明
code稳定错误代码。代码与 HTTP 状态的完整列表:packages/contract/src/errors.ts
statusHTTP 状态码
message英文说明,供不识别代码的客户端使用
data消息参数

认证与输入校验失败使用 oRPC 通用代码:UNAUTHORIZED(401)、BAD_REQUEST(400)。

Web UI 接口

/rpc/* 使用 oRPC RPC 协议,供 Web UI 调用,路径为过程路径,例如 POST /rpc/sites/list。

要求说明
会话 Cookie由 /api/auth 的登录端点签发
x-csrf-token: orpc缺少时返回 403
x-api-key被剥离,不能代替会话 Cookie

第三方集成使用 /api/v1。

认证端点

/api/auth/* 由 better-auth 处理,只放行下表中的路径与方法。匹配为精确匹配(不接受前缀、编码变体或结尾斜杠),其余请求返回 404。请求中的 x-api-key 被剥离;客户端 IP 按 EDGEWEIR_TRUSTED_PROXIES 解析后传入。控制台不开放注册,唯一的账户由初始化向导创建;AccessKey 经 accessKeys.* 管理。NODE_ENV=production 时启用限速,计数存于 PostgreSQL。

路径方法
/api/auth/get-sessionGET
/api/auth/sign-in/emailPOST
/api/auth/sign-outPOST
/api/auth/change-passwordPOST
/api/auth/two-factor/enablePOST
/api/auth/two-factor/disablePOST
/api/auth/two-factor/verify-totpPOST
/api/auth/two-factor/verify-backup-codePOST
/api/auth/passkey/generate-register-optionsGET
/api/auth/passkey/verify-registrationPOST
/api/auth/passkey/generate-authenticate-optionsGET
/api/auth/passkey/verify-authenticationPOST
/api/auth/passkey/list-user-passkeysGET
/api/auth/passkey/delete-passkeyPOST

健康检查

GET /healthz 返回 200:

{ "status": "ok", "version": "20260929-a1b2c3d" }
字段说明
status固定为 ok
version运行版本(EDGEWEIR_VERSION);源码运行为 dev

HTTP 服务监听即返回,不检查数据库。容器健康检查见命令行。

安装脚本与发布镜像

GET /install.sh 返回节点安装脚本(text/x-shellscript,cache-control: no-store),脚本中的控制台地址替换为 EDGEWEIR_PUBLIC_URL。选项见命令行。

GET、HEAD /downloads/* 从 EDGEWEIR_DOWNLOADS_DIR 提供文件,URL 路径与目录中的相对路径相同:

路径内容cache-control
/downloads/<项目>/latest最新版本号,文本no-cache
/downloads/<项目>/v<语义化版本>/<文件>发布文件public, max-age=86400, immutable

<项目> 为 edgeweir-node 或 cosign;edgeweir-openresty、edgeweir-openresty-modsecurity 软件包放在 edgeweir-node 的同一版本目录。其他路径、不存在的文件、指向目录外的符号链接返回 404。目录准备见接入节点。

节点通道

项值
协议Connect-RPC,HTTPS(HTTP/2,兼容 HTTP/1.1),TLS 1.2 及以上
监听NODE_API_HOST:NODE_API_PORT,默认 8443
服务edgeweir.node.v1.NodeService(节点)与 edgeweir.node.v1.ProbeService(区域探针与兼任探针的节点),定义见 proto/edgeweir/node/v1/node.proto 与 probe.proto
服务器证书控制台内部 CA 在每次启动时签发,名称见环境变量
认证Enroll、EnrollProbe:一次性注册 token(ewt_、ewp_),节点或探针预先固定内部 CA 的 SHA-256 指纹。其他 RPC:内部 CA 签发的客户端证书(mTLS),CN 为节点 ID(O=Edgeweir Node)或探针 ID(O=Edgeweir Probe);探针证书只能调用 ProbeService,节点证书只在节点兼任探针时调用 GetProbeTargets、ReportProbeResults
其他路径404

节点通道必须直连或四层透传;终结 TLS 的代理会使节点 mTLS 失败。见端口、反向代理与可信代理。

在 GitHub 上编辑

本页目录