红网云/Docs 中文 EN
红网云 是企业级 Web 防护管理平台。后端 Go + Gin REST API,前端 Vue 3 SPA,附带 zcloud CLI 工具用于自动化运维。

本文档属于红网云 — 企业 Web 防护管理平台
CLI 工具:zcloud · 5 大模块:guard / sys / analytics / cli_release / auth
完整 API 索引:/api/openapi.json · 文档地图:/sitemap.xml · AI 速读:/llms.txt


API 文档

商业级 REST API 文档 · 57 个 endpoint · 80+ 图表数据接口(chart-key)· 双通道鉴权
适用对象:客户对接工程师、SRE、SaaS 集成商、AI agent
阅读顺序:先看 §0 总体约定§1 鉴权 → 再按业务模块跳转


目录

必读

公开接口(无需鉴权)

业务接口(双通道鉴权)

套餐目录

节点运维

Analytics 统计分析(92 chart-key · 单一形状契约)

参考

完整 OpenAPI: /api/openapi.json · AI 速读: /llms.txt · 错误码: /docs/errors · CLI: /docs/cli · 权限: /docs/permissions


§0 总体约定

红网云后端基于 Gin 实现的 RESTful 服务。

0.1 基础协议

项目
协议 HTTPS(推荐)/ HTTP
数据格式 请求与响应均为 application/json(特例:POST /api/guard/certs 仍是 JSON,PEM 走字符串字段;导出/下载接口返回 text/csvapplication/pdf 等)
字符集 UTF-8
路径前缀 所有业务 API 都挂在 /api/
时间格式 Unix 毫秒时间戳(int64),不是 ISO 字符串
国际化 Accept-Language: zh-CNen-US,影响错误消息和权限名称

0.2 统一响应信封

任何 JSON 响应都遵循下面三段结构:

{
  "code": 0,
  "message": "ok",
  "data": { /* 业务载荷,类型依接口而定 */ }
}
字段 类型 含义
code number 0=成功;非 0 = 业务错误码
message string 错误描述(受 Accept-Language 影响)
data any 业务数据;列表接口为 { list, total, page, size }

特例:导出文件下载接口(POST /api/analytics/overview/exportGET /api/analytics/reports/:id/downloadPOST /api/analytics/logs/export)直接返回二进制流或 CSV/JSON 原文,包裹信封。

0.3 HTTP 状态码

状态码 何时出现
200 业务成功(仍需检查 code
201 资源创建成功
400 入参错误(参数缺失、格式非法、超出范围)
401 未登录、token 过期、API Key 已吊销
403 已登录但权限不足 / 跨 OEM 越权(参见 权限矩阵
404 资源不存在或不在可见范围
429 限流(默认每 Key 每秒 100 请求)
5xx 服务端异常

0.4 列表接口分页约定

所有列表接口统一使用 page + size不是 page_size):

字段 类型 默认 范围
page int 1 ≥ 1
size int 20 1 - 100

响应:

{
  "code": 0,
  "data": {
    "list": [ /* ... */ ],
    "total": 42,
    "page": 1,
    "size": 20
  }
}

0.5 跨模块设计标记(D*)

文档中少量 D* 标记来自跨模块设计决策,用于提醒对接方不要使用不存在或语义错误的字段:

标记 含义
D4 percentile / p50 / p95 / p99 不作为通用字段暴露;仅时间窗 ≤ 24h 时走实时 ES percentile 计算
D7 报表模板枚举为闭集,超出枚举的模板名不可调用
D8 缓存价值字段以 total_cache_* 为准,不存在 cache_count / cache_bytes / cache_hit 这类单字段
D10 告警/风险处置闭环字段以 process_uid / process_time / status / level 为准,不使用旧字段 handle_user / handle_time / risk_score / alert_status

0.6 三类读者的阅读路径

读者 入口 优先用
人类对接工程师 本文档 + 快速上手 curl / Postman 调单接口
脚本 / CI / 第三方系统 本文档 + API Key 管理 API Key + 受限 scopes
机器 / AI agent /api/openapi.json / /llms.txt / /llms-full.txt OpenAPI v3 schema

§1 鉴权(先读)

红网云当前支持两条认证通道,同一请求只能选其中一种

场景 请求头 适用对象 说明
人工登录 / Web 控制台 / CLI 交互登录 Authorization: Bearer <token> token 由 POST /api/auth/login 颁发,会过期,适合短期会话
脚本 / CI / 第三方系统集成 Authorization: ApiKey zck_<prefix>.<secret> 机器调用 API Key 明文仅签发时返回一次,适合长期自动化对接

公开接口(无需鉴权)只有 4 个:

其它所有接口都必须携带上述任一认证头。下面各接口示例默认使用 Bearer;如改用 API Key,只需把请求头替换为 Authorization: ApiKey zck_<prefix>.<secret>,并确保该 Key 的 scopes 覆盖接口所需权限。

1.1 API Key 权限规则

effective_perms = user.RBAC ∩ key.scope

API Key 的权限不会超过签发用户当前的 RBAC;scope 只能收窄,不能放大。中间件认证通过后会注入与会话通道一致的 user_id / role_id 上下文,下游 RBAC/OEM 隔离保持一致。

安全约束

API Key 管理接口的完整说明见 §4.2


§2 CLI 发布(公开接口)

供 zcloud CLI 自更新与一键安装使用,无需鉴权。

GET /api/cli/version — 查询 CLI 最新版本

用途:客户端启动时自检版本;安装脚本 /api/cli/install.sh 内部依赖此接口决定下载哪个二进制。

鉴权:无(公开)

输入参数:无

输出字段

字段 类型 说明
data.version string 形如 v0.1.0-31,对齐 git tag
data.binaries[] array 4 个 os/arch 组合的下载地址
data.binaries[].os string linux / darwin
data.binaries[].arch string amd64 / arm64
data.binaries[].download_url string 拼接服务地址即可下载

可视化建议:纯文本展示(版本徽章),不适合图表。前端可用作"系统设置 - CLI 版本"页面的 KPI 数字卡。

示例响应

{
  "code": 0,
  "message": "ok",
  "data": {
    "version": "v0.1.0-31",
    "binaries": [
      { "os": "linux",  "arch": "amd64", "download_url": "/api/cli/download/linux-amd64" },
      { "os": "linux",  "arch": "arm64", "download_url": "/api/cli/download/linux-arm64" },
      { "os": "darwin", "arch": "amd64", "download_url": "/api/cli/download/darwin-amd64" },
      { "os": "darwin", "arch": "arm64", "download_url": "/api/cli/download/darwin-arm64" }
    ]
  }
}

GET /api/cli/install.sh — 一键安装脚本

用途:在 Linux/macOS 上一行命令完成 CLI 安装。返回 text/x-shellscript,可直接 curl ... | sh

鉴权:无(公开)

输入参数:无

输出字段:纯 shell 脚本文本,走 JSON 信封。

可视化建议:不适合图表,作为代码片段展示。

示例

curl -fsSL https://waf.example.com/api/cli/install.sh | sh

GET /api/cli/download/{filename} — 下载指定二进制

用途:拉取特定平台的 zcloud 二进制(已签名)。filename 取自 /api/cli/version 返回的 download_url 的最后一段,如 linux-amd64

鉴权:无(公开)

输入参数

字段 类型 必填 说明
filename path linux-amd64 / linux-arm64 / darwin-amd64 / darwin-arm64 其一

输出字段:二进制流(application/octet-stream)。

可视化建议:不适合图表。


GET /api/cli/checksums.txt — 二进制校验和

用途:配合 /api/cli/download/* 做 SHA256 完整性校验,安装脚本会先 fetch 这个文件再下载二进制。

鉴权:无(公开)

输入参数:无

输出字段:纯文本 text/plain,每行一个 <sha256> <filename>

可视化建议:不适合图表。


§3 用户认证

POST /api/auth/login — 登录

用途:用用户名 + 密码换取一个会话 token。Web 控制台、CLI 交互式登录、移动端均走此接口。

鉴权:无(公开)

输入参数(请求体):

字段 类型 必填 说明
username string 用户名
password string 密码(前端 base64 编码后传入,后端 decode 后再 bcrypt 比对,兼容老系统)

输出字段

字段 类型 说明
data.token string 32 位会话 token,用于后续 Authorization: Bearer <token>
data.user_id string 用户唯一 ID
data.user_name string 用户名
data.nick_name string 显示名
data.need_change_password bool true 表示首次登录需强制改密码

可视化建议:登录响应不直接做图,但 need_change_password=true 时前端应跳转改密页。

示例请求

curl -X POST https://waf.example.com/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","password":"'$(echo -n 'your_password' | base64)'"}'

示例响应

{
  "code": 0,
  "message": "ok",
  "data": {
    "token": "550e8400-e29b-41d4-a716-446655440000",
    "user_id": "u-admin",
    "user_name": "admin",
    "nick_name": "系统管理员",
    "need_change_password": false
  }
}

常见误用


POST /api/auth/logout — 注销

用途:主动失效当前 Bearer token;再次请求该 token 返回 401。

鉴权Bearer <token>(API Key 通道无 logout 概念,吊销走 DELETE /api/sys/api-keys/:id

输入参数:无

输出字段datanull

可视化建议:不适合图表。

示例请求

curl -X POST https://waf.example.com/api/auth/logout \
  -H "Authorization: Bearer $TOKEN"

§4 系统管理

4.1 用户管理

适用场景:在"系统设置 - 用户管理"页面增删改查用户。所有用户接口都受 OEM 隔离,跨 OEM 操作会返回 403。

GET /api/sys/users — 用户列表(分页)

用途:在"用户管理"页面渲染用户表格,支持关键字模糊搜索 + 分页。

鉴权sys.user.list

输入参数

字段 类型 必填 取值/示例 说明
page int 1 页码,从 1 起
size int 20 每页条数,1-100
keyword string admin 用户名/显示名模糊搜索

输出字段data.list[]):

字段 类型 说明
user_id string 用户唯一 ID
user_name string 登录用户名
nick_name string 显示名
email string 邮箱
mobile string 手机号
locked int 0=正常,非 0=锁定
role_ids int64[] 角色 ID 列表(可多角色)
roles[] array 角色摘要 {role_id, name, level}
ctime int64 创建时间,Unix 毫秒

可视化建议:表格展示。locked 列建议用徽章(绿/红);roles 用 chip 标签。

示例请求

curl -H "Authorization: Bearer $TOKEN" \
  "https://waf.example.com/api/sys/users?page=1&size=20&keyword=admin"

示例响应

{
  "code": 0,
  "message": "ok",
  "data": {
    "list": [
      {
        "user_id": "u-001",
        "user_name": "admin",
        "nick_name": "系统管理员",
        "email": "admin@example.com",
        "mobile": "",
        "locked": 0,
        "role_ids": [1],
        "roles": [{ "role_id": 1, "name": "超级管理员", "level": 1 }],
        "ctime": 1714521600000
      }
    ],
    "total": 42,
    "page": 1,
    "size": 20
  }
}

常见误用


POST /api/sys/users — 创建用户

用途:在"用户管理"页面提交"新建用户"表单。

鉴权sys.user.create

输入参数(请求体):

字段 类型 必填 取值/示例 说明
user_name string u1 2-255 字符
password string InitPassw0rd! 6-72 字符(bcrypt 上限)
nick_name string 运维 A ≤ 100 字符
email string u1@x.com 标准邮箱格式
mobile string 13800138000 ≤ 20 字符
comment string 值班同事 备注

输出字段:返回新建用户对象,结构同列表项。

可视化建议:不适合图表,是一次性写操作;前端应在成功后刷新用户列表。

示例请求

curl -X POST https://waf.example.com/api/sys/users \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"user_name":"u1","password":"InitPassw0rd!","nick_name":"运维 A","email":"u1@example.com"}'

DELETE /api/sys/users/{id} — 删除用户

用途:用户管理页面"删除"按钮的后端接口。删除会级联清理会话、API Key、角色绑定。

鉴权sys.user.delete

输入参数

字段 类型 必填 说明
id path 用户 user_id

输出字段datanull

可视化建议:不适合图表。


PUT /api/sys/users/{id}/password — 重置密码

用途:管理员替用户重置密码。被重置用户下次登录后强制改密码。

鉴权sys.user.resetpwd

输入参数

字段 类型 必填 说明
id path 用户 user_id
password string 新密码(明文,6-72 字符;后端自动 bcrypt)

输出字段datanull

可视化建议:不适合图表。

示例请求

curl -X PUT https://waf.example.com/api/sys/users/u-001/password \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"password":"NewPassw0rd!"}'

常见误用


4.2 API Key 管理

适用场景:在"系统设置 - API Key"页面发放/回收脚本调用凭证。配合 /api/sys/api-keys/:id/logs|stats|audit-actions 做调用审计。

POST /api/sys/api-keys — 签发新 API Key

用途:为脚本/CI/第三方系统签发一条机器调用凭证。明文 api_key 字段仅此一次返回,前端必须立即让用户复制保存。

鉴权sys.apikey.create

输入参数(请求体):

字段 类型 必填 取值/示例 说明
name string 生产对接 ≤ 100 字符,用于审计识别
scopes string[] ["guard.domain.list"] 权限 full key 列表;空数组 = 完整继承签发用户当前 RBAC
expires_in_days int 90 过期天数,默认 90,最大 365
allowed_ip_cidrs string[] ["203.0.113.0/24"] E14 IP 白名单 CIDR;为空表示不限制来源 IP

输出字段

字段 类型 说明
data.key_id string API Key 唯一 ID(吊销/查日志用此 ID)
data.name string 与请求一致
data.api_key string 完整明文 prefix.secret,仅此一次返回
data.prefix string 形如 zck_abc12345,可写入日志
data.last4 string secret 末 4 位,前端用于"我刚签的那条"识别
data.expires_at int64 过期时间,Unix 毫秒

错误码

可视化建议:签发响应是一次性写操作,建议前端用模态框 + 一次性复制按钮 + 遮码展示明文(参考行业惯例 GitHub/Stripe)。

示例请求

curl -X POST https://waf.example.com/api/sys/api-keys \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name":"生产对接","scopes":["guard.domain.list"],"expires_in_days":90}'

示例响应

{
  "code": 0,
  "data": {
    "key_id": "8f21c0c5-55ae-4cbd-a60a-8e64a6e2b1d0",
    "name": "生产对接",
    "api_key": "zck_abc12345.A1b2C3d4E5f6G7h8I9j0K1L2M3n4O5p6Q7r8S9t0",
    "prefix": "zck_abc12345",
    "last4": "5t0",
    "expires_at": 1732982400000
  }
}

常见误用


GET /api/sys/api-keys — 查询 API Key 列表

用途:在 API Key 管理页面展示当前用户/OEM 内的 Key 清单与状态。

鉴权sys.apikey.list

输入参数

字段 类型 必填 说明
page int 默认 1
size int 默认 20,最大 100

输出字段data.list[]):

字段 类型 说明
key_id string API Key 唯一 ID
name string 名称
prefix string zck_xxx 前缀
last4 string secret 末 4 位
user_id string 持有人
oem_id string OEM 隔离边界
scopes string[] 限定权限列表
allowed_ip_cidrs string[] E14 IP 白名单
status int 1=active,2=revoked
expires_at int64 过期时间
last_used_at int64 最近调用时间;从未用为 0
last_used_ip string 最近调用 IP;从未用为 ""
ctime int64 签发时间

可见范围

可视化建议:表格展示。status 用徽章(绿=active/灰=revoked),expires_at 即将到期(< 7d)建议高亮。配合 /stats 接口可做柱状图"调用量 TOP 5 Key"。

示例请求

curl -H "Authorization: Bearer $TOKEN" \
  "https://waf.example.com/api/sys/api-keys?page=1&size=20"

DELETE /api/sys/api-keys/{id} — 吊销 API Key

用途:软吊销(statusrevoked),保留审计痕迹。中间件认证时 status != active 直接拒绝。

鉴权sys.apikey.delete

输入参数

字段 类型 必填 说明
id path API Key key_id

输出字段datanull

幂等性:重复吊销返回 200,前端反复操作不报错。

可操作范围

错误码1051 API Key 不存在或不在可见范围。

可视化建议:不适合图表;前端"吊销"按钮触发,建议加二次确认对话框。

示例请求(用 API Key 调用)

curl -X DELETE https://waf.example.com/api/sys/api-keys/8f21c0c5-55ae-4cbd-a60a-8e64a6e2b1d0 \
  -H "Authorization: ApiKey zck_abc12345.A1b2C3d4..."

GET /api/sys/api-keys/{id}/logs — 查询某 Key 的调用流水(E12)

用途:审计某条 API Key 的调用历史。返回该 Key 在审计表 api_key_audit_logs 中的事件列表。

鉴权sys.apikey.logs

输入参数

字段 类型 必填 说明
id path API Key key_id
event string call(默认,调用流水)/ manage(管理操作)
page int 默认 1
size int 默认 20,最大 100

输出字段(每条记录):

字段 类型 说明
id int 自增主键
event_type string call / manage
key_id string 关联的 API Key ID
user_id string 操作主体(call=key 持有人;manage=操作人)
auth_mode string apikey / session,记录请求通过的认证通道
action string call=<METHOD> <PATH>;manage=create / revoke / renew / revoke-all
status_code int HTTP 响应状态码(call 类型有效)
biz_code int 业务错码(0 = 成功;call 类型有效)
client_ip string 客户端 IP
user_agent string UA(≤ 255 字符,超长截断)
extra string JSON 字符串,承载续期 / 批量动作等结构化扩展字段
ctime int Unix 毫秒时间戳

可视化建议

可见范围

错误码1051 API Key 不存在或不在可见范围。


GET /api/sys/api-keys/{id}/stats — 查询某 Key 的聚合统计

用途:在 API Key 详情页展示该 Key 的调用聚合 KPI(总次数、成功率、QPS、TOP 接口)。仅基于 event_type=call 的记录。

鉴权sys.apikey.stats

输入参数

字段 类型 必填 说明
id path API Key key_id
since string/int 聚合窗口起点;支持 24h / 7d / 30m 相对值,或纯整数 = 毫秒时间戳;留空 = 全量历史

输出字段

字段 类型 说明
total_calls int 窗口内总调用次数
success int 200 ≤ status_code < 400 的次数
client_err int 400 ≤ status_code < 500 的次数
server_err int status_code ≥ 500 的次数
top_endpoints array 调用次数 Top 5 的 action({action, count},按次数降序)
last_1h_qps float 最近 1 小时 QPS(次数 / 3600)

可视化建议

错误码400 since 解析失败;1051 API Key 不存在或不在可见范围。

示例请求

curl -H "Authorization: Bearer $TOKEN" \
  "https://waf.example.com/api/sys/api-keys/8f21c0c5-55ae-4cbd-a60a-8e64a6e2b1d0/stats?since=24h"

GET /api/sys/api-keys/audit-actions — 查询管理操作流水(E13)

用途:跨 Key 的审计视图,返回 API Key 管理动作(创建 / 吊销 / 续期 / 批量吊销)的流水。

鉴权sys.apikey.audit

输入参数page / size,标准分页。

输出字段:与 GET /api/sys/api-keys/{id}/logs 相同;event_type 全部为 manage

可见范围

可视化建议


4.3 权限树

GET /api/sys/permissions/tree — 权限树

用途:返回当前 OEM 下的完整权限树(含模块/资源/动作三层 + i18n 名)。前端"角色权限"页用此渲染勾选树;签发 API Key 选 scope 时也用同一颗树。

鉴权:登录态(无具体权限要求)

输入参数:可附加 Accept-Language: en-US 切换权限名语言。

输出字段(节选,data[]):

字段 类型 说明
module string 模块名,如 guard / sys / analytics
resources[] array 该模块下的资源列表
resources[].prefix string 资源前缀,如 guard.domain
resources[].name string 资源 i18n 显示名
resources[].actions[] array 该资源的动作列表
resources[].actions[].key string 动作短 key,如 list / create
resources[].actions[].name string 动作 i18n 显示名
resources[].actions[].full_key string 完整权限 key,如 guard.domain.list

可视化建议

示例响应(节选):

{
  "code": 0,
  "data": [
    {
      "module": "guard",
      "resources": [
        {
          "prefix": "guard.domain",
          "name": "域名",
          "actions": [
            { "key": "list",   "name": "列表",   "full_key": "guard.domain.list" },
            { "key": "view",   "name": "查看",   "full_key": "guard.domain.view" },
            { "key": "create", "name": "创建",   "full_key": "guard.domain.create" }
          ]
        }
      ]
    }
  ]
}

4.4 操作审计 /api/sys/audit-logs · /api/sys/login-records

前端「日志中心 › 操作审计」一页两个 Tab:操作日志与登录日志。两者共用 sys.audit.view 权限。

数据可见范围(重要):在 repo 的 SQL 层收敛,按 role_id 分三档(handler 的 scopeOemID / scopeUserID):

角色档 能看到谁的记录
平台级(role_id < 10:超管 / 运维 / 审计员) 全部,跨 OEM 无过滤
业务级根用户(role_id >= 10users.first_id 为空,典型是总代) 本 OEM 内,自己 + 整条创建链下游
业务级下级(二代 / 客户) 只有自己,外加自己被客服代客操作 / 代登录的那些行

没有任何入参可以指定被查用户keyword 虽能 LIKE 到 user_id / user_name,但与 scope 条件是 AND 关系,无法越权。

GET /api/sys/audit-logs — 操作日志

用途:控制台写操作的审计流水 —— 谁改了什么、改前改后、成功失败、是否代客操作。

鉴权sys.audit.view

输入参数

参数 类型 说明
page / size int 分页,size 上限 100
keyword string 宽口径 LIKE,覆盖 14 列(操作 / 主体 / IP / 资源 / 委托上下文等)
channel string 主体通道过滤,别名 actor_type;取 user / apikey / oauth / aegeon_staff / internal_service,非法值静默忽略
start_time / end_time int64 Unix 时间戳,传秒或毫秒都行(service 层 normalizeLogTime 归一到毫秒,因为前端时间选择器历来传秒而存储是毫秒)。闭区间,缺省表示该端不限

输出字段(节选,data.list[],完整字段见 OpenAPI 的 AuditLog):

字段 类型 说明
ctime int64 发生时间(毫秒时间戳)
action string 业务动作码,如 guard.domain.update
message string 人话操作描述,失败时带失败原因
resource_type / resource_label string 资源类型与人话名(如「防护域名」或具体域名)
actor_type / actor_name string 主体通道与展示名
remote_ip string 操作来源 IP
status / http_status / biz_code int 业务结果(1 成功 2 失败)与 HTTP / 业务码
detail string 结构化明细 JSON:changes(字段 old→new)与 params(业务上下文)
on_behalf_* / delegation_* string 客服代客操作时的委托上下文

标签真值表:写路由必须登记在 internal/middleware/audit_spec.goproductionAuditSpecs 里才有人话标签;纯查询型 POST 登记在 productionReadOnlyAuditPosts 里不记账。两张清单都没有的写路由会落库成 action=unregistered.business_operation + message=未登记业务操作,由 audit_spec_coverage_test.go 守着不再新增。

GET /api/sys/login-records — 登录日志

用途:登录 / 代登录流水 —— 谁何时从哪个 IP、用哪种方式登录,成功还是失败。

鉴权sys.audit.view

输入参数

参数 类型 说明
page / size int 分页,size 上限 100
keyword string 宽口径 LIKE,覆盖 15 列(用户名 / IP / 设备 / UA / 认证通道 / 失败原因 / 委托上下文等)
status int 登录结果:1 成功,2 失败;缺省或 0 表示全部
start_time / end_time int64 与操作日志同口径(秒或毫秒皆可,闭区间)

输出字段(节选,data.list[],完整字段见 OpenAPI 的 LoginRecord):

字段 类型 说明
ctime int64 登录时间(毫秒时间戳),login_time 是同一时刻的格式化串
user_name / user_id string 登录账号
ip / login_district string 来源 IP 与登录地(内网 IP 无地理信息时为空或占位)
auth_channel string 认证通道:password / apikey / oauth / aegeon_staff
login_status / failure_reason int / string 结果与失败原因
ttl_seconds int64 该次会话的生存时长(秒)
staff_* / on_behalf_* / delegation_* string 员工代登录时的委托上下文

§5 Guard 资源管理

📦 Guard 资源管理 · 30 个 endpoint · 用于配置防护对象(域名/证书/策略/CC&ACL 规则/名单/转发/调度/WAF 规则),是 WAF 防护能力的"配置面"
完整 schema 见 /api/openapi.json,本节给出对接最关键的字段名、枚举与典型踩坑点。

5.1 域名 /api/guard/domains

域名是 Guard 的核心资源——所有防护策略、证书绑定、统计聚合都以 domain_id 为锚点。

GET /api/guard/domains — 域名列表

用途:在"防护管理 - 域名"页面渲染域名表格,按审核状态筛选 + 关键字搜索。

鉴权guard.domain.list

输入参数

字段 类型 必填 取值/示例 说明
page int 1 页码
size int 20 每页条数,1-100(不是 page_size
keyword string api.example 模糊搜索 domainasset_name
audit_status int 4 1=未审核 2=审核中 3=未通过 4=通过

输出字段data.list[]DomainVO):

字段 类型 说明
domain_id string 域名唯一 ID
domain string 域名本身,如 api.example.com
asset_name string 资产备注名
user_id string 归属用户 UUID(保留兼容老平台)
user_name string 归属用户名(P1.3 新增,来源 cloud sys.users
policy_id string 关联策略 ID
cname string 后端为该域名分配的 CNAME
auto_cert bool 是否启用自动签发证书
mode int32 接入模式(1=反代等,参见运维文档)
audit_status int32 1=未审核 2=审核中 3=未通过 4=通过
switches map<string,int32> 保护开关,key 取自 waf/cc/acl/bot/cache,1=开 0=关
ctime / utime int64 创建/更新时间

可视化建议

示例请求

curl -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  "https://waf.example.com/api/guard/domains?page=1&size=20&audit_status=4"

示例响应

{
  "code": 0,
  "data": {
    "list": [
      {
        "domain_id": "d_8a3b1c",
        "domain": "api.example.com",
        "asset_name": "线上 API 网关",
        "user_id": "u_abc",
        "policy_id": "p_default",
        "cname": "api.example.com.cname.zcloud.io",
        "auto_cert": false,
        "mode": 1,
        "audit_status": 4,
        "switches": { "waf": 1, "cc": 1, "acl": 1, "bot": 0, "cache": 1 },
        "ctime": 1714521600000,
        "utime": 1714608000000
      }
    ],
    "total": 8,
    "page": 1,
    "size": 20
  }
}

常见误用


POST /api/guard/domains — 创建域名

用途:在"添加域名"表单提交后调用,创建一条新的防护域名。

鉴权guard.domain.create

输入参数(请求体 DomainCreateReq):

字段 类型 必填 说明
domain string 域名本身,如 api.example.com
policy_id string 防护策略 ID,用 GET /api/guard/policies 查询
src_sites array 源站配置,1-20 条
src_sites[].addr string 源站地址(IP 或域名,不支持通配符)
src_sites[].port int 源站端口 1-65535
src_sites[].protocol string 回源协议:http / https
src_sites[].weight int 权重,默认 1
asset_name string 资产备注名
cert_id int 接入时直接绑定证书;证书须存在、归属当前用户且能覆盖该域名

policy_id 为什么必填:域名的 WAF、CC、访问控制、黑白名单、BOT 规则全部挂在策略上。不绑策略的域名会被正常代理,但一条防护规则都不生效 —— 接入了却不设防。

同地址同端口同协议的 src_sites 会被静默去重,不报错。

证书两条路都行:建域名时传 cert_id(域名 + 源站 + 绑证书在一个事务内完成,任一步失败整体回滚),或建完再调 POST /api/guard/certs/:id/bind

输出字段:返回 DomainVO(结构同列表项),含分配的 domain_idcname

可视化建议:不适合图表。建议创建成功后立即跳转域名详情页或刷新列表。


GET /api/guard/domains/{id} — 域名详情

用途:进入域名详情页时拉单条详情。

鉴权guard.domain.view

输入参数:path id = domain_id

输出字段DomainVO,与列表项完全一致。

可视化建议:表单展示。switches 渲染为开关组;audit_status 用徽章。


PUT /api/guard/domains/{id} — 更新域名

用途:编辑域名的资产名、策略绑定、自动证书等可变字段。

鉴权guard.domain.edit

输入参数(请求体 DomainUpdateReq):

字段 类型 必填 说明
asset_name string 资产备注名
policy_id string 切换策略
auto_cert bool 切换自动签发开关
mode int32 接入模式

输出字段:返回更新后的 DomainVO

可视化建议:不适合图表。


DELETE /api/guard/domains/{id} — 删除域名

用途:从防护列表中移除域名。删除会级联清理 settings、证书绑定、统计快照。

鉴权guard.domain.delete

输入参数:path id = domain_id

输出字段datanull

可视化建议:不适合图表。建议删除前二次确认,提示"会清理统计与绑定"。


GET /api/guard/domains/{id}/settings — 域名设置查询

用途:在域名详情页"高级设置"标签下展示当前生效的 settings(保护模块开关、缓存策略、CC 限速等)。

鉴权guard.domain.view

输入参数:path id = domain_id

输出字段

字段 类型 说明
data.settings map<string,string> key 取自后端 settings.* 字典,典型 waf/cc/acl/bot/cache,value 是 stringified 配置 JSON

可视化建议:表单展示,每个 key 一行配置卡片。


GET /api/guard/domains/{id}/src-check-peers — 同源站探测对端查询(只读)

用途:探测在节点侧按源站 IP 跑,而不是按域名跑。同一用户名下多个域名回源到同一 IP 时,节点只跑一份探测,且取其中最小的监测频率 —— 于是会出现"界面写 60 秒、实际按 10 秒探测"而用户无从得知。本接口把共用源站且已开启探测的对端配置回显出来,解释实际生效值为何与本域名设置不同。

鉴权guard.domain.settings(读的就是同一批域名设置,不另立权限点)

输入参数:path id = domain_id

输出字段

字段 类型 说明
data.shared bool 是否存在共用源站且已开启探测的对端;false 时前端不显示该提示
data.domains string[] 对端域名清单(空数组而非 null
data.ping_config / tcp_configs / http_configs object 对端该探测的关键参数;该探测未被任何对端开启时字段整体缺省

对端探测对象字段:detection_time_intervals(秒,已按对端取最小值)、action(4=只告警 5=切换流量并告警)、portsfail_check_typeserial_failure_countfailure_ratio_windosfailure_ratio(0-100 整数百分比)。

语义要点:纯只读,不写任何库,也不改本域名的存量值 —— 存 60 就还是 60,只是实际跑 10。范围限定在同一用户名下(与 zmod where user_id = (...) 同口径),别人的域名共用同一 IP 不会出现在这里。

可视化建议:在源站探测配置区顶部以弱底提示条呈现,不用警示色(它不是错误,只是"你的设置可能不是实际生效值")。


PUT /api/guard/domains/{id}/settings — 域名设置更新

用途:修改域名的 settings map。

鉴权guard.domain.edit

输入参数(请求体 DomainSettingsUpdateReq):

字段 类型 必填 说明
settings map<string,string> key 必须取自 GET /settings 返回的 settings.* 键名;非法 key 后端会拒绝

输出字段datanull,调用方应紧接调用 GET 拉新值。

可视化建议:不适合图表。

常见误用


PUT /api/guard/domains/{id}/origins/{service_id}/status — 启停对外服务或源站

鉴权guard.domain.origin_status

输入参数:path id(域名 ID)、service_id(源站配置里 src_configs[].service_id)。

字段 类型 必填 取值 说明
status int32 1 / 2 1=禁用 2=启用
source object 不传 = 改整条对外服务;传了 = 只改该服务下这一台源站
source.ip string 是* 10.0.0.9 source 时必填
source.port int32 是* 80 source 时必填
source.line string Line_1_Default 不传则不参与匹配

源站配置有两级状态:对外服务(监听端口+协议)和组内单台源站,两级彼此独立——
「服务开着、组内某台单独关掉」是现网真实存在的状态。源站按 ip+port(+可选 line)定位,
不用下标:下标依赖调用方与存量顺序严格一致,中间增删过源站就会关错机器。

PUT /api/guard/domains/{id}/settings 的区别:那条收整包 JSON 字符串,由调用方读出-改-写回。
源站配置存在共库 guard_domain_settings.service_config_setting,老平台也在读写同一份,整包重建漏掉任何一个键都会写坏它。
本接口只声明「改哪一条的状态」,JSON 修改在服务端做,其余键(含本平台不认识的)原样保留。写入的是字符串枚举
CFGOPTION_2_ENABLE / CFGOPTION_1_DISABLE

仍然生效:租户隔离、域名必须保留至少一个已启用的普通 HTTP/HTTPS 源站。状态变更触发配置下发。

输出字段:无。


GET /api/guard/domains/{id}/dns/advance/config — 解析高级配置查询

用途:查询解析调度的高级配置:自动回源、节点解析最少开启数、IPv6 检查。字段对齐 zmod「解析高级配置」对话框与 gen-server DnsAdvanceConfig

鉴权guard.domain.settingsdisp_config_setting 本就在通用 settings 接口白名单里,同一份数据换个入口,不另立权限点)。

输入参数:path id = domain_id

输出字段dataDnsAdvanceConfigVO):

字段 类型 说明
auto_return_source string 自动回源:CFGOPTION_1_DISABLE | CFGOPTION_2_ENABLE
auto_switch_case int 节点解析最少开启数(历史数据可能为 0,界面展示按 1)
src_ipv6_check string 监测源站包含 IPv6(同上枚举)
default_ipv6_check string 检查包含 IPv6 默认解析(同上枚举)
auto_switch string 自动切换总开关(只读回显)
total_count / ava_ratio / max_count int / float / int 监测触发阈值,界面不改、只读展示

语义要点src_ipv6_check / default_ipv6_check 任一开启时,「初始化解析」才会为支持 IPv6 的节点/源站生成 AAAA 记录(IPv4/IPv6 双栈接入的开关就在这里)。

示例请求

curl -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/dns/advance/config"

PUT /api/guard/domains/{id}/dns/advance/config — 解析高级配置更新

用途:修改解析高级配置。与 zmod 界面同构:只写下表四个字段,disp_config_setting 存量里其余 key(total_count/ava_ratio/max_count/auto_switch/cname 等)服务端原样保留。

鉴权guard.domain.settings

输入参数(请求体 DnsAdvanceConfigUpdateReq,四字段均必填):

字段 类型 必填 说明
auto_switch_case int 节点解析最少开启数,1-1000
auto_return_source string CFGOPTION_1_DISABLE | CFGOPTION_2_ENABLE
src_ipv6_check string 同上枚举
default_ipv6_check string 同上枚举

输出字段datanull

语义要点:保存不会立刻改动现有解析记录;IPv6 开关在下一次「初始化解析」(POST /api/guard/schedules/domains/{id}/init)时生效(含 AAAA 记录的增删)。枚举必须传字符串名——数字形态会被 gen-server 侧 types.CFGOPTION 拒绝。

示例请求

curl -X PUT -H "Authorization: ApiKey $ZCLOUD_API_KEY" -H "Content-Type: application/json" \
  -d '{"auto_switch_case":1,"auto_return_source":"CFGOPTION_1_DISABLE","src_ipv6_check":"CFGOPTION_2_ENABLE","default_ipv6_check":"CFGOPTION_2_ENABLE"}' \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/dns/advance/config"

域名接入 UX 改造 · 后端接口盘点(2026-06-26)

前端正在把域名管理页升级为「列表行内跳转 → 域名工作台 → 接入向导」三段式 UX。盘点结论:大部分是前端编排现有接口;域名工作台(Phase 2)落地后实测发现 2 处需后端补充(见表后「实测后端补充」)。对应关系:

前端能力 复用的现有接口 说明
行内跳转预过滤(源站 / 解析 / 日志 / 证书) GET /forwards?keyword= · GET /schedules/domains?keyword= · GET /analytics/logs?host= · 证书列表 keyword 均已支持按域名过滤,前端读 ?keyword= 预填,已落地
工作台 · 配置包(settings) GET /domains/{id}/settings独立端点 ⚠️ GET /domains/{id} 不含 settings,必须单独调此端点;前端已对齐
工作台 · 源站列表 GET /forwards?domain_id= domain_id 精确过滤
工作台 · 解析 / 接入状态 GET /schedules/domains?keyword=(含 parsing_state / node·src count)+ /records ⚠️ parsing_state 不在 Detail 里,前端从此端点取
工作台 · 监控概要 POST /analytics/batchdomain_id 单域名维度统计已支持
工作台 · 绑定证书 GET /domains/{id} 返回 cert_id Detail 已回传当前绑定证书 ID,0 表示未绑定
工作台 · 防护节点 GET /domains/{id} 返回 nodes[] Detail 已回传防护节点列表
向导 · 建域 / 源站 / 证书 / 解析 POST /domainsPOST /forwardsPOST /certs/{id}/bind/{domainId}POST /schedules/domains/{id}/init 全链路写接口已有

已补充 ①(工作台详情字段)

GET /api/guard/domains/{id} 在现有 DomainVO 上已追加 cert_id + nodes + shadow_cache_addr。其中 settingsparsing_state 仍按既有设计走独立端点(GET /domains/{id}/settingsGET /schedules/domains)。

字段 类型 取值来源
cert_id uint64(0 = 未绑定) 证书↔域名绑定关系按 domain_id 反查(即 POST /certs/{id}/bind/{domainId} 的反向)取该域名最新绑定的证书 id(兼容保留)
certs []DomainCertVO(未绑定时省略) 该域名绑定的全部证书(绑定是多对多,支持双证书/换证过渡并存)。每项含 id / name / certificate_type / common_name / issuer / expired_at / auto_cert,不含 PEM
nodes []DomainNodeVO 该域名的防护节点(字段 ip_addr / node_id / enabled / line
shadow_cache_addr string(未配置时省略) 云分身缓存节点 IP,取自 guard_platforms.cache_addr(全平台单行配置,与域名无关)。工作台在「云分身」开关说明里提示把它加进本地安全设备白名单;列表接口不返回

目标响应形状(在现有 DomainVO 上加 2 字段,对齐前端 DomainDetailVO):

{
  "domain_id": "...", "domain": "...", "cname": "...", "switches": {},
  "cert_id": 12,
  "shadow_cache_addr": "192.168.14.103",
  "nodes": [
    { "id": 1, "ip_addr": "1.2.3.4", "node_id": "n-001", "enabled": 1, "line": 0 }
  ]
}

前端可直接读 detail.cert_id(再 GET /certs/{id} 取 common_name/到期/cert_type)和 detail.nodes(节点表)。

接入向导用法(①.5)POST /domains 现已直接消费 src_sites + cert_id,域名、源站配置、证书绑定在一个事务内完成,任一步失败整体回滚。向导即按此一次建好,不再走「建域 → 逐源站 POST /forwardsPOST /certs/{id}/bind/{domainId}」的多请求变通。旧的分步接口仍然可用,用于给已存在的域名追加源站或换绑证书。

POST /api/guard/domains/{id}/verify — 域名接入主动探测

用途:向导「接入验证」步骤主动检查源站可达性与公网 DNS 是否已指向分配的 CNAME。

鉴权guard.domain.verify

输入参数:path id = domain_id

输出字段

字段 类型 说明
origin_reachable bool 后端从 service_config_setting 取第一个源站,做 TCP/80 探测
origin_latency_ms int64 TCP 连接耗时,探测失败为 0
dns_effective bool 公网 CNAME 是否等于该域名分配的 cname
resolved_cname string 实际解析到的 CNAME
checked_at int64 探测时间,Unix 毫秒

回源分组 /api/guard/domains/{id}/origin-groups

回源分组:一个分组 = 一批防护节点绑定其回源的源站子集,让"哪台防护节点回哪些源站"可控(如电信节点回电信源站)。每个域名恰好有 1 个默认回源组(承载全部源站、禁删、未分组节点的兜底);节点在域名内唯一归组;组内源站含 IPv6 时必须至少含一个支持 IPv6 的节点。3 个接口鉴权均为 guard.domain.origin_group

GET /api/guard/domains/{id}/origin-groups — 回源分组列表

用途:查询该域名回源分组列表(默认组+自定义组,默认组排最前)。

鉴权guard.domain.origin_group

输入参数:path id = domain_id

输出字段data.list[]OriginGroupVO):

字段 类型 说明
group_id int64 分组 ID
name string 分组名
is_default bool 是否默认回源组(承载全部源站、禁删)
node_list[] array 分组内防护节点,元素字段 node_id / name / line / ipv6
src_list[] array 分组内源站,元素字段 src_ip / line / ipv6
utime int64 更新时间(Unix 毫秒)

示例请求

curl -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/origin-groups"

POST /api/guard/domains/{id}/origin-groups — 新建回源分组

用途:归组操作——所选节点从原组拉入新组,被拉空的自定义组自动删除并回归默认组。节点/源站元数据由服务端回填,客户端只传标识。

鉴权guard.domain.origin_group

输入参数(path id = domain_id;请求体 OriginGroupCreateReq):

字段 类型 必填 说明
name string 分组名,max 100;不能与已有分组冲突或使用保留名
node_ids []string 防护节点 ID 列表,min 1;必须已绑定到该域名
src_ips []string 源站 IP 列表,min 1;必须在该域名源站配置内

输出字段data 为新建分组的 OriginGroupVO(字段同列表项)。

错误(400):分组名冲突/保留、默认组不存在、节点不在域名绑定列表、源站不在源站配置、组内源站含 IPv6 但无支持 IPv6 的节点。

示例请求

curl -X POST -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"电信组","node_ids":["n-a","n-b"],"src_ips":["10.0.0.1","10.0.0.2"]}' \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/origin-groups"

DELETE /api/guard/domains/{id}/origin-groups/{group_id} — 删除自定义分组

用途:删除自定义回源分组,组内节点/源站并回默认组;默认回源组禁删

鉴权guard.domain.origin_group

输入参数:path id = domain_id,path group_id = 分组 ID(int64)。

输出字段

字段 类型 说明
group_id int64 被删除的分组 ID

错误404 分组不存在;400 默认回源组禁删。

示例请求

curl -X DELETE -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/origin-groups/5301"

PUT /api/guard/domains/{id}/audit — 域名审核

用途:审核域名。新建域名默认 audit_status=1(未审核),未过审的域名不会下发到防护节点;置为 4(通过)后自动触发配置下发。

鉴权:仅平台级角色(role_id < 10)且持有 guard.domain.audit;业务角色即使被误授该权限也返回 403

输入参数(path id = domain_id;请求体):

字段 类型 必填 说明
audit_status int 审核状态:2=审核中 3=驳回 4=通过(仅允许这 3 个值)

输出字段

字段 类型 说明
domain_id string 域名 ID
audit_status int 更新后的审核状态

错误400 audit_status 参数非法;403 非平台角色或权限不足;404 域名不存在或无权访问。

示例请求

curl -X PUT -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"audit_status":4}' \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/audit"

域名节点分配 /api/guard/domains/{id}/nodes

节点分配:域名绑定哪些防护节点,决定其流量由哪些节点承载与清洗。5 个按域名接口鉴权均为 guard.domain.node;分配/移除成功都会触发配置重新下发,锁定只改状态不下发。另有运维对账入口 POST /api/guard/domains/nodes/syncguard.domain.edit,前端不暴露、仅 CLI),见本节末尾。

GET /api/guard/domains/{id}/nodes — 已分配节点列表

用途:查询域名已分配的防护节点(含锁定状态与节点元数据)。

鉴权guard.domain.node

输入参数:path id = domain_id

输出字段data.list[]DomainAssignedNodeVO):

字段 类型 说明
node_id string 节点 ID
name string 节点名
machine_room string 机房
ipv6 bool 节点是否支持 IPv6
ip_addr string 节点 IP
line int32 线路编码(电信/联通/移动等)
lock_status int32 1=正常 2=锁定(锁定后配置渲染与下发跳过该节点,绑定关系保留)

错误404 域名不存在或无权访问。

示例请求

curl -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/nodes"

GET /api/guard/domains/{id}/nodes/available — 可分配节点列表

用途:查询该域名还可分配的节点(账号节点池 − 已分配)。

鉴权guard.domain.node

输入参数:path id = domain_id

输出字段data.list[]AssignableNodeVO):node_id / name / machine_room / ip_addr / line / ipv6(含义同上表,无 lock_status)。

错误404 域名不存在或无权访问。

示例请求

curl -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/nodes/available"

PUT /api/guard/domains/{id}/nodes — 分配节点

用途:分配节点到域名(增量追加,重复分配的节点自动跳过)。节点同时同步进 CNAME 解析记录:enable_parse=true 时同时开启解析记录(节点调度模式下生效;回源模式只同步 last_status,下次加速自动开启);不勾选时记录会创建但不开启。成功后触发配置重新下发。

鉴权guard.domain.node

输入参数(path id = domain_id;请求体 DomainNodeAssignReq):

字段 类型 必填 说明
node_ids []string 节点 ID 列表,1-100 个;必须在账号节点池内
enable_parse bool 分配的同时开启这些节点的解析记录(默认 false)

输出字段dataDomainNodeAssignResultVO):

字段 类型 说明
added int 实际新增绑定数(重复分配的节点被跳过)
dns_synced bool 解析记录是否已同步(解析调度模块不可用时为 false)

错误400 所选节点不可分配(不在账号节点池内或已分配给该域名);404 域名不存在或无权访问。

示例请求

curl -X PUT -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"node_ids":["n-a","n-b"],"enable_parse":false}' \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/nodes"

DELETE /api/guard/domains/{id}/nodes/{node_id} — 移除节点

用途:从域名移除节点。成功后触发配置重新下发。

鉴权guard.domain.node

输入参数:path id = domain_id,path node_id = 节点 ID。

保护性拒绝(400)

输出字段

字段 类型 说明
domain_id string 域名 ID
node_id string 被移除的节点 ID

错误400 见上方保护性拒绝;404 域名不存在或该节点未分配给此域名。

示例请求

curl -X DELETE -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/nodes/n-a"

PATCH /api/guard/domains/{id}/nodes/{node_id}/lock — 锁定/解锁节点

用途:锁定/解锁域名节点。锁定(2)后配置渲染与下发跳过该节点,绑定关系保留;解锁(1)恢复。只改状态,不触发重新下发。

鉴权guard.domain.node

输入参数(path id = domain_id,path node_id = 节点 ID;请求体 DomainNodeLockReq):

字段 类型 必填 说明
lock_status int32 锁定状态:1=正常 2=锁定

输出字段

字段 类型 说明
domain_id string 域名 ID
node_id string 节点 ID
lock_status int32 更新后的锁定状态

错误400 锁定状态无效(仅允许 1/2);404 域名不存在或该节点未分配给此域名。

示例请求

curl -X PATCH -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"lock_status":2}' \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/nodes/n-a/lock"

POST /api/guard/domains/nodes/sync — 节点分配运维对账

用途运维对账工具——把所有域名的节点绑定与上层(aeg)的用户节点分配全量对齐(多退少补),节点被收回后跑一次可批量清理幽灵绑定。会覆盖按域名的手动精细分配,日常增量分配请用 PUT /api/guard/domains/{id}/nodes。前端已不再暴露此入口(仅 CLI zcloud guard domains nodes sync)。发生变更的域名触发配置重新下发。

鉴权guard.domain.edit。平台级用户可用 ?user_id= 定向对账某个客户,业务级用户只对账自己的域名。

输入参数:query user_id(可选)= 只对账指定用户的域名。

输出字段dataDomainNodeSyncVO):

字段 类型 说明
domains int 发生变更的域名数
added int 补绑的节点绑定数
removed int 解绑的节点绑定数(幽灵绑定清理)
changed_domain_ids []string 发生变更的域名 ID(无变更时省略)

示例请求

curl -X POST -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  "https://waf.example.com/api/guard/domains/nodes/sync"

域名防暴力破解规则 /api/guard/domains/{id}/brute-force/rules

防暴力破解:域名级请求频率规则——同一源 IP / Bot 会话在统计时长内对指定 URI 的请求次数超过阈值后执行处置动作(封禁/跳转/验证码等)。规则存共享表并联动下发;4 个接口鉴权均为 guard.domain.bruteforce;新增/更新/删除成功都会触发配置重新下发。弱密码拦截配置不在本组接口,走已有域名设置接口的 guard_weak_password_setting 项(GET/PUT /api/guard/domains/{id}/settings)。

GET /api/guard/domains/{id}/brute-force/rules — 规则列表

用途:查询域名的防暴力破解规则(按规则 ID 降序)。

鉴权guard.domain.bruteforce

输入参数:path id = domain_id

输出字段data.list[]BruteForceRuleVO):

字段 类型 说明
id int64 规则 ID(服务端生成)
name string 规则名称
describe string 规则描述
uri string 防护 URI
rate int64 请求次数阈值
rate_time int64 统计时长
req_time_unit string 统计时长单位:ReqUnit_1_PSec=秒 | ReqUnit_2_PMin=分
status bool 规则开关
level string 限制级别:ip(按源 IP)| bot_session(按 Bot 会话)
action object 处置动作,字段见下表

action 对象字段:

字段 类型 说明
action_type string 处置动作:block / pass / jump / log / js_check / meta_check / captcha
block_time int64 封禁时长
block_time_unit string 封禁时长单位:ReqUnit_1_PSec | ReqUnit_2_PMin
code string 自定义响应状态码(510-599)
content string 自定义响应内容
limit int64 限速值
window int64 限速窗口
window_unit string 限速窗口单位:ReqUnit_1_PSec | ReqUnit_2_PMin
jump_addr string 跳转地址(action_type=jump 时生效)

错误404 域名不存在或无权访问。

示例请求

curl -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/brute-force/rules"

POST /api/guard/domains/{id}/brute-force/rules — 新增规则

用途:新增防暴力破解规则。规则 ID 服务端生成;status 服务端强制 true(新增即启用)。成功后触发配置重新下发。

鉴权guard.domain.bruteforce

输入参数(path id = domain_id;请求体 BruteForceRuleCreateReq,字段同上方 BruteForceRuleVO,无 id)。注意:

输出字段data 即创建后的 BruteForceRuleVO,含服务端生成的 id)。

错误400 请求体/枚举值非法;404 域名不存在或无权访问。

示例请求

curl -X POST -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"login-guard","describe":"登录接口限速","uri":"/login","rate":10,"rate_time":60,"req_time_unit":"ReqUnit_1_PSec","level":"ip","action":{"action_type":"block","block_time":10,"block_time_unit":"ReqUnit_2_PMin"}}' \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/brute-force/rules"

PUT /api/guard/domains/{id}/brute-force/rules/{rule_id} — 更新规则

用途:更新防暴力破解规则——白名单字段(name/describe/uri/rate/rate_time/req_time_unit/status/level/action整行覆盖,未提供的字段按零值写入;rule_id 必须属于该域名(防跨域名改共享表)。status 按请求值落库(可用于停用规则)。成功后触发配置重新下发。

鉴权guard.domain.bruteforce

输入参数:path id = domain_id,path rule_id = 规则 ID;请求体 BruteForceRuleUpdateReq(字段与新增一致,枚举约束同上)。

输出字段data 即更新后的 BruteForceRuleVO)。

错误400 请求体/枚举值非法或 rule_id 非数字;404 域名不存在、规则不存在或不属于该域名。

示例请求

curl -X PUT -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name":"login-guard","describe":"登录接口限速","uri":"/login","rate":20,"rate_time":1,"req_time_unit":"ReqUnit_2_PMin","status":true,"level":"bot_session","action":{"action_type":"captcha"}}' \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/brute-force/rules/20220414"

DELETE /api/guard/domains/{id}/brute-force/rules/{rule_id} — 删除规则

用途:删除防暴力破解规则(域名关联摘除 + 物理删行);rule_id 必须属于该域名。成功后触发配置重新下发。

鉴权guard.domain.bruteforce

输入参数:path id = domain_id,path rule_id = 规则 ID。

输出字段

字段 类型 说明
domain_id string 域名 ID
rule_id int64 被删除的规则 ID

错误400 rule_id 非数字;404 域名不存在、规则不存在或不属于该域名。

示例请求

curl -X DELETE -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/brute-force/rules/20220414"

域名缓存动作 /api/guard/domains/{id}/cache

缓存动作:对域名的 CDN 缓存做预热/清理。两个接口均为异步语义:服务端按域名节点关系与对外服务配置构造 CDN 缓存指令,经老平台 gen-service(zRPC cmd=554 → MQ)下发节点执行,发送即返回——不落库、无执行结果查询接口,返回成功仅代表指令已提交下发通道。两个接口鉴权均为 guard.domain.cache。缓存规则/高级配置/预热资源列表不在本组接口,走已有域名设置接口的 cache_config_v2_setting 项(GET/PUT /api/guard/domains/{id}/settings)。

POST /api/guard/domains/{id}/cache/warm — 预热缓存资源

用途:预热指定资源到节点缓存(对齐老平台"缓存预热",缓存指令 cmd_type=2)。异步:发送即返回。

鉴权guard.domain.cache

输入参数(path id = domain_id;请求体):

字段 类型 说明
cache_res string 预热的资源路径,多条用逗号/空格/回车分隔;可空(原样透传到节点缓存指令)

输出字段

字段 类型 说明
domain_id string 域名 ID
action string 恒为 warm

错误400 请求体非法、域名未分配节点、对外服务配置(service_config_setting)缺失或非法;404 域名不存在或无权访问;500 下发通道不可用。

示例请求

curl -X POST -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cache_res":"/index.html,/static/app.js"}' \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/cache/warm"

POST /api/guard/domains/{id}/cache/purge — 清理缓存资源

用途:清理节点上指定资源的缓存(对齐老平台"缓存清理",缓存指令 cmd_type=1)。异步:发送即返回。

鉴权guard.domain.cache

输入参数(path id = domain_id;请求体):

字段 类型 说明
cache_res string 清理的资源路径,多条用逗号/空格/回车分隔;可空(原样透传到节点缓存指令)

输出字段

字段 类型 说明
domain_id string 域名 ID
action string 恒为 purge

错误400 请求体非法、域名未分配节点、对外服务配置(service_config_setting)缺失或非法;404 域名不存在或无权访问;500 下发通道不可用。

示例请求

curl -X POST -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cache_res":"/index.html,/static/app.js"}' \
  "https://waf.example.com/api/guard/domains/d_8a3b1c/cache/purge"

5.2 证书 /api/guard/certs

证书管理走"上传 PEM 文本 → 绑定域名"两步流程;Content-Type 是 application/json,不是 multipart/form-data

三种证书类型certificate_type,与老平台共库同值):

类型 必填材料 说明
1 标准 TLS(缺省) cert + key 常规 RSA/ECDSA 证书;另可选带一对签名证书 sign_cert + sign_key(标准算法,非 SM2)
2 国密 NTLS sign_cert + sign_key(签名对)与 cert + key(加密对) SM2 双证书。共库没有独立的 enc 列,加密对复用 cert/key 字段
3 无私钥(keyless) cert + no_key_tls_addr 私钥留在客户自己的 keyless 服务器上,本地不存;key 传了也会被丢弃

ssl_password_file(加密证书凭证/私钥口令)对类型 1、2 可选,类型 3 无本地私钥因而忽略。

GET /api/guard/certs — 证书列表

用途:"防护管理 - 证书"页面表格。

鉴权guard.cert.list

输入参数page / size / keyword(搜索 name/common_name)。

输出字段data.list[] = CertVO):

字段 类型 说明
id uint64 证书唯一 ID
name string 自定义名称
user_name string 归属用户名(P1.3 替换原 user_id,来源 cloud sys.users
certificate_type int32 证书类型:1 标准 TLS / 2 国密 NTLS / 3 无私钥
common_name string 证书 CN
issuer string 颁发者
expired_at int64 到期时间,Unix 毫秒
auto_cert bool 是否自动续期
ctime / utime int64 创建/更新时间

不存在 bound_domains 字段;要查证书绑定哪些域名,调 GET /api/guard/certs/{id}/domains

可视化建议


POST /api/guard/certs — 上传证书

用途:上传一条 PEM 证书。客户对接最容易踩的坑就是误用 multipart/form-data,请务必用 JSON。

鉴权guard.cert.create

安全提示:直接集成本接口时,cert / key 仍按 JSON PEM 文本传入;但不要把私钥写入 AI 对话、工单、日志或可观测埋点。若通过 Aegeon Cloud 对话助手操作证书,优先使用其"安全证书附件"上传入口:浏览器将证书/私钥上传到 Aegeon 后只在对话中保留附件引用,私钥不会进入 AI 消息内容。

输入参数(请求体 CertUploadReqapplication/json):

字段 类型 必填 说明
name string 证书名
certificate_type int32 1 标准 TLS(缺省)/ 2 国密 NTLS / 3 无私钥
cert string PEM 文本字符串(含 -----BEGIN CERTIFICATE----- 头尾);类型 2 时为加密证书
key string 视类型 私钥 PEM 文本;类型 1、2 必填(类型 2 时为加密密钥),类型 3 传了会被丢弃
sign_cert string 视类型 签名证书 PEM。类型 2 必填(SM2);类型 1 可选(标准 RSA/ECDSA)
sign_key string 视类型 签名私钥 PEM。与 sign_cert 同进同出
no_key_tls_addr string 视类型 无私钥服务器地址 IP:端口域名:端口。类型 3 必填,其余类型忽略
ssl_password_file string 加密证书凭证(私钥口令);类型 3 忽略

输出字段:返回新建 CertVO

可视化建议:不适合图表。前端上传组件应支持"粘贴 PEM 文本"和"读取本地文件"两种方式。

示例请求

curl -X POST https://waf.example.com/api/guard/certs \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name":"prod-2026","cert":"-----BEGIN CERTIFICATE-----\nMII...\n-----END CERTIFICATE-----","key":"-----BEGIN PRIVATE KEY-----\nMII...\n-----END PRIVATE KEY-----"}'

国密 / 无私钥示例

# 国密 NTLS:签名对 + 加密对四份材料缺一不可
curl -X POST https://waf.example.com/api/guard/certs \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"gm-2026","certificate_type":2,"sign_cert":"-----BEGIN CERTIFICATE-----\n...","sign_key":"-----BEGIN PRIVATE KEY-----\n...","cert":"-----BEGIN CERTIFICATE-----\n...","key":"-----BEGIN PRIVATE KEY-----\n..."}'

# 无私钥:只传证书 + keyless 服务器地址
curl -X POST https://waf.example.com/api/guard/certs \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"keyless-2026","certificate_type":3,"cert":"-----BEGIN CERTIFICATE-----\n...","no_key_tls_addr":"10.0.0.9:8443"}'

常见误用


GET /api/guard/certs/{id} — 证书详情

用途:详情页展示,含完整 PEM 文本。

鉴权guard.cert.view

输入参数:path id

输出字段CertDetailVO = CertVO + cert + sign_cert(PEM 文本,方便下载)+ no_key_tls_addr + has_ssl_password

私钥材料(key / sign_key)一律不回显no_key_tls_addr 不是密钥,随详情返回供编辑回填。
口令(ssl_password_file)也不回显原文,只返回布尔 has_ssl_password —— 它是解私钥的凭证,回显等于让任何有 guard.cert.view 的只读账号取走。老平台同样只进不出。
因此前端编辑时「口令留空」只能表示保持原口令;要清空必须显式提交空串。

可视化建议:表单展示。可加"下载证书"按钮(前端拼 PEM 触发下载)。


Keyless 安装包接口

下载权限为 guard.cert.download;接口只返回公开发布包的元数据或安装包,
不接收、拼接或记录业务私钥。首次部署不需要先创建 certificate_type=3
证书
:必须先安装 Keyless,才能获得后续表单需要填写的 HOST:8443

GET /api/guard/keyless/package 返回版本、支持的 Linux 架构、ZIP 验证包及其
内含 tar.gz 的 SHA-256、完整性校验状态和使用说明。每个
发行版本在受控 CI 中由仓库内固化、仅含源码的 KeyServer 快照交叉编译
amd64/arm64,随后连同 SHA-256 校验文件写入不可变 Cloud 后端镜像;
后端再次核对完整性后才显示下载入口。发布目录默认是 /app/releases/keyless。

POST /api/guard/keyless/package/download

请求体:

{"arch":"amd64"}

响应是 ZIP 验证包,并带 X-Artifact-SHA256。ZIP 内有 tar.gz、其 SHA-256、
VERIFY.txt;先按 VERIFY.txt 在解压 tar.gz 前验证,再执行安装。下载动作写入
操作审计。校验和、包结构任一项
不通过时不会提供下载。安装包内提供 verify-package.sh、install.sh、
provision.sh、restart.sh、status.sh 和 verify-transport.sh,完整使用方法见
安装包内 docs/QUICKSTART.md;首次安装可执行:

sudo ./scripts/install.sh --copy-private-key-file /absolute/path/to/business.key --enable

推荐的一键安装方式只在目标 Linux 主机读取并校验源私钥,再复制到
/etc/keyless/keys/business.keykeyless:keyless0600);源文件不修改、
不上传平台。保留原始路径的高级方式仍可使用 --private-key-file 或手工编辑
user.yml,但必须自行保证 keyless 用户可读取该文件并可进入所有父目录。

服务启动并确认可达 HOST:8443 后,再在平台新建无私钥证书、粘贴匹配的
公开 fullchain 并填写该地址。KeyServer 只读取目标 Linux 主机上的未加密
RSA PEM/DER 私钥,不读取平台证书链。TLS 连通检查不等于远程签名已验证。

已有无私钥证书的集成可继续使用兼容接口
/api/guard/certs/{id}/keyless/package
/api/guard/certs/{id}/keyless/package/download;新界面和首次部署应使用
不依赖证书记录的全局接口。


PUT /api/guard/certs/{id} — 更新证书

用途:直接替换 PEM 文本(无需先删后建)。

鉴权guard.cert.edit

输入参数(请求体 CertUpdateReq,字段同 Upload 但全部可选):name / certificate_type / cert / key / sign_cert / sign_key / no_key_tls_addr / ssl_password_file

输出字段:返回更新后的 CertVO


DELETE /api/guard/certs/{id} — 删除证书

鉴权guard.cert.delete

输入参数:path id

输出字段datanull

副作用:删除前会自动解绑该证书绑定的所有域名。


GET /api/guard/certs/{id}/domains — 查询证书已绑定的域名

用途:在证书详情页展示"该证书正在保护哪些域名"。

鉴权guard.cert.view

输入参数:path id

输出字段data[] = CertDomainVO[]):

字段 类型 说明
domain_id string 域名 ID
domain string 域名
cert_id uint64 证书 ID(即 path id
ctime int64 绑定时间

可视化建议:表格展示。


POST /api/guard/certs/{id}/bind — 绑定证书到域名

用途:把指定证书绑到一个域名上。

鉴权guard.cert.edit

输入参数(请求体 CertBindReq):

字段 类型 必填 说明
domain_id string 目标域名 ID

输出字段datanull

可视化建议:不适合图表。


DELETE /api/guard/certs/{id}/bind/{domainId} — 解绑证书与域名

用途:解除证书与某个域名的绑定关系。

鉴权guard.cert.edit

输入参数:path id = 证书 ID;path domainId = 域名 ID。

输出字段datanull

路径:是 DELETE /bind/{domainId}不是 POST /unbind


证书管理 UX 改造 · 后端补充盘点(2026-06-29)

前端正在重设计证书管理页(到期概要 + 绑定域名 + 国密上传)。盘点结论:核心能力后端已具备(列表 / 上传含国密 sign_cert·sign_key / 详情含 PEM / 绑定·解绑 / GET /certs/{id}/domains),重设计主要是前端补类型 + 接线。下列为建议后端补充 / 澄清项:

性质 说明
证书类型徽章 建议补字段(否则只能名称启发式) 实测 certificate_type 在后端恒为 1(model.CertificateTypeTLS,上传时写死),不携带 DV/OV/EV 校验级别,也不区分 RSA/ECC/国密;且列表 CertVO 不返 PEM/sign_cert,算法无法客户端从列表推导。前端现变通:从证书名/CN 后缀(_RSA/_ECDSA/_SM2)启发式推断算法徽章,无后缀回落 TLS(不权威,可能误标)。要做权威「类型」需后端二选一:(a) 让 certificate_type 真正分级 + 加 key_algorithm 字段;(b) 后端解析已存 PEM,在列表 VO 返回 key_algorithm + is_gm(可选 san[])。
auto_cert 可写 建议(自动续期开关) CertUpdateReqname/cert/key/sign_*,不含 auto_cert。列表/详情若要做「自动续期」开关切换,需 update 接受 auto_cert(或单独端点);否则前端只能只读展示该状态。
申请免费证书(ACME) 可选(友商有) 现无签发/申请端点。如产品要做「一键申请 Let's Encrypt / DigiCert 免费证书」,需后端补签发流程端点;不做则前端隐藏该入口。

无需后端:SAN 列表 / 指纹 / 序列号 / 密钥长度等详情字段,前端可从 GET /certs/{id} 返回的 cert(PEM 全文)客户端解析,不必后端加字段。
前端待对齐(非后端项,备忘):前端 CertVO 类型为旧字段(cert_type/user_id),实际后端是 certificate_type/user_name;CertDetailVO.domains[] 后端不存在(绑定域名走 GET /certs/{id}/domains)。这些是前端要改的,不劳后端。


5.3 策略 /api/guard/policies

策略是规则的容器:CC / ACL / 黑白名单 都挂在策略下。一个域名绑一个策略。

GET /api/guard/policies — 策略列表

鉴权guard.policy.list

输入参数page / size / keyword

输出字段data.list[] = PolicyVO):

字段 类型 说明
policy_id string 策略 ID
name string 策略名
comment string 备注(不是 remark
user_id string 归属用户 UUID(保留兼容老平台)
user_name string 归属用户名(P1.3 新增,来源 cloud sys.users
default_main_rule_version string 默认主规则版本
is_default bool 是否系统默认策略
schema_id int64 schema 版本
cc_rule_count int32 该策略下的 CC 规则数
bwl_rule_count int32 黑白名单规则数
acl_rule_count int32 ACL 规则数
switches object 策略防护开关原样透传(只读)。waf 为 GuardMode 三态(GuardMode_1_Close / GuardMode_2_Log / GuardMode_3_Enable),其余为 CFGOPTION 两态(CFGOPTION_2_ENABLE / CFGOPTION_1_DISABLECFGOPTION_0_UNKNOWN = 未配置)。修改走 PUT /api/guard/policies/{id}/features/{key}
ctime / utime int64 创建/更新时间

switches 同时出现在创建 / 详情 / 列表响应里(三者共用同一 VO),域名接入向导据此展示所选策略当前开了哪些防护。新建策略的默认档:waf=GuardMode_3_Enablecc / acl / ddos / anti_crawler / black_white_list / user_priority = CFGOPTION_2_ENABLE,其余为 CFGOPTION_1_DISABLEgeo / acl_rule 保持 CFGOPTION_0_UNKNOWN)。

可视化建议:表格 + 三个 chip(cc/bwl/acl 数量)。


POST /api/guard/policies — 创建策略

鉴权guard.policy.create

输入参数PolicyCreateReq):name(必填)/ comment

重名口径:按属主(user_id判定,不是全局唯一——不同用户可以使用相同的策略名。同一属主下已存在同名策略时不报错,服务端自动追加 _01/_02… 序号后缀放行(与老平台 zmod CheckPolicyNameAlready 一致);名称本身已带 _0N 后缀时先剥掉后缀再从 _01 起找空位。

输出字段:返回新建 PolicyVO实际落库的名称以 data.name 为准,可能与请求里的 name 不同,请据此回显给用户。


GET /api/guard/policies/{id} — 策略详情

鉴权guard.policy.view

输入参数:path id

输出字段PolicyVO


PUT /api/guard/policies/{id} — 更新策略

鉴权guard.policy.edit

输入参数PolicyUpdateReq):name / comment

重名口径:改名时在该策略属主名下查重(排除自身),与老平台 zmod「修改防护策略」一致;撞名直接返回业务码 2004err.guard.policy.name_exists),不会像创建那样自动加后缀。

输出字段:返回更新后的 PolicyVO


DELETE /api/guard/policies/{id} — 删除策略

鉴权guard.policy.delete

输入参数:path id

输出字段datanull。删除前需保证该策略未被任何域名引用。


POST /api/guard/policies/{id}/copy — 复制策略

鉴权guard.policy.copy

输入参数:path id(源策略)。请求体全部可选:

字段 类型 说明
name string 新策略名称;留空则以源策略名为基自动追加 _01/_02… 求空位。查重范围是新策略属主名下,不是全局
user_id string 新策略归属用户;留空跟随源策略所有者(源为公共策略时归发起者)
domain_ids string[] 复制后顺带把这些已审核域名指派给新策略并触发下发

深拷贝源策略的全部配置 blob(界面没有的字段原样保留)与三张 live 规则子表(WAF 精准规则组+条目、CC 规则、访问控制规则),子表换新 ID、blob 内的 ID 数组同步重写。源策略不受影响。输出字段data 为新策略 VO。


GET /api/guard/policies/{id}/geo-config — 查询区域封禁配置

鉴权:guard.policy.view

返回 guard_policies.geo_config 的友好视图:mode(black=黑名单/white=白名单)、oversea(拒绝国外访问)、world_list/prov_list/city_list(国家/省份/城市)、custom_list(例外 IP 名单组)、stime/etime(生效整点,0-0=全天)、enabled(switches.geo 开关,只读)。

PUT /api/guard/policies/{id}/geo-config — 更新区域封禁配置

鉴权:guard.policy.edit

输入:mode(必填 black|white)/ oversea / world_list / prov_list / city_list / custom_list / stime / etime(0-23)。

custom_list(例外 IP 名单组)省略即保留服务端原值,显式传数组才写入(传 [] = 清空)。Cloud 访问控制界面不提供该项,zcloud guard policies geo-config update 也不下发它,以免覆盖已有的名单组绑定。

只落库不下发:变更计入待下发,统一走 POST /policies/{id}/publish。请求未包含的存量字段原样保留。

GET /api/guard/policies/{id}/sensitive-config — 查询敏感信息保护配置

鉴权:guard.policy.view

返回 guard_policies.sensitive_config 的透传视图:hide_sensitive(脱敏枚举位掩码 enum_sensitive + 枚举键数组 enum_sensitives + 自定义敏感信息 src_str)、info_leakage(信息泄漏防护)、hide_head(隐藏的服务信息头)、status_codes(异常状态码保护清单)、enabled(switches.sensitive_protection 开关,只读)。

PUT /api/guard/policies/{id}/sensitive-config — 更新敏感信息保护配置

鉴权:guard.policy.edit

输入:config(必填,整包 JSON:hide_sensitive / info_leakage / hide_head / status_codes)/ enabled(可选,同步 sensitive_protection 开关)。

config 整包透传:gen-server 拥有完整 schema,cloud 只落库并触发下发,请求未包含的存量字段原样保留。

GET /api/guard/policies/{id}/crawler-config — 查询网页防爬虫配置

鉴权:guard.policy.view

返回 guard_policies.crawler_config 的透传视图:search_engine/scanner/script_tool/other(四类识别拦截开关)、robots(自定义 robots.txt)、limit_often_err_req + limit_often_err_req_cfg(高频错误请求限制:default_code/custom_code/limit_rate/limit_rate_time(_unit)/deny_time(_unit))、enabled(switches.anti_crawler,只读)。

PUT /api/guard/policies/{id}/crawler-config — 更新网页防爬虫配置

鉴权:guard.policy.edit

输入:config(必填,整包 JSON)/ enabled(可选,同步 anti_crawler 开关)。整包透传,未包含的存量字段原样保留;落库并触发下发。

GET /api/guard/policies/{id}/global-blacklist-config — 查询全网协同防御配置

鉴权:guard.policy.view

返回 guard_policies.global_blacklist_config 的透传视图:block_time(恶意 IP 加入动态黑名单后的拦截时长,秒,候选 100/300/400/500/1000)、enabled(switches.global_blacklist,只读)。

PUT /api/guard/policies/{id}/global-blacklist-config — 更新全网协同防御配置

鉴权:guard.policy.edit

输入:config(必填,{block_time})/ enabled(可选,同步 global_blacklist 开关)。落库并触发下发。

GET /api/guard/policies/{id}/pending-changes — 待下发变更

鉴权:guard.policy.view

返回:dirty(策略修改时间晚于最近下发,从未下发视为 true)、last_modified_atlast_applychanges[](最近下发之后 cloud 侧的操作明细:action/resource_label/detail 字段级 diff/oper_name/ctime)。老平台界面同期修改只反映在 dirty,不出现在 changes

PUT /api/guard/policies/{id}/features/{featureKey} — 能力开关

鉴权:guard.policy.edit

输入:enabled(布尔);featureKey=waf 时可传 mode(disable=关闭/log=记录/block=拦截,填了 mode 则忽略 enabled)。

可用 featureKey:waf / cc / geo / global-blacklist / one-key-close / one-key-lock / tamper / sensitive / brute-force / crawler / web-lock

5.4 CC 规则 /api/guard/policies/{id}/cc/rules

CC = HTTP 速率限制规则(Connection / Concurrency Control)。挂在策略下,按 policy_id 隔离。

GET /api/guard/policies/{id}/cc/rules — CC 规则列表

鉴权guard.cc.list

输入参数:path id = policy_id;query page / size

输出字段data.list[] = CcRuleVO):

字段 类型 说明
rule_id int64 规则 ID
name string 规则名
describe string 描述
matches[] array 匹配条件结构(路径/方法/头部等)
stats object 统计聚合维度
limit object 速率限制阈值
action object 命中动作(拦截/验证码/限速等)
stime / etime int64 生效起止时间
status int32 1=启用 2=禁用
ctime / utime int64 创建/更新时间

可视化建议:表格 + status 徽章。matches 太复杂建议折叠为"详情"按钮弹出。


POST /api/guard/policies/{id}/cc/rules — 创建 CC 规则

鉴权guard.cc.create

输入参数CcRuleCreateReq):

字段 类型 必填 说明
name string 规则名
describe string 描述
matches[] array 至少 1 项匹配条件
stats object 统计维度(IP/URI/UA 等)
limit object 速率上限
action object 命中动作
stime / etime int64 生效时间窗

复杂结构建议先调 list 取一条样本作为模板。

输出字段:返回新建 CcRuleVO


GET /api/guard/policies/{id}/cc/rules/{rid} — CC 规则详情

鉴权guard.cc.view

输入参数:path id = policy_idrid = rule_id。会校验 rule_id 是否归属当前 policy_id,跨策略读取返回 NotFound

输出字段CcRuleVO


PUT /api/guard/policies/{id}/cc/rules/{rid} — 更新 CC 规则

鉴权guard.cc.edit

输入参数:path id + rid,body 同 Create。

输出字段:返回更新后的 CcRuleVO


DELETE /api/guard/policies/{id}/cc/rules/{rid} — 删除 CC 规则

鉴权guard.cc.delete

输入参数:path id + rid

输出字段datanull


PUT /api/guard/policies/{id}/cc/rules/{rid}/status — 切换 CC 规则状态

用途:在列表页用开关组件启用/禁用规则。

鉴权guard.cc.edit

输入参数CcRuleStatusReq):

字段 类型 必填 取值
status int32 1=启用 2=禁用

重要statusint32 数字,不是 "enabled" / "disabled" 字符串。

输出字段datanull

示例请求

curl -X PUT https://waf.example.com/api/guard/policies/p_default/cc/rules/12345/status \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"status":1}'

5.5 ACL 规则 /api/guard/policies/{id}/acl/rules

ACL = 访问控制列表(基于 IP / Header / URI 的放行/拦截)。结构与 CC 规则相似但没有 priority 字段。

GET /api/guard/policies/{id}/acl/rules — ACL 规则列表

鉴权guard.acl.list

输入参数:path id,query page / size

输出字段data.list[] = AclRuleVO):

字段 类型 说明
rule_id int64 规则 ID
name string 规则名
describe string 描述
matches[] array 匹配条件
action object 命中动作(block/page/pass
stime / etime int64 生效起止时间
status int32 1=启用 2=禁用
ctime / utime int64 创建/更新时间

可视化建议:表格 + status 徽章 + action.type chip 染色(block 红 / pass 绿 / page 蓝)。


POST /api/guard/policies/{id}/acl/rules — 创建 ACL 规则

鉴权guard.acl.create

输入参数AclRuleCreateReq):name / describe / matches[](≥1)/ action / stime / etime

action.typeblock / pagecontent 字段服务端会 base64 编码存储;调用方传明文。

输出字段:返回新建 AclRuleVO


GET /api/guard/policies/{id}/acl/rules/{rid} — ACL 规则详情

鉴权guard.acl.view

输入参数:path id + rid

输出字段AclRuleVO


PUT /api/guard/policies/{id}/acl/rules/{rid} — 更新 ACL 规则

鉴权guard.acl.edit

输入参数:path id + rid,body 同 Create。

输出字段:返回更新后的 AclRuleVO


DELETE /api/guard/policies/{id}/acl/rules/{rid} — 删除 ACL 规则

鉴权guard.acl.delete

输入参数:path id + rid

输出字段datanull


PUT /api/guard/policies/{id}/acl/rules/{rid}/status — 切换 ACL 规则状态

鉴权guard.acl.edit

输入参数AclRuleStatusReq):status int32(1=启用 2=禁用,数字)。

输出字段datanull


5.6 黑白名单 /api/guard/bwlist

黑白名单分两层:集合(set) 是逻辑容器(黑/白名单),IP 是集合内的具体条目。每个集合自带生效范围(scope)all=对当前用户全部域名生效(含后续新增域名),selected=只对指定域名生效。集合与域名的绑定由后端自动写入域名配置并触发下发,无需再到域名或策略下单独开开关。

存量兼容:早期集合通过 policy_id 绑定策略生效(此时 scope_type 返回 policy),仍继续工作;编辑该集合并选择新生效范围后自动迁移并解绑 policy_id。灰黑(3)/灰白(4)名单已废弃,列表默认不返回(include_grey=true 可见),仅兼容存量数据。

GET /api/guard/bwlist/summary — 账号级名单组计数

用途:判定链路节点/类型段控的计数。与列表分开:这组数不随搜索关键词变化,前端只在进页面时取一次。

鉴权guard.bwlist.list输出{black, white, cdn_white, total}

路径不挂在 /sets 下(/sets/summary 会与 /sets/{id} 的路由参数冲突)。


GET /api/guard/bwlist/sets — 名单集合列表

鉴权guard.bwlist.list

输入参数page / size / keyword / ip_set_type1=黑名单 2=白名单)/ policy_id / include_grey(默认 false,只返回黑/白名单)。

输出字段data.list[] = IPSetVO):

字段 类型 说明
id uint64 集合 ID
user_name string 归属用户名(P1.3 替换原 user_id,来源 cloud sys.users
name string 集合名
policy_id string 存量策略绑定(新集合为空)
ip_set_type int32 1=黑名单 2=白名单3/4 灰名单已废弃;数字枚举,不是字符串
status int32 1=禁用 2=启用
count int64 总条目数
enable_count int64 启用条目数
unable_count int64 禁用条目数
describe string 描述(不是 remark
is_default bool 是否默认集合
is_private bool 是否私有
private_domain_id string 私有集合关联的域名
scope_type string 生效范围:all=全部域名 selected=指定域名 policy=存量策略绑定 空=未关联
domain_ids array<string> selected 时的生效域名 ID 列表
domain_names array<string> 生效域名的友好名称(与 domain_ids 对应)
last_applied_at int64 最近一次同步生效时间(Unix 毫秒;0=尚未同步)
ctime / utime int64 时间戳

可视化建议

搜索keyword 一框多用 —— 组名 LIKE ∪ 描述 LIKE ∪ 组内 IP 文本 LIKE组内网段包含该 IP(搜 1.2.3.4 可命中装着 1.2.3.0/24 的组,对齐 zmod FindIPInIPSet)。网段包含仅在 keyword 是合法 IP 时触发。

GET /api/guard/bwlist/sets/{id} — 查询单个名单集合

鉴权guard.bwlist.list(与列表同权限)

输入参数:path id

输出字段:单个 IPSetVO(字段同上表,含 scope_type / domain_ids / domain_names)。

供控制台名单详情页深链/刷新时按 ID 加载。业务级用户只能读取归属自己的集合,读取他人集合返回 404——不区分「不存在」与「无权」,避免用 ID 探测他人数据;平台级用户不受此限。


POST /api/guard/bwlist/sets — 创建名单集合

鉴权guard.bwlist.create

输入参数BWListSetCreateReq):

字段 类型 必填 说明
name string 集合名
ip_set_type int32 1=黑名单 2=白名单(3/4 已废弃,不建议新建)
scope_type string 生效范围:all=全部域名 selected=指定域名;policy_id 二选一
domain_ids array<string> 视情况 scope_type=selected 时必填,至少 1 个
policy_id string 存量策略绑定路径(兼容保留,不建议新用)
status int32 1=禁用 2=启用,默认 2
describe string 描述

输出字段:返回新建 IPSetVO

指定 scope_type 时,后端把集合写入范围内每个域名的黑白名单配置(域名侧开关自动打开)并触发下发;scope_type=all 的集合对后续新增域名也自动生效。scope_typepolicy_id 同时传返回 400


PUT /api/guard/bwlist/sets/{id} — 更新名单集合

鉴权guard.bwlist.edit

输入参数BWListSetUpdateReq):name / status / describe / scope_type / domain_ids(全部可选)。

输出字段:返回更新后的 IPSetVO

修改 status 会同步域名配置并触发下发:启用集合进入黑/白名单,禁用集合进入禁用列表(dis_list)。传 scope_type 会重算生效范围:缩小范围时自动从移出的域名撤下配置;对存量策略绑定集合传 scope_type 会解绑 policy_id 并迁移到新范围模型。

status 不建议使用:集合级启停是本平台的扩展,旧平台没有这个概念(其名单组界面只有 名单信息/描述/IP/创建时间/操作),实际数据里也从未被使用。控制台与 zcloud CLI 均不提供该能力 —— 集合被禁用后控制台不会显示原因、也没有恢复入口。启停请用 IP 级的 PUT /api/guard/bwlist/ips/{id}/status
本接口仍接受 status 只为不破坏既有对接方;不传 = 不改动库中原值,故从旧平台导入的 status 会原样保留并按其语义下发。


DELETE /api/guard/bwlist/sets/{id} — 删除名单集合

鉴权guard.bwlist.delete

输入参数:path id

输出字段datanull会级联删除集合内所有 IP 条目

删除集合会同时从所有生效域名(及存量策略)的黑白名单配置中解绑,并触发相关域名重新下发。


GET /api/guard/bwlist/sets/{id}/ips — 集合内 IP 列表

鉴权guard.bwlist.ip_list

输入参数:path id = 集合 ID;query page / size / keyword(按 IP/CIDR 模糊搜索)。

输出字段data.list[] = IPVO):

字段 类型 说明
id uint64 条目 ID
ipset_id uint64 所属集合
ip_addr string IP 或 CIDR(不是 ip
status bool true=启用 false=禁用,默认 true
ctime / utime int64 时间戳

不存在有效期字段。名单条目没有到期概念(与 zmod 的 IPV2 模型一致):expired_at / expire_at / left_time / timeout / ttl 一律不接受也不返回,传了会被忽略。临时封禁请自行删除条目。

可视化建议:表格。


POST /api/guard/bwlist/sets/{id}/ips — 单条添加 IP

鉴权guard.bwlist.ip_add

输入参数BWListIPAddReq):

字段 类型 必填 说明
ip_addr string IP 或 CIDR
status bool 默认 true

输出字段:返回新建 IPVO


POST /api/guard/bwlist/sets/{id}/ips/batch — 批量添加 IP

用途:一次导入数百条 IP(如威胁情报源),减少多次往返开销。重复 IP 自动跳过(幂等)。

鉴权guard.bwlist.ip_add

输入参数BWListIPBatchAddReq):

字段 类型 必填 说明
ips[] array 至少 1 项;每项 {ip_addr, status?}

输出字段datanull 或返回新增 ID 列表(按实现)。


POST /api/guard/bwlist/sets/{id}/ips/batch/delete — 批量删除 IP

用途:一次删除集合下多条 IP(如清理过期封禁),一个事务原子完成,避免逐条删除的部分失败与审计刷屏。

鉴权guard.bwlist.ip_delete

输入参数BWListIPBatchDeleteReq):

字段 类型 必填 说明
ip_ids[] array<int64> 至少 1 项;要删除的 IP 条目 ID。删除范围限定在集合 {id} 内,不属于该集合的 ID 会被忽略

输出字段data.deleted 为实际删除条数。集合内无任一匹配返回 404


PUT /api/guard/bwlist/ips/{id}/status — 启停单条 IP

鉴权guard.bwlist.ip_add(复用写权限)

输入参数:path id = IP 条目 ID;body(BWListIPStatusReq):

字段 类型 必填 说明
status bool true=启用 false=禁用;禁用后该 IP 立即不参与名单匹配

输出字段:返回更新后的 IPVO


DELETE /api/guard/bwlist/ips/{id} — 删除单条 IP

重要:删除路径是顶级路径 DELETE /api/guard/bwlist/ips/{id}不是嵌套在 sets 下的 DELETE /sets/{sid}/ips/{id}

鉴权guard.bwlist.ip_delete

输入参数:path id = IP 条目 ID。

输出字段datanull


5.7 IP 转发 /api/guard/forwards

这是 TCP/UDP 端口转发(4 层),不是路径转发 / 反向代理(7 层)。

GET /api/guard/forwards — IP 转发列表

鉴权guard.forward.list

输入参数

字段 类型 说明
page / size int 标准分页
user_id string 按归属用户过滤
domain_id string 按域名过滤
status int32 1=禁用 2=启用
keyword string 搜索 domain / describe

输出字段data.list[] = ForwardVO):

字段 类型 说明
id uint64 转发 ID
user_id string 归属用户 UUID(保留兼容老平台)
user_name string 归属用户名(P1.3 新增,来源 cloud sys.users
domain string 转发的域名/IP
domain_id string 关联域名 ID
schema int32 3=TCP 4=UDP,默认 3
port int32 端口 1-65535
node_ipaddrs string 源 IP 列表(逗号分隔)
describe string 描述
status int32 1=禁用 2=启用
src_setting json 源设置 raw JSON
adv_settings json 高级设置 raw JSON
node_setting json 节点设置 raw JSON
dev_setting string 设备设置
ctime / utime int64 时间戳

可视化建议


POST /api/guard/forwards — 创建 IP 转发

鉴权guard.forward.create

输入参数ForwardCreateReq):

字段 类型 必填 取值 说明
domain string *.example.com 转发域名
domain_id string d_8a3b1c 关联域名
port int32 443 1-65535
schema int32 3 3=TCP(默认)/4=UDP
node_ipaddrs string 10.0.0.1,10.0.0.2 源 IP 列表
describe string 描述
status int32 2 1=禁 2=启用
src_setting / adv_settings / node_setting json raw JSON 配置
dev_setting string 设备配置

不存在 source_path / target 字段。

输出字段:返回新建 ForwardVO


GET /api/guard/forwards/{id} — IP 转发详情

鉴权guard.forward.view

输入参数:path id

输出字段ForwardVO


PUT /api/guard/forwards/{id} — 更新 IP 转发

鉴权guard.forward.edit

输入参数:path id,body 同 Create(字段全部可选)。

输出字段:返回更新后的 ForwardVO


PUT /api/guard/forwards/{id}/status — 启停 IP 转发

鉴权guard.forward.status

输入参数:path id,body 只有一个字段。

字段 类型 必填 取值 说明
status int32 1 / 2 1=禁用 2=启用(oneof=1 2 强校验)

PUT /api/guard/forwards/{id} 的区别:本接口只改状态一列,跳过节点归属与源站的全量校验
防护节点被回收、下线或从未迁入 cloud 节点库的历史规则(zmod 导入数据常见),走全量更新会被
「选择了未分配或不可用的防护节点」拒绝,导致规则连停都停不掉——而停用恰是此时最需要的操作。
老平台 zmod 的对应接口(PATCH /api/guard/forward/ipfromward/ipfromwards/{id}/status)同样不做这层校验。

仍然保留:租户隔离、域名绑定的历史规则不可在此单独启停(需先转为独立转发)、启用时检查同节点同协议端口占用。
状态实际发生变化时触发配置下发;状态未变则幂等返回、不下发。

输出字段:更新后的 ForwardVO(含 apply_triggered / apply_message)。


DELETE /api/guard/forwards/{id} — 删除 IP 转发

鉴权guard.forward.delete

输入参数:path id

输出字段datanull


源站转发 UX 改造 · 后端补充盘点(2026-06-30)

前端正在重设计源站转发页(对标友商「非网站 / 端口转发」:阿里云 DDoS 高防端口接入、腾讯云 BGP 高防非网站防护)。友商一条 L4 转发规则可配:转发协议(TCP/UDP)、转发端口、源站端口(与转发端口分离)、源站 IP/域名(阿里 ≤20 个逗号分隔自动负载均衡、腾讯 ≤20 个/规则)、权重 + 负载均衡算法会话保持(开关 + 超时)、健康检查(TCP/UDP 探测:间隔 / 响应超时 / 健康 + 不健康阈值 / 端口,自动剔除异常源站)、新建连接 + 读写超时、连接 / 新建速率限速;列表展示每源站健康态 + 实时连接数 / 带宽。盘点我们现状如下:

# 性质 说明
schema 语义前端写错 前端 bug(非后端) 后端 schema = 3=TCP / 4=UDP(L4),但前端 forward/index.vue 误当 1=HTTP / 2=HTTPS / 3=both 展示 + 下拉。前端改回 TCP/UDP。记此防再错。
源站 IP 不可编辑 前端 bug(非后端) 源站地址在 node_ipaddrs(逗号分隔 IP 列表),但当前表单根本没这个字段 → 建出来的转发规则没源站。重设计必须加「源站 IP」编辑(对齐友商:≤20 个逗号分隔,自动负载均衡)。后端已支持,无需改。
src_setting / adv_settings / node_setting JSON schema 未定义 需后端澄清(阻塞结构化高级表单) 友商的 源站端口 / 会话保持 / 健康检查 / 源站权重 / 负载均衡算法 / 连接超时 / 限速 在我们这只能塞进这三个 raw JSON,但 dto / model / CLI 都没定义其结构,节点如何消费也未文档化。请后端给出这三个 JSON 各自的字段 schema(哪个字段承载 源站端口 / 会话保持〔开关+超时〕/ 健康检查〔间隔/超时/阈值/端口〕/ 权重 / 负载均衡算法 / 超时 / 限速),否则前端只能给 raw JSON 文本框(易错且丑)。
源站健康状态不回传 建议(展示用) 友商列表展示每源站 健康 / 异常 + 自动剔除。ForwardVO 不含源站健康检查结果。要展示需后端补 健康状态字段 / 查询接口(每个 node_ipaddr 的存活态)。
转发规则实时监控缺失 可选(展示用) 友商展示每条规则实时 连接数 / 带宽。我们无。如做,走 chart 契约(规则 6)新增 analytics chart-key,勿在 forward 接口里塞统计。

结论:① ② 前端自查即可修(后端已支持);③ 是关键阻塞 —— 后端给出 raw JSON schema 前,「高级配置」(源站端口 / 会话保持 / 健康检查 / 权重)无法做成结构化表单,前端先做 协议 + 转发端口 + 源站 IP + 描述 + 启用 的核心闭环,高级项待后端 schema。④ ⑤ 是展示增强,可选。


5.8 调度管理 /api/guard/schedules

本模块只做 DNS 解析调度(域名解析模式切换 / 批量启停记录)。 旧版 cron 定时任务接口已下架。

⚠️ 异步生效约定:本组接口先落库兼容表 guard_db.dns_records + guard_db.dns_affairs,随后由 cloud DNS worker 调用 DNS provider API 异步生效。前端 / 调用方必须轮询 affairs 接口获取最终状态(初始 AffairsStatus_StartAffairsStatus_Succeed / AffairsStatus_Faild)。

⚠️ 数据真值源dns_records.group_type 是模式真相源,guard_configs.parsing_state 异步同步,均来自老平台 guard_db;存在最长 30s 不一致窗口。VO 同时返回两者,前端在不一致时显示"同步中" badge。

5.8.1 域名调度

GET /api/guard/schedules/domains — 域名列表

鉴权guard.schedule.list

输入参数

字段 类型 说明
page / size int 分页(size 上限 100)
keyword string 按域名模糊搜索
user_id string 按归属用户过滤(仅超管/总代有效)
mode int32 0=全部 / 1=源站(SRC) / 2=节点(NODE)

输出字段data = ScheduleDomainListResp):

字段 类型 说明
list[].domain_id string 域名 ID(guard_configs.domain_id
list[].domain_name string 域名
list[].user_id / user_name string 归属用户
list[].parsing_state int32 guard_configs.parsing_state(1=SRC / 2=NODE)
list[].dns_group_type int32 dns_records.group_type 多数票(真值源,1=SRC / 2=NODE)
list[].src_count int group_type=1 的记录数
list[].node_count int group_type=2 的记录数
list[].src_records[] array 源站解析摘要:subdomain / record_type / record_line / value / status
list[].node_records[] array 节点解析摘要:subdomain / record_type / record_line / value / status
list[].last_affair_status string 最近一条事务状态(AffairsStatus_Start / AffairsStatus_Succeed / AffairsStatus_Faild
list[].last_affair_ctime int64 最近一条事务创建时间(毫秒)
list[].last_affair_message string 最近一条事务消息(HTML 片段)
total int64 总条数

POST /api/guard/schedules/domains/{id}/switch-mode — 切换源站/节点

鉴权guard.schedule.switch

输入参数

字段 类型 必填 说明
path id string domain_id
body target_mode int32 1=源站(SRC) / 2=节点(NODE)
body comment string 事务备注(写入 dns_affairs.message

实现要点:guard_db 单库事务内 SELECT FOR UPDATE 锁住该域名全部 dns_records → 更新 group_typeswitch_state=2(切换中)→ INSERT dns_affairsstatus=AffairsStatus_Start)→ 提交后 NSQ Cmd=0 通知。

输出字段:返回新建 ScheduleAffairVO,前端应将 affairs_id 写入轮询轮换。


POST /api/guard/schedules/domains/switch-mode — 批量切换源站/节点

鉴权guard.schedule.switch(与单域名切换同一权限,不新增 perm key)

输入参数

字段 类型 必填 说明
body domain_ids string[] 域名 ID 列表,1~100 个;重复 ID 自动去重
body target_mode int32 1=源站(SRC) / 2=节点(NODE)
body comment string 事务备注(写入 dns_affairs.message

实现要点:与老平台 PATCH /api/guard/schedule/domain/dns/switchid_list 语义对齐 ——
整批一个事务,任一域名校验失败则全部回滚(老平台原话「放弃执行」),不做部分成功。
事务内逐个域名执行与单切完全相同的校验(FOR UPDATE 锁记录、无进行中事务、目标模式有可生效记录、
无已启用的锁定记录),随后只 INSERT 一条 dns_affairs,其 content逗号分隔的 domain_id 列表
(老平台同款格式,cloud 的 LatestByDomainIDs 本就按逗号切分读取,两平台可互读)。
提交后 cloud DNS worker 逐个更新 guard_configs.parsing_state 并调用 DNS provider。

输出字段:返回新建 ScheduleAffairVO(一条,对应整批)。


POST /api/guard/schedules/domains/{id}/init — 初始化解析

鉴权guard.schedule.init

输入参数:path iddomain_id);body 可选 comment

实现要点:读取 guard_domain_settings 的源站/调度配置与 domain_node_ships 节点绑定,锁住该域名旧 dns_records 后删除并重建源站/节点记录;随后 INSERT dns_affairs,以 NSQ Cmd=0 通知 zdns 同步一次。

输出字段:返回新建 ScheduleAffairVO


POST /api/guard/schedules/domains/{id}/reset — 重置解析

鉴权guard.schedule.reset

输入参数:path iddomain_id);body 可选 comment

实现要点:所有 dns_records.switch_state 归位为 1status 回滚到 last_status;落事务后 NSQ Cmd=0 通知。

输出字段:返回新建 ScheduleAffairVO


GET /api/guard/schedules/domains/{id}/records — 域名解析记录

鉴权guard.schedule.records

输入参数

字段 类型 说明
path id string domain_id
group_type int32 1=SRC / 2=NODE
status int32 1=禁用 / 2=启用
page / size int 分页

输出字段data = ScheduleRecordsResp):

字段 类型 说明
list[] DnsRecordVO 解析记录视图
list[].record_id string 主键
list[].associated_id string DNS 服务商侧解析记录 ID,用于确认记录已在 ZDNS/服务商侧重建或关联
list[].domain / subdomain / value string 域 / 子域 / 解析值
list[].record_type int32 DNS 记录类型内部编码
list[].record_line int32 解析线路
list[].ttl int64 TTL(秒)
list[].status / last_status int32 1=禁用 / 2=启用
list[].group_type int32 1=SRC / 2=NODE
list[].switch_state int32 1=就绪 / 2=切换中
list[].ctime / utime int64 时间戳(毫秒)

直查 zdns_db.dns_records,只读,不落事务。


5.8.2 解析调度

POST /api/guard/schedules/domains/{id}/records — 新建解析记录

鉴权guard.schedule.batch

输入参数ScheduleRecordCreateReq):

字段 类型 必填 说明
path id string domain_id
record_type int32 1=A / 2=AAAA(CNAME 由源站域名模式自动生成,不支持手工新增)
value string A=IPv4 / CNAME=域名,≤255 字符
ttl int64 0=默认 600;否则 60-86400 秒
group_type int32 1=源站组 / 2=节点组
record_line int32 解析线路:1-7 基础 / 14 搜索引擎 / 34-126 省级;0 或缺省=默认线路

语义要点:目标分组与当前解析模式一致时记录立即启用,否则建为禁用态;相同分组+类型+线路+值查重;默认线路守卫——分组内存在启用 A 记录时必须保留至少一条默认线路(Line 1),否则未匹配任何运营商线路的访客将解析落空(对齐 zmod NoIPv4DefaultLine)。

输出字段data 为调度事务 ScheduleAffairVO


PUT /api/guard/schedules/records/{id} — 编辑解析记录

鉴权guard.schedule.batch

输入参数ScheduleRecordUpdateReq,三字段均必填):

字段 类型 说明
path id string record_id
value string 按记录原类型校验(A=IPv4 / CNAME=域名)
ttl int64 60-86400 秒
record_line int32 同新建的 record_line 值域

语义要点:类型与分组不可改(改型走删除重建);锁定记录(mark=2)拒绝编辑;改动后过默认线路守卫。

输出字段data 为调度事务 ScheduleAffairVO


DELETE /api/guard/schedules/records/{id} — 删除解析记录

鉴权guard.schedule.batch

语义要点:锁定记录、当前模式最后一条生效记录、以及删除后打破默认线路守卫的操作均被拒绝。

输出字段data 为调度事务 ScheduleAffairVO


POST /api/guard/schedules/records/batch-status — 批量启停记录

鉴权guard.schedule.batch

输入参数ScheduleBatchStatusReq):

字段 类型 必填 说明
record_ids string[] 目标 record_id 集合
status int32 1=禁用 / 2=启用
comment string 事务备注

实现要点UPDATE dns_records SET last_status=status, status=? WHERE record_id IN (?);落事务后 NSQ Cmd=0 通知。

输出字段:返回新建 ScheduleAffairVO


5.8.3 事务记录

GET /api/guard/schedules/affairs — 事务列表

鉴权guard.schedule.affairs

输入参数

字段 类型 说明
page / size int 分页
user_id string 按归属用户过滤
status string AffairsStatus_Start / AffairsStatus_Succeed / AffairsStatus_Faild
ctime_from / ctime_to int64 时间范围(毫秒)
domain_id string 按受影响 domain 过滤(实现走 content LIKE

输出字段data = ScheduleAffairListResp):见下方 ScheduleAffairVO


GET /api/guard/schedules/affairs/{id} — 事务详情

鉴权guard.schedule.affairs

输入参数:path idaffairs_id)。

输出字段data = ScheduleAffairVO):

字段 类型 说明
affairs_id string 事务 ID,格式 {ts}_{rand10}
user_id / user_name string 操作人
status string AffairsStatus_Start / AffairsStatus_Succeed / AffairsStatus_Faild(字符串枚举,对齐老平台 MarshalJSON 行为)
message string 事务消息,含 HTML 片段(如 <br>
content string 受影响 domain_id,逗号分隔
json_content object 扩展 JSON,含 outbox 重试计数等
affairs_oper string AffairsOperType_Page / AffairsOperType_Cron / AffairsOperType_Cli
ctime / utime int64 创建/更新时间(毫秒)

轮询建议:前端发起写操作后,每 2~5s 轮询一次本接口,直到 status 切换为 SucceedFaild;若长时间停在 Start,说明 cloud DNS worker 仍在执行或重试,可在 UI 显示"同步中"。


解析调度 UX 改造 · 后端补充盘点(2026-07-01)

前端将重设计解析调度页。对标友商:阿里云云解析「智能解析 / GTM 全局流量管理」、腾讯 DNSPod、Cloudflare Load Balancing,以及各家 WAF 的 CNAME 接入 + 灾备切换我们的模型 = 按域名在 源站(SRC=1) ↔ 防护节点(NODE=2) 间切换 DNS 解析(cloud 写兼容表并由 cloud DNS worker 调用 DNS provider API,每次写产生一条 affair 事务并由 worker 回写终态)。友商比我们多的能力:健康检查 + 自动 failover(节点挂→自动切源站)、权重负载均衡、智能线路(电信/联通/移动/geo)、单条记录 CRUD、TTL 编辑。据此盘点后端补充:

# 性质 说明
同步状态判定太弱 建议(重设计要用) 前端"同步中"目前靠 dns_group_type(按 SRC/NODE 记录数派生;大多域名两组都有 → 落到"对齐 parsing_state"分支,几乎不触发)+ 末条 affair=Start请在 ScheduleDomainVO 直接返回一个干净的 sync_state/is_syncing 字段(以 dns_records 真实 group_type vs guard_configs.parsing_state 的一致性 + 是否有未终结 affair 判定),别让前端用记录数猜。
全量启停工具 已收口 该类工具对新系统过于危险,保留内部实现但不挂 route、不注册 perm、不发布 API/CLI 文档;对外仅提供 batch-status 精确批量启停记录。
源站/节点健康状态 能力增强(健康展示 / failover 前提) 友商列表逐条展示 源站/节点 健康/异常 + 自动剔除。我们 DNSRecordVO 无健康字段,operator 只能盲切。若要做健康展示或自动 failover,需后端(或 zdns)返回每条记录/节点的存活态字段。
自动 failover 策略 能力增强(可选,工作量大) 友商:节点故障→CNAME 自动切节点 IP、极端时自动切回源站。我们只有手动 switch-mode。若产品要自动灾备,需后端策略引擎(健康探测 + 自动切换 + 事件通知)。先评估,非首期。
权重 / 智能线路 能力增强(看产品范围) DNSRecordVOweightrecord_line 目前只用 1=默认。若要 多源站权重负载均衡 / 分线路解析(电信/联通/移动/geo),需后端加 weight 字段 + 线路枚举 + zdns 侧支持。
单条记录 CRUD 能力增强 已补齐(2026-08-04) POST/PUT/DELETE /schedules/...records 均已上线:新增/编辑(值/TTL/线路)/删除,TTL 可写,含默认线路守卫。
affair message 是遗留中文 HTML 备忘(不阻塞) dns_affairs.message 是兼容表契约,保留中文 HTML(已记豁免)。若重设计要展示结构化事务状态/进度,建议后端另返一个结构化 status_detail(而非让前端从 HTML 抠)。并在 affair 上带 target_mode(切到源站=1/节点=2),否则「切换历史」时间线只能显泛化「解析切换」、标不出切到哪个模式(现已如此实现)。

前端自查项(非后端)DnsRecordVO.mark / .protect_status 前端类型声明了但后端 DTO 不返回 → 前端清掉;parsing_state 不在域名 Detail(专走 /schedules/domains)是有意拆分,不改。

结论:① ② 是重设计前应先定的(同步状态字段 + 孤儿接口去留),成本小。③④⑤⑥ 属于"要不要把解析调度从『手动源站/节点切换』升级成『带健康/权重/线路/CRUD 的智能解析』"的产品决策,依赖最终 mock 定的范围,先请后端评估 zdns_db + NSQ 侧可行性。核心闭环(手动 SRC/NODE 切换 + 记录批量启停 + init/reset + affair 轮询)后端已完整支持,重设计可先做纯 UX(域名中心化流程 + 更清晰的当前模式/同步状态 + 事务切换历史)。


5.8bis 运维:配置快照 + 源站级状态位 · 后端补充盘点(2026-07-01)

来源:域名中心化整体联动 review + 用户两个运维场景(几十个域名各关一个特定源站 / 操作前存快照、事后一键恢复)。前端已先行落地「配置快照 UI」(源站转发页,localStorage 暂存 + 用现有 PUT /forwards/:id 逐条回放),待以下接口就绪后切换为服务端实现。

# 需求 现状 / 缺口 建议后端
配置快照 CRUD + 恢复 无接口。前端临时:快照存浏览器 localStorage;恢复用 PUT /forwards/:id 逐条回放,仅回放仍存在的规则,删除/新增不 reconcile 服务端快照:POST /guard/snapshots(name+scope+payload)、GET /guard/snapshots?scope=POST /guard/snapshots/:id/restore原子回放,含增删 reconcile)、DELETE /guard/snapshots/:id。scope 先 forward,可扩 domain/schedule
源站级启用/禁用(例子1) forwards.node_ipaddrs 是逗号分隔字符串,单个源站无状态位;要「关 A 域名的源站1、保留源站2」只能进编辑删 IP(破坏性、删了即丢) 前端已采用向后兼容方案 + UI 就绪ForwardCreateReq/ForwardVOdisabled_ipaddrs(逗号分隔、已禁用源站子集,是 node_ipaddrs 的子集),create/update 已在发送;转发抽屉每个源站已有开关、列表已把禁用源站置灰划掉。后端只需 honor 该字段disabled_ipaddrs 里的 IP 不参与转发/负载均衡),先忽略也不影响存储。honor 后即生效、可一键开回。① 的快照因此可只带状态位
源站维度的跨域名批量 现批量是「整条规则同动作」,表达不了「A 源站1 + B 源站3」这种异构操作 可选:GET /guard/origins?...(按源站维度跨域名列出)+ 批量启停接口,配合 ② 的状态位

优先级建议:② 源站软关闭是地基,成本相对小、直接解掉「关了怎么恢复」;① 服务端快照其次;③ 看产品。②③ 依赖是否把 L4 转发从「整规则开关」升级为「源站粒度」,属产品决策,先请后端评估可行性。

④ 接入闭环 · 验证接入(对标大厂):接入向导完成页已加「验证接入」按钮,前端 interim 用 GET /guard/domains/:idproxy_switch && !stoping 判是否生效(被动、依赖后端异步检测)。建议后端加主动探测 POST /guard/domains/:id/verify-access:实时查 ① DNS 是否已 CNAME 指向我方 ② 回源是否可达,返回 { dns_ok, origin_ok, resolved_cname, message },让「验证接入」即点即得(对标阿里/腾讯/华为的接入检测)。


5.8ter 配置下发记录(Apply)· 后端补充盘点(2026-07-02)

前端把「配置下发记录」重设计成下发任务监控看板(列表进度条 + 详情节点按集群折叠 + 失败聚类 + 重试/取消 + 下发中实时轮询)。现有模型(ApplyVO 状态/成功失败数、ApplyNodeStatnode_group_name 分集群、ApplyDetailItemVO.apply_err 每节点错误、retryApplyApi(node_ids)/quitApplyApi)已足够支撑核心。以下是加固项(架构评审提出的边界):

⚠️ 架构现实(核对后端代码后必读,决定各项落点):cloud 是薄前台——apply + apply_detail 单库 ACID 写入 cloud_guard,经 outbox → zRPC cmd=550 → gen-service(老配置生成平台);真正下发到节点、并回写 apply_status(PENDING1→RUNNING4→SUCCESS2/FAILED3)/effect_status/apply_err 的是 gen-service(cloud 不主动写这些)outbox 已有 30s 未回写就重投。因此下面各项落点不同②severity 是 cloud 侧就能做(触发时 cloud 知道改的是啥,打标即可,最低成本、优先);①generation/回滚、③规范化错误码、④生效语义 多在 gen-service 侧(要老平台配合,非 cloud Go 改改);⑤超时/重投 cloud outbox 已部分覆盖(节点级最终一致仍在 gen-service); affair 走 zdns/NSQ、apply 走 gen-service/outbox,是两套后端,合并成本高。

# 需求 现状 / 缺口 建议后端
配置版本 generation + 回滚 apply 无版本号;同域名连续两次下发可能乱序(旧的晚到 → 节点落旧配置);无回滚 每次下发关联 generation(单调递增),节点只接受更新版本;POST /guard/applies/rollback(域名 + 目标 gen)。与配置快照 §5.8bis① 同一能力族
安全等级 severity 无字段区分「安全类配置」(拦截规则/CC/DDoS)vs 普通配置 apply 加 severity;安全类部分失败 = 红色告警(前端已按 severity 升级展示,待字段驱动)—— 部分节点未覆盖 = 流量暴露
每节点失败原因规范化 apply_err 已有,前端已做失败聚类(按 err 分组);若 err 是自由文本、聚类会碎 后端填规范化原因码(如 CERT_INVALID/TIMEOUT)+ 文案,聚类才准
生效探测真实性 effect_status 语义不清:是节点回执还是主动验证过配置在跑? 若只是回执,建议对已下发节点做生效校验回填;否则前端「已生效」是假确定性(现已用「待确认」措辞收敛)
离线节点最终一致 + 超时 下发时离线节点算「失败」还是「待补发」?卡死「下发中」如何兜底? 离线标「待补发」、上线自动补(不算失败);下发超时状态机(如 30s 未回执转失败)
统一变更中心(大决策) affair(DNS 切换,解析调度)和 apply(配置下发)本质同构:异步、面向节点、有状态、可重试;现分散在两页 长期可合成一个「变更/任务中心」,运维一处看所有 pending/失败变更。产品级决策,先各做各的

优先级:①(版本+回滚,防乱序 + 与快照一条线)和 ②(安全等级告警)最高;③④⑤ 是健壮性;⑥ 是长期架构。


5.8quater 防护节点与回源(租户视角)· 待后端接口(2026-07-02)

背景:cloud 定位为租户产品(用户从上层控制台跳转进入),「集群节点」管理(平台运维/节点列表/分组/ACL/分配/部署升级)已从 cloud 前端整体移除(归上层控制台);后端 /api/node 全部保留(agent 自注册、domain_node_ships 是调度/下发地基,上层平台直接调用)。租户侧需 2 个租户视角新接口(对标阿里/腾讯 WAF 的回源 IP 段公示 + 高防 IP 线路视图;前端已按此契约实现,接口未就绪时显示提示、不显示假数据)。
前端消费位置(2026-07-02 验收后调整,不做独立页)origin-ip-ranges接入向导完成步「源站放行回源 IP 段」块 + 域名工作台接入区「回源放行清单」弹窗(共享组件 OriginIpRanges.vue);my-nodes解析调度详情流量走向图「防护节点」上的健康徽标 + 线路名。

接口 说明 响应
GET /api/guard/my-nodes 当前用户被分配的防护节点聚合(按线路/分组,脱敏:只给展示名与统计,不暴露内部管理 IP/机房细节)。数据源 = 节点分配关系 + 节点状态 { groups: [{ name(线路/分组展示名), status(1=正常 2=维护 3=异常), node_count, domain_count, domains?: string[] }] }
GET /api/guard/origin-ip-ranges 当前用户域名的回源 IP 段(节点回源出口 IP 聚合成 CIDR,用户需在源站防火墙放行) { list: [{ cidr, line? }] }

要点:① 权限挂租户已有的 guard.domain.list 即可(只读观测);② status 的健康判定是解析调度页「节点健康」占位的正式数据源(自动 failover 前置);③ 回源出口 IP 若与节点管理 IP 不同(NAT/独立出口),以实际回源出口为准,别把管理 IP 泄给租户。


5.9 WAF 规则 /api/guard/waf/rules

WAF 规则组挂在策略(policy_id)下,一个规则组含若干子规则(rules[]),由网关侧匹配 zone / pattern 决定命中后 action(拦截 / 记录 / 验证码)。

⚠️ 状态约定:本组接口的 status 字段统一约定 1=禁用 2=启用(S-6 修复后已与 forwards / bwlist / schedules 对齐)。POST /status 接口的 oneof 校验也是 1|2

⚠️ 路径 ID 取 tagPUT/DELETE /api/guard/waf/rules/{id}{id} 是规则组的 tag(uint64 主键),不是 rule_id(业务编号)。前端从列表的 tag 字段取值。

GET /api/guard/waf/rules — WAF 规则组列表

鉴权guard.waf.list

输入参数

字段 类型 说明
page / size int 标准分页
policy_id string 按策略过滤(不传则返回当前用户可见的全部规则组)

输出字段data.list[] = WafGroupVO):

字段 类型 说明
tag uint64 规则组主键(路径 {id} 用)
rule_id int64 业务规则编号
name string 规则组名称
describe string 描述
waf_type int32 WAF 类型内部编码
sub_rule_condition int32 子规则组合逻辑(0=AND / 1=OR,按 model 实现为准)
scope string 作用域(domain / path / 留空 = 全部)
action int32 1=block(拦截) 2=log(记录) 3=captcha(验证码)
status int32 1=禁用 2=启用
policy_id string 所属策略 ID
rules[] WafRuleVO 子规则列表(每条含 zone / pattern / pattern_type / is_not)
ctime / utime int64 时间戳(毫秒)

WafRuleVO(子规则)字段

字段 类型 说明
tag uint64 子规则主键
rule_id int64 业务规则编号
group_id int64 所属规则组(关联 WafGroupVO.tag / rule_id
zone string 匹配区域(如 URL / ARGS / HEADER / BODY
sub_field string 区域内的子字段(如 header name)
pattern_type int32 匹配类型(精确 / 正则 / 包含等内部编码)
pattern string 匹配表达式
describe string 描述
is_not bool true = 取反匹配
ctime / utime int64 时间戳(毫秒)

可视化建议


POST /api/guard/waf/rules — 创建 WAF 规则组

鉴权guard.waf.create

输入参数WafGroupCreateReq):

字段 类型 必填 取值 说明
name string "SQL 注入防护" 规则组名称
policy_id string "1" 挂载策略
rule_id int64 业务编号,不传由后端生成
describe string 描述
waf_type int32 WAF 类型内部编码
sub_rule_condition int32 子规则组合逻辑
scope string "*.example.com" 作用域
action int32 1 1=block / 2=log / 3=captcha
status int32 2 1=禁用 2=启用
rules[] object[] 子规则列表(字段同 WafRuleReq,见下)

WafRuleReq(子规则请求体)

字段 类型 说明
rule_id int64 业务编号,不传由后端生成
zone string 匹配区域
sub_field string 区域内的子字段
pattern_type int32 匹配类型
pattern string 匹配表达式
describe string 描述
is_not bool true = 取反

示例

curl -X POST https://waf.example.com/api/guard/waf/rules \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "SQL 注入防护",
    "policy_id": "1",
    "describe": "OWASP Top 10 SQLi 黑名单",
    "action": 1,
    "status": 2,
    "rules": [
      { "zone": "ARGS", "pattern_type": 2, "pattern": "union\\s+select", "describe": "SQLi: union select" }
    ]
  }'

输出字段:返回新建 WafGroupVO(含 tagrules[] 已落库的子规则)。

不存在字段:enabled 布尔(用 status 整数)/ description(用 describe)/ domains[](作用域用 scope 字符串)。


PUT /api/guard/waf/rules/{id} — 更新 WAF 规则组

鉴权guard.waf.edit

输入参数(path id = 规则组 tag;body WafGroupUpdateReq,字段全部可选,按 flag 增量更新;rules[] 全量替换):

字段 类型 说明
name string 规则组名称
describe string 描述
waf_type *int32 指针类型,未传不动
sub_rule_condition *int32 指针类型
scope string 作用域
action *int32 指针类型
rules[] object[] 全量替换子规则列表

本接口不更新 status——启停切换走独立的 PUT /api/guard/waf/rules/{id}/status

输出字段:返回更新后的 WafGroupVO


DELETE /api/guard/waf/rules/{id} — 删除 WAF 规则组

鉴权guard.waf.delete

输入参数:path id(规则组 tag)。

实现要点:级联删除子规则(waf_rules.group_id = tag)。

输出字段datanull


PUT /api/guard/waf/rules/{id}/status — 切换 WAF 规则组启停

鉴权guard.waf.status

输入参数(path id = 规则组 tag;body WafStatusReq):

字段 类型 必填 取值 说明
status int32 1 / 2 1=禁用 2=启用,oneof=1 2 校验

示例

curl -X PUT https://waf.example.com/api/guard/waf/rules/42/status \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "status": 1 }'

输出字段datanull

GET /api/guard/waf/rules/export — 导出精准防护规则为 JSON 文件

鉴权guard.waf.export

输入参数(query):

字段 类型 必填 说明
policy_id string 策略 ID

输出:直接返回 JSON 文件流(Content-Disposition: attachment; filename="waf-precision-rules.json" {code,data} 信封)。文件结构:

{
  "version": 1,
  "kind": "waf-precision-rules",
  "rules": [
    {
      "name": "拦截可疑注入", "describe": "", "waf_type": 8, "sub_rule_condition": 1,
      "scope": "/api/", "action": 1, "status": 2,
      "rules": [ { "zone": "URL", "sub_field": "", "pattern_type": 1, "pattern": "select", "describe": "", "is_not": false } ]
    }
  ]
}

rules[] 中每组不含 id/tag/policy_id/时间戳,便于跨策略导入。

POST /api/guard/waf/rules/import — 从 JSON 批量导入精准防护规则

鉴权guard.waf.import

输入参数(body WafRuleImportReq):

字段 类型 必填 说明
policy_id string 目标策略 ID
rules array 规则组数组(≥1),可直接取导出文件的 rules 字段

行为:各组独立校验/创建(追加,单组失败不阻断其余),成功后即时下发一次。

示例

curl -X POST https://waf.example.com/api/guard/waf/rules/import \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "policy_id": "P123", "rules": [ { "name": "block-x", "waf_type": 8, "action": 1, "sub_rule_condition": 1, "rules": [ { "zone": "URL", "pattern_type": 1, "pattern": "select" } ] } ] }'

输出字段data = { imported, failed, errors[] }(成功数 / 失败数 / 每条失败原因)。


5.9 WEB 基础防护 /api/guard/policies/{id}/waf/*

从 zmod 移植的 WEB 基础防护,四块配置:基础防护(自定义拦截页/引擎/XML/Body 大小/内置策略)、高频攻击惩罚精准白名单BODY 检测白名单。精准防护规则本体仍走 §5.x 的 /api/guard/waf/rules。除高频惩罚存独立列 guard_policies.waf_rate_limit 外,其余内联 guard_policies.waf_config JSON。所有写接口即时下发ApplyTagWaf)到策略关联域名。枚举字段用 zmod pb 枚举名字符串(如 WAFPageType_1_DEFAULT),前端只透传。权限:读 guard.waf.view/guard.waf.list,写 guard.waf.create/edit/delete

接口 说明
GET /api/guard/policies/{id}/waf/base-config 查询基础防护配置
PUT /api/guard/policies/{id}/waf/base-config 更新基础防护(校验拦截码 509-599、PageType 联动、req_body_size 越界回落 4)
GET /api/guard/policies/{id}/waf/rule-versions 内置策略「规则库版本」下拉候选(去重,非管理员隐藏 interface-test,v1/v2 引擎用)
GET /api/guard/policies/{id}/waf/policy-schemas?rule_version=<ver> 内置策略「策略模式」下拉候选(按规则库版本过滤 waf_schemas,返回 id/name)
GET /api/guard/policies/{id}/waf/semantic 查询语义检测配置(14 类分析器的开关与检测级别;未配置过返回 config="{}"
PUT /api/guard/policies/{id}/waf/semantic 更新语义检测(投影到该策略全部已审核域名的 settings 并即时下发;未审核域名在审核通过时补投影)
GET /api/guard/policies/{id}/waf/rate-limit 查询高频攻击惩罚
PUT /api/guard/policies/{id}/waf/rate-limit 更新高频攻击惩罚
GET /api/guard/policies/{id}/waf/white-rules 精准白名单列表
POST /api/guard/policies/{id}/waf/white-rules 新增精准白名单(id 后端自增,默认启用)
PUT /api/guard/policies/{id}/waf/white-rules/{wid} 更新精准白名单(含状态)
DELETE /api/guard/policies/{id}/waf/white-rules/{wid} 删除精准白名单
GET /api/guard/policies/{id}/waf/white-rules/export 导出精准白名单为 JSON 文件(waf-white-rules 结构,attachment)
POST /api/guard/policies/{id}/waf/white-rules/import 从 JSON 批量导入精准白名单(追加,body {rules[]},返回 imported/failed)
GET /api/guard/policies/{id}/waf/inner-rules?version= 内置规则候选(过白规则选择器,读 main_rule_*_v3
GET /api/guard/policies/{id}/waf/body-rules BODY 检测白名单列表
POST /api/guard/policies/{id}/waf/body-rules 新增 BODY 白名单(rule_id 自增,首条 20220413,status 强制 true)
PUT /api/guard/policies/{id}/waf/body-rules/{bid} 更新 BODY 白名单(含状态)
DELETE /api/guard/policies/{id}/waf/body-rules/{bid} 删除 BODY 白名单

精准白名单条目{ id, name, describe, rule_id(逗号串), rule_name, op(str/regex), url, status(CFGOPTION_2_ENABLE/..), zones[{zone,sub_field}] }。「跳过哪些检测」由 rule_id(逗号分隔的过白内置规则编号)表达,白名单无 action/waf_type

BODY 白名单条目{ rule_id, op(prefix/suffix/regex/equal), data(匹配路径), describe, status(bool) }。注意 status 是 bool(与白名单的 CFGOPTION 串不同)。

基础防护体{ waf_policy{name,policy_id,default_main_rule_version}, waf_custom_err_code{page_type,code,content,redirect_addr}, waf_xml{status,max_depth,...}, req_body_size, engine{version} }

⚠️ 精准防护规则(/api/guard/waf/rules)的子规则 pattern/describeuser_rule_v3base64 存储(与 zmod/gen-server 一致),本次移植已补齐编解码并兼容存量明文。


§6 Analytics 统计分析

📊 Analytics 统计分析 · 18 paths(含 80+ chart-key 单图接口)· 用于构建 WAF 监控大屏、运营报表、处置闭环
所有 /api/analytics/* 路径已对外,只能增加字段或新增接口,不能修改或删除已发布路径
完整 chart-key → 数据形态映射见各小节"chart-key 索引表",每行都明确推荐图表。

6.0 本章统一调用约定

Analytics 接口数量多但大多是同构的图表查询。本章用"统一约定 + 索引表 + 特殊接口展开"组织。

鉴权与权限

HeaderAuthorization: Bearer <token>Authorization: ApiKey zck_...,可附加 Accept-Language: zh-CN / en-US

权限分三类:

类型 判断方式 示例
页面级只读 analytics.<page>.view GET /api/analytics/overview/kpi 需要 analytics.overview.view
特殊动作 表格或小节单独标出 POST /api/analytics/overview/export 需要 analytics.overview.export
登录态 只要求认证,不要求具体业务权限 GET /api/analytics/glossary

使用 API Key 时,Key 的 scopes 必须覆盖接口所需权限。例如调用 GET /api/analytics/access/status,Key 至少包含 analytics.access.view

单图 GET 调用模板

curl -sS 'https://waf.example.com/api/analytics/access/status?window=last_24h&site_id=site-001' \
  -H "Authorization: ApiKey $ZCLOUD_API_KEY" \
  -H 'Accept-Language: zh-CN'

返回统一 JSON 信封,图表 data 固定使用 Chart 统一契约docs/specs/chart-contract.md)。这是新系统对外唯一契约;老数据库表、聚合表和 ES 索引只作为内部数据源,不影响调用方入参或响应结构。

图表 data 固定 5 字段,禁止 series / totals / kpis / points / list 作为对外顶层字段

字段 类型 说明
chart_key string 与请求 <chart> 完全一致
render_hint enum 8 词汇之一:kpi / categorical_distribution / categorical_distribution_over_time / time_series_single / time_series_multi / topn / geo / table,前端据此选渲染器
schema object 列元信息 {dimensions:[{name,type,unit?,values?}], measures:[{name,type,unit?,format?}]}
rows array tidy 长表,一行一个观测(禁止横铺),空数据返回 []
meta object 调试字段 {source, cache, latency_ms, partial?, available?, ...}

window.granularity 不是入参:客户传 window=last_24h,后端按时间窗大小自动选择 5m/1h/1d 聚合表,并在响应里回填实际命中的 granularity

Batch 调用模板

适用于 POST /api/analytics/batchPOST /api/analytics/<page>/batch

curl -sS -X POST https://waf.example.com/api/analytics/overview/batch \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
        "time_window": "last_24h",
        "site_id": "",
        "domain_id": "",
        "compare": false,
        "charts": [
          { "key": "kpi" },
          { "key": "bandwidth" }
        ]
      }'

Batch 响应的 data.data 以 chart-key 为键;data.meta 给出整体耗时、缓存命中率、失败图表数和实际时间窗。单个 chart 失败时优先查看该 chart 节点里的 partial / error 字段。


6.1 术语表

GET /api/analytics/glossary — 统计术语表

用途:返回统计分析术语表,前端 tooltip 用此渲染"什么是 QPS / 拦截率"等说明。

鉴权:登录态

输入参数:无。

输出字段

字段 类型 说明
data.terms map<string,string> key=术语短名,value=多语言解释

可视化建议

示例响应

{
  "code": 0,
  "data": {
    "terms": {
      "qps": "每秒请求数",
      "block_rate": "拦截率"
    }
  }
}

6.2 跨页 Batch

POST /api/analytics/batch — 跨页通用批量查询

用途:在前端大屏页面初始化时一次性请求多个 chart-key,省去 N 次单图 GET 往返。后端并行执行各子查询并合并响应。

鉴权:按 page 字段映射到对应 analytics.<page>.view

输入参数(请求体):

字段 类型 必填 取值/示例 说明
page string overview 必须是当前已支持页面之一:overview / access / protect / ai / bot / alert / health / ops / closure / cache
time_window string last_24h window
stime / etime int64 1746748800000 自定义时间戳
site_id string 站点过滤
domain_id string 域名过滤
target_user_id string 客户级切换被查看用户
compare bool false 是否启用上一周期对比
charts[] array [{key:"kpi"}] 至少 1 项 chart-key

logs(Phase 1 访问日志)和 reports(Phase 4 报表中心)是独立 group,不走 batch 模式

输出字段

字段 类型 说明
data.data map key=chart-key;value 固定为 {chart_key, render_hint, schema, rows, meta} 5 字段 Chart 统一契约
data.meta.elapsed_ms int 整体耗时
data.meta.cache_hit_ratio float 缓存命中率(0-1)
data.meta.total_charts int 请求 chart 总数
data.meta.failed_charts int 失败 chart 数
data.meta.window object 实际命中时间窗

可视化建议

示例请求/响应

// 请求
{
  "page": "overview",
  "time_window": "last_24h",
  "compare": false,
  "charts": [
    { "key": "kpi" },
    { "key": "bandwidth" }
  ]
}
// 响应
{
  "code": 0,
  "data": {
    "data": {
      "kpi": {
        "chart_key": "overview/kpi",
        "render_hint": "kpi",
        "schema": {
          "dimensions": [],
          "measures": [
            { "name": "domain_count", "type": "integer", "unit": "" },
            { "name": "requests",     "type": "integer", "unit": "requests" },
            { "name": "blocked",      "type": "integer", "unit": "events" },
            { "name": "block_rate",   "type": "percent", "unit": "%" },
            { "name": "qps",          "type": "float",   "unit": "qps" },
            { "name": "ai_detect",    "type": "integer", "unit": "events" }
          ]
        },
        "rows": [
          { "domain_count": 8, "requests": 12345, "blocked": 678, "block_rate": 5.49, "qps": 0.143, "ai_detect": 0 }
        ],
        "meta": { "source": "postgres", "cache": "miss", "latency_ms": 10 }
      },
      "event-type": {
        "chart_key": "overview/event-type",
        "render_hint": "categorical_distribution",
        "schema": {
          "dimensions": [{ "name": "event_type", "type": "string" }],
          "measures": [{ "name": "count", "type": "integer", "unit": "events" }]
        },
        "rows": [
          { "event_type": "sql_injection", "count": 1234 },
          { "event_type": "xss", "count": 567 }
        ],
        "meta": { "source": "elasticsearch", "cache": "miss", "latency_ms": 15, "partial": false }
      }
    },
    "meta": {
      "elapsed_ms": 22,
      "cache_hit_ratio": 0,
      "total_charts": 2,
      "failed_charts": 0,
      "window": { "stime": 1746662400000, "etime": 1746748800000, "granularity": "1h" }
    }
  }
}

注意:上例中 kpi 的 measure 名 requests / blocked 是对外契约真值名(与 pkg/chart/contract 真值结构体对齐)。底层数据库字段名不对外暴露,前端严格按 schema.measures[].name 取值。


POST /api/analytics/{page}/batch — 页面级 Batch

用途:与 POST /api/analytics/batch 等价,但 page 由 URL 决定(前端固定页面调用更直观)。

鉴权:根据 {page} 映射到对应 analytics.<page>.view

支持的 page 值

URL 权限
POST /api/analytics/overview/batch analytics.overview.view
POST /api/analytics/access/batch analytics.access.view
POST /api/analytics/protect/batch analytics.protect.view
POST /api/analytics/ai/batch analytics.ai.view
POST /api/analytics/bot/batch analytics.bot.view
POST /api/analytics/alert/batch analytics.alert.view
POST /api/analytics/health/batch analytics.health.view
POST /api/analytics/ops/batch analytics.ops.view
POST /api/analytics/closure/batch analytics.closure.view
POST /api/analytics/cache/batch analytics.cache.view

输入/输出:与 POST /api/analytics/batch 完全一致;调用方不需要在请求体里再传 page,即使传了也以路径中的页面名为准。

可视化建议:同上。


6.3 单图 GET 调用入口

GET /api/analytics/{page}/{chart} — 单图通用入口

用途:拉取单个 chart-key 的数据。{page} 取值同 batch;{chart} 取值参见各页面小节的 chart-key 索引表。

鉴权:根据 {page} 映射到 analytics.<page>.view(少数特殊 chart 用独立权限,详见各小节)。

输入参数:见 §A 通用查询参数

输出字段:统一信封 + data

可视化建议:前端按 render_hint 自动分发到对应图表组件;复杂图表的列定义以 schema 为准。


6.4 总览页面(Overview)

适用场景:WAF 防护监控大屏首页 KPI + 趋势 + 排行 + 地图。

下表所有 GET 接口都用 §6.0 单图 GET 调用模板§A 通用查询参数 和 Chart 统一契约响应结构。

API chart-key render_hint 推荐图表 说明
GET /api/analytics/overview/kpi kpi kpi KpiGroupCard(6 measure) 站点数 / requests / blocked / block_rate / qps / ai_detect
GET /api/analytics/overview/bandwidth bandwidth time_series_multi 折线图(双 Y 轴) 总带宽与回源带宽时序
GET /api/analytics/overview/request-attack request-attack time_series_multi 折线图(双系列) 请求量 vs 攻击量对比
GET /api/analytics/overview/event-type event-type categorical_distribution 饼图 / 环形图 事件类型分布(dim=event_type, measure=count)
GET /api/analytics/overview/waf-type waf-type categorical_distribution 饼图 / 环形图 WAF 命中类型分布
GET /api/analytics/overview/geo geo geo 中国/世界地图热力 攻击来源地理分布
GET /api/analytics/overview/top-domains top-domains topn 横向 bar / 表格 被攻击域名 TOP 5
GET /api/analytics/overview/domain-traffic domain-traffic table 域名管理列表流量列 每域名请求量/攻击数聚合(最多 1000 域名)
GET /api/analytics/overview/top-ip top-ip topn 横向 bar / 表格 攻击源 IP TOP,meta.row_extras 可带地理信息
GET /api/analytics/overview/top-url top-url topn 横向 bar / 表格 受攻击 URL TOP
GET /api/analytics/overview/bot bot categorical_distribution 饼图 / 环形图 人类 / 友好 Bot / 可疑 Bot / 拦截构成

「检测引擎健康」卡已落地(无需新后端 chart-key):前端 EngineHealthCard.vue 直接复用 detection 页已实现的 detection/semantic/kpi + detection/latency/kpi(真实 WAF ES)自取数渲染,整卡点击跳转 /reports/detection-engine

chart-key 数据形态详解

kpirender_hint = kpi

schema.measures 共 6 项:

measure name type unit 说明
domain_count integer (空) 站点数
requests integer requests 时间窗内总请求量
blocked integer events 时间窗内总拦截量
block_rate percent % 拦截率 = blocked / requests
qps float qps QPS = requests / 窗口秒数
ai_detect integer events AI 识别数(当前固定 0,后续接 ES 实数)

rows 单行:[ { domain_count, requests, blocked, block_rate, qps, ai_detect } ]

对外字段名requests / blocked 是 API 契约字段名。底层数据库若仍使用 request_today / attack_today 等历史字段,由后端在服务层转换,不暴露给调用方。

推荐图表KpiGroupCard(6 个 KPI 数字卡),block_rate 用百分比 + 进度条;qps 配迷你 sparkline。


bandwidth / request-attack — 时序双系列

输出 [{ctime: int64ms, bandwidth: float, origin_bandwidth: float}, ...][{ctime, requests, attacks}, ...]

推荐图表:折线图,X 轴 ctime,Y 轴双系列。


event-typerender_hint = categorical_distribution

schema 部分 内容
dimensions [{ name: "event_type", type: "string" }]
measures [{ name: "count", type: "integer", unit: "events" }]

rows 长表:[ { event_type: "sql_injection", count: 1234 }, { event_type: "xss", count: 567 }, ... ]

推荐图表PieCard(≤8 类自动饼图)/ BarCard(>8 类自动横向 bar);前端按 categorical_distribution 词汇分发。


waf-type — 维度分布

输出 [{key: string, count: int}, ...]

推荐图表:饼图(≤ 8 类)或环形图。


geo — 地理热力

输出 [{region: string, count: int}, ...],region 为国家/省份名。

推荐图表:地图热力(中国地图 + 世界地图叠加)。


top-domains — 域名排行

输出 [{host: string, attack_count: int}, ...],按 attack_count DESC,最多 5 条。

推荐图表:横向 bar 图。


POST /api/analytics/overview/export — 总览页导出

用途:把 KPI 与图表快照导出为 CSV 或 JSON 文件,供线下分析或汇报。返回原文文件流,不走信封

鉴权analytics.overview.export

输入参数(请求体):

字段 类型 必填 取值/示例 说明
format string csv / json 导出格式
window string last_24h 时间窗
charts[] array [{key:"kpi"}] 要导出的 chart-key 列表

输出:直接返回文件流,Content-Type: text/csvapplication/jsonContent-Disposition: attachment

可视化建议:不适合图表;触发后浏览器下载。


6.5 访问分析页面(Access)

适用场景:流量与质量分析大屏 — 看请求量、流量、缓存命中、状态码、耗时分布、运营商、TOP IP/URL、地域。

API chart-key render_hint 推荐图表 说明
GET /api/analytics/access/request-hm request-hm time_series_single(无 compare)/ time_series_multi(compare) LineCard 请求量趋势;compare=true 走子形态 B(period 维度区分 current/previous)
GET /api/analytics/access/flow-hm flow-hm time_series_multi LineCard 多线 5 measure:total_bytes / request_bytes / response_bytes / upstream_send / upstream_receive
GET /api/analytics/access/cache-hm cache-hm time_series_multi 折线图(双 Y 轴) 缓存命中次数 + 缓存字节趋势
GET /api/analytics/access/bandwidth bandwidth time_series_multi LineCard 多线 4 measure:bandwidth / origin_bandwidth / up_bandwidth / down_bandwidth
GET /api/analytics/access/status status categorical_distribution_over_time StackedBarCard 4 类 HTTP 状态码(dim=status_class enum["2xx","3xx","4xx","5xx"] + time)按时间堆叠
GET /api/analytics/access/flow-duration flow-duration time_series_multi 折线图(3 分位) 请求耗时 P50/P95/P99(D4:仅时间窗 ≤ 24h 走实时计算)
GET /api/analytics/access/isp isp categorical_distribution 饼图 运营商分布(移动/联通/电信/其它)
GET /api/analytics/access/top-ip top-ip topn 表格 / 横向 bar(含地理) 访问 IP TOP(默认 10,可调 top
GET /api/analytics/access/top-url top-url topn 表格 / 横向 bar URL TOP,可按 order=bytes_desc/cache_desc 切换排序
GET /api/analytics/access/geo geo geo 中国/世界地图热力 访问来源地理分布

已支持 chart-key(访问分析重设计 · docs/proposals/2026-06-29-access-analytics-redesign.md §3)

以下 chart-key 走同一批量端点 POST /api/analytics/access/batch 与单图 GET /api/analytics/access/{chart},响应严格五字段(chart_key / render_hint / schema / rows / meta)。已全部实现并走统一契约;protocol 在采集字段未接入时返回 meta.available=false。dims/measures 为建议命名,最终以契约自检为准。

API chart-key render_hint dimensions measures 说明
GET /api/analytics/access/kpi kpi kpi period(enum: current/previous,compare 时 2 行) requests, qps_peak, bandwidth_peak(bps), bandwidth_95th(bps), uv, cache_req_hit_rate(%), cache_byte_hit_rate(%), origin_rate(%), rate_4xx(%), rate_5xx(%), pass_rate(%), block_rate(%), block_count, effective_rate(%) 已实现;PG flow/status + ES(uv=distinct uuid/session;pass/block 来自 z_final_action)
GET /api/analytics/access/action-hm action-hm categorical_distribution_over_time action(enum: pass/block/challenge), time(ms) count 已实现;ES z_final_action 按时间桶(处置趋势已迁移至 protect/action-trend
GET /api/analytics/access/status-origin status-origin categorical_distribution_over_time status_class(enum: 2xx/3xx/4xx/5xx), time(ms) count 已实现;ES upstream_status
GET /api/analytics/access/cache-rate cache-rate time_series_multi time(ms) req_hit_rate(%), byte_hit_rate(%), origin_rate(%) 已实现;PG cache_count/request_count、cache_bytes/total_bytes
GET /api/analytics/access/latency-hist latency-hist categorical_distribution_over_time bucket(enum: 0-50ms/50-200/200-500/500ms-1s/1-3s/>3s), time(ms) count 已实现;ES request_time 分桶
GET /api/analytics/access/latency-pct latency-pct time_series_multi time(ms) p50(ms), p95(ms), p99(ms) 已实现;ES request_time 分位(P50/P95/P99)
GET /api/analytics/access/slow-url slow-url topn url(string) avg_time(ms)(extra: request_count) 已实现;PG flow_urls.ava_req_time
GET /api/analytics/access/error-url error-url topn url(string) count_4xx, count_5xx 已实现;ES uri × status
GET /api/analytics/access/top-ua top-ua topn user_agent(string) count 已实现;ES user_agent
GET /api/analytics/access/device device categorical_distribution device(enum: desktop/mobile/tablet/bot/other) count 已实现;ES user_agent 解析(后端 UA parser)
GET /api/analytics/access/method method categorical_distribution method(enum: GET/POST/PUT/DELETE/HEAD/OPTIONS/other) count 已实现;ES method
GET /api/analytics/access/content-type content-type categorical_distribution content_type(string) count(extra: bytes) 已实现;ES response_content_type
GET /api/analytics/access/top-referer top-referer topn referer(string) count 已实现;ES referer(待后端确认该字段是否采集
GET /api/analytics/access/human-bot human-bot categorical_distribution category(enum: human/good_bot/bad_bot/blocked) count 已实现;ES z_final_type + z_bot_action(环形占比对比真人/好Bot/坏Bot/拦截,非时序)
GET /api/analytics/access/protocol protocol categorical_distribution scheme(http/https) / http_version(1.1/2/3) / tls_version / ip_family(v4/v6)(4 facet,按查询参数单选) count 已实现空响应降级;当前 ES access 索引未采集 scheme/http_version/tls_version/ip_family,返回 meta.available=false

chart-key 数据形态详解

request-hm

无 compare(render_hint = time_series_single):

schema 部分 内容
dimensions [{ name: "time", type: "timestamp", unit: "ms" }]
measures [{ name: "requests", type: "integer", unit: "requests" }]

rows[ { time: 1746748800000, requests: 1234 }, ... ]

开 compare(render_hint = time_series_multi 子形态 B:1 categorical + 1 ts + 1 measure):

schema 部分 内容
dimensions [{ name: "period", type: "enum", values: ["current","previous"] }, { name: "time", type: "timestamp", unit: "ms" }]
measures [{ name: "requests", type: "integer", unit: "requests" }]

rows[ { period: "current", time: ..., requests: ... }, { period: "previous", time: ..., requests: ... }, ... ] 长表,按 period pivot 成 2 条 series。

推荐图表LineCard;compare 模式按 period pivot 双色实虚线,前端自动处理。


flow-hmrender_hint = time_series_multi

schema 部分 内容
dimensions [{ name: "time", type: "timestamp", unit: "ms" }]
measures 5 项:total_bytes / request_bytes / response_bytes / upstream_send / upstream_receive(type=integer,unit=bytes,format=iec)

rows[ { time: ..., total_bytes: ..., request_bytes: ..., response_bytes: ..., upstream_send: ..., upstream_receive: ... }, ... ]

推荐图表LineCard 多线(5 条)/ 堆叠面积。


cache-hm — 缓存趋势

输出 [{ctime, cache_count, cache_bytes, cache_response}, ...]

真值字段(D8):底层用 total_cache_count / total_cache_bytes / total_cache_response_bytes;返回时映射为 cache_count / cache_bytes / cache_response

推荐图表:双 Y 轴折线(左轴次数,右轴字节)。


bandwidthrender_hint = time_series_multi

schema 部分 内容
dimensions [{ name: "time", type: "timestamp", unit: "ms" }]
measures 4 项:bandwidth / origin_bandwidth / up_bandwidth / down_bandwidth(type=float,unit=bps)

rows[ { time: ..., bandwidth: ..., origin_bandwidth: ..., up_bandwidth: ..., down_bandwidth: ... }, ... ]

推荐图表LineCard 多线(4 条)。


statusrender_hint = categorical_distribution_over_time

schema 部分 内容
dimensions [{ name: "status_class", type: "enum", values: ["2xx","3xx","4xx","5xx"] }, { name: "time", type: "timestamp", unit: "ms" }]
measures [{ name: "count", type: "integer", unit: "requests" }]

rows tidy 长表(禁止横铺 c2xx/c3xx/c4xx/c5xx):

[
  { "status_class": "2xx", "time": 1746748800000, "count": 1200 },
  { "status_class": "3xx", "time": 1746748800000, "count": 30 },
  { "status_class": "4xx", "time": 1746748800000, "count": 8 },
  { "status_class": "5xx", "time": 1746748800000, "count": 0 },
  { "status_class": "2xx", "time": 1746752400000, "count": 1340 },
  ...
]

每个时间点必须覆盖 4 个 status_class(缺则补 count: 0,前端堆叠柱图依赖完整网格)。

推荐图表StackedBarCard(按 status_class 堆叠时序)。前端按 categorical_distribution_over_time 词汇分发。


flow-duration — 耗时分位

输出 {p50, p95, p99} 或时序数组。

真值边界(D4):百分位字段仅时间窗 ≤ 24h 时由 ES 实时计算;窗口更长时该接口返回 available:false

推荐图表:折线图(3 分位曲线)或 KPI 数字卡(3 个)。


isp — 运营商分布

输出 {mobile, unicom, telecom, other}

推荐图表:饼图(4 段)。


top-ip — IP TOP

输出 [{remote_addr, count, country, region, isp}, ...],最多 top 条。

推荐图表:表格(含国旗 + 地区 + 运营商);或横向 bar 图。


top-url — URL TOP

输出 [{url, request_count, request_bytes, cache_bytes}, ...]

推荐图表:表格(默认);或横向 bar(可按字节/缓存切换)。


geo — 地理热力

输出 [{region, count}, ...]

推荐图表:地图热力。


6.6 防护分析页面(Protect)

适用场景:WAF / CC / DDoS 三大防护引擎的命中分析。

API chart-key render_hint 推荐图表 说明
GET /api/analytics/protect/overview overview kpi 多 KPI 卡 WAF/CC/DDoS 总量汇总(时序由 statistics 系列独立 chart 提供)
GET /api/analytics/protect/waf/statistics waf/statistics time_series_multi 折线图 WAF 命中趋势(waf 命中数 + 总攻击数)
GET /api/analytics/protect/waf/types waf/types categorical_distribution 饼图 / 横向 bar WAF 命中类型分布(SQL 注入/XSS/扫描器等)
GET /api/analytics/protect/waf/top-ip waf/top-ip topn RankingCard / 横向 bar dim=ip(string), measure=count(events),country/province 进 meta.row_extras
GET /api/analytics/protect/waf/geo waf/geo geo GeoHeatmapCard dim=country(geo), measure=count(events);provinces 暂存 meta.provinces
GET /api/analytics/protect/cc/statistics cc/statistics time_series_single 折线图 CC 命中趋势
GET /api/analytics/protect/cc/top-ip cc/top-ip topn 表格 / 横向 bar CC 攻击 IP TOP
GET /api/analytics/protect/cc/geo cc/geo geo 地图热力 CC 攻击地域
GET /api/analytics/protect/cc/top-url cc/top-url topn 表格 CC 攻击 URL TOP
GET /api/analytics/protect/ddos/statistics ddos/statistics time_series_multi 折线图(双 Y 轴) DDoS 事件数 + 峰值带宽时序
GET /api/analytics/protect/ddos/types ddos/types categorical_distribution 饼图 / 横向 bar DDoS 攻击类型分布(syn flood/udp flood 等)
GET /api/analytics/protect/ddos/top-ip ddos/top-ip topn 表格 DDoS 源 IP TOP(含峰值带宽)

已支持 chart-key(IA 重构与防护重设计 · docs/proposals/2026-07-01-analytics-ia-and-protect-redesign.md §3)

以下 chart-key 走同一批量端点 POST /api/analytics/protect/batch 与单图 GET /api/analytics/protect/{chart},响应严格五字段(chart_key / render_hint / schema / rows / meta)。已全部实现并走统一契约。dims/measures 为建议命名,最终以契约自检为准。

API chart-key render_hint dimensions measures 说明
GET /api/analytics/protect/kpi kpi kpi period(enum: current/previous,compare 时 2 行) requests, blocked, block_rate(%), attack_ips, targeted_hosts, ddos_peak_bps(bps) 已实现;PG <prefix>_attack_domains(request_count/attack_count/ddos_bytes) + PG COUNT(DISTINCT remote_addr)(不查 ES);block_rate=blocked/requests
GET /api/analytics/protect/kpi-trend kpi-trend time_series_multi time(time) requests, blocked 已实现;供 KPI 卡片的 sparkline 迷你走势
GET /api/analytics/protect/action-trend action-trend categorical_distribution_over_time action(enum: pass/block/captcha), time(ms) count 已实现;ES(waf/access) z_final_action(0/1/2) 按时间桶 date_histogram
GET /api/analytics/protect/module-share module-share categorical_distribution module(enum: waf/cc/ddos/bot) count 已实现;ES z_final_mod(mod_waf/mod_cc/mod_ddos/mod_bot) terms;或 PG attack_domains 各模块列求和
GET /api/analytics/protect/top-rule top-rule topn rule_id(string) count(extra: rule_name/waf_type 入 meta.row_extras) 已实现;ES waf 索引 z_waf_id terms(或 WAF 引擎 ES matches.rule_id)
GET /api/analytics/protect/top-host top-host topn host(string) attack_count 已实现;PG <prefix>_attack_domains GROUP BY host, SUM(attack_count)
GET /api/analytics/protect/top-url top-url topn uri(string) count 已实现;统计表 {tfs,hs,ds}_flow_urlsmod='waf' 聚合(与 cc/top-url 同表异 mod)。原实现聚合 ES waf 索引的 request.uri,因该模板 dynamic:false 恒空,2026-08-11 改走统计表
GET /api/analytics/protect/events events table 列: time, remote_addr, country, host, uri, method, z_final_type, z_waf_type, action, attack_count 已实现;ES waf 索引 hits(访问日志查询已存在,收敛为表格列)

chart-key 数据形态详解(关键差异)

overviewrender_hint = kpirows = [{waf, cc, ddos_bytes}](单行汇总,dimensions=[],measures=waf/cc/ddos_bytes)。推荐 3 KPI 卡;时序由 protect/waf/statisticsprotect/ddos/statistics 独立 chart 提供。

waf/statisticsrender_hint = time_series_multirows = [{time, waf, attack_count}, ...] tidy 长表(time=ms 时间戳,2 measure 双线)。推荐折线图(双系列)。

waf/types / ddos/typesrender_hint = categorical_distributionrows = [{waf_type, count}, ...] / [{ddos_type, count}, ...] tidy 长表,用饼图或横向 bar。

waf/top-iprender_hint = topn

schema 部分 内容
dimensions [{ name: "ip", type: "string" }]
measures [{ name: "count", type: "integer", unit: "events" }]

rows[ { ip: "1.2.3.4", count: 1234 }, { ip: "5.6.7.8", count: 567 }, ... ],已按 count DESC 排序。country / province 等附加列进 meta.row_extras(前端按需取,不入 schema 列)。

推荐图表RankingCard / 横向 bar 图。


ddos/top-ip:含地理的 IP TOP,推荐含国旗的表格。


waf/georender_hint = geo

schema 部分 内容
dimensions [{ name: "country", type: "geo" }]
measures [{ name: "count", type: "integer", unit: "events" }]

rows[ { country: "China", count: 1234 }, { country: "US", count: 567 }, ... ]

provinces 数据暂存 meta.provinces(结构 [{province, count}])。当前前端 GeoHeatmapCard 渲染主图用 rows,省级钻取用 meta.provinces;后续如需要省级独立图表,再新增 protect/waf/geo-provinces chart-key。

推荐图表GeoHeatmapCard(中国 / 世界地图热力)。


cc/georender_hint = georows = [{country, count}, ...],地图热力。

ddos/statisticsrender_hint = time_series_multirows = [{time, events, bandwidth}, ...] tidy 长表(events=integer events,bandwidth=float bytes/iec)。推荐双 Y 轴折线。


6.7 AI 识别页面(AI)

当前 AI 页面路径已发布。/logs 走独立 analytics.ai.logs 权限,其它走 analytics.ai.view
第一版多数 chart 返回 BatchChartResult 占位(rows: []meta.available = falsemeta.reason 说明原因),路径与契约稳定,后端可在保持路径不变的前提下逐步升级真实数据。

API chart-key 推荐图表 说明
GET /api/analytics/ai/attack-trend attack-trend 折线图 AI 攻击趋势
GET /api/analytics/ai/top-ip top-ip 表格 / 横向 bar AI 命中 IP TOP
GET /api/analytics/ai/top-url top-url 表格 AI 命中 URL TOP
GET /api/analytics/ai/detection detection KPI 数字卡 / 雷达图 AI 检测能力面板
GET /api/analytics/ai/test-results test-results 表格 / 柱图 AI 测试结果
GET /api/analytics/ai/logs logs 表格(明细,分页) AI 命中日志明细(独立权限 analytics.ai.logs

占位响应:未升级的 chart 返回标准 BatchChartResult(5 字段齐全,rows: []meta.available = falsemeta.reason 说明原因),前端应渲染"暂无数据"占位卡片,不展示假数据。


6.8 主动防护 / Bot 页面

适用场景:识别和分析爬虫/Bot 流量。

API chart-key 推荐图表 说明
GET /api/analytics/bot/statistics statistics KPI(6 个) Bot 请求/会话/IP/已知未知 Bot 总览(趋势线由独立 chart 提供,未拆时不展示)
GET /api/analytics/bot/effectiveness effectiveness 能力卡 / 表格 按 JS 会话校验、真人与设备校验、自动化工具识别、动态令牌、动态封装等能力聚合 bot_reason,用于展示防护功能是否真正生效
GET /api/analytics/bot/reason reason 饼图 / 横向 bar Bot 防护原因分布,来源 ES bot_reason
GET /api/analytics/bot/advance-warn advance-warn 表格 Bot 预警列表
GET /api/analytics/bot/browser browser 饼图 浏览器分布(chrome/safari/firefox/edge/wechat/other)
GET /api/analytics/bot/operating operating 饼图 操作系统分布(android/ios/windows/mac/other)
GET /api/analytics/bot/geo geo 地图热力 Bot 地域分布
GET /api/analytics/bot/top-agent top-agent 表格 / 横向 bar User-Agent TOP
GET /api/analytics/bot/top-ip top-ip 表格(含地理) Bot IP TOP
GET /api/analytics/bot/scatter scatter 散点图 Bot 预警散点(X=ctime / Y=top_visit_count,size=top_ip_count)
GET /api/analytics/bot/sessions sessions 表格(分页) Bot 会话列表(独立权限 analytics.bot.session
GET /api/analytics/bot/sessions/{sid} - 时间线 单个 Bot 会话时间线详情(独立权限 analytics.bot.session

chart-key 数据形态详解

statisticsrender_hint = kpirows = [{requests, sessions, ips, known_bot, unknown_bot, req_per_session}](单行汇总,6 measure)。推荐 6 个 KPI 卡;请求/会话趋势由独立 chart 提供(当前未拆,需要时新增 bot/sessions-trend)。

effectivenessrender_hint = tablerows = [{capability, label, description, status, reasons, count}, ...]capability 是产品能力分组(如 session_challenge / client_integrity / automation_tool / dynamic_token / dynamic_packaging),statuseffectiveno_hitsreasons 保留原始 bot_reason:count 证据,前端可下钻到访问日志。

reasonrender_hint = categorical_distributionrows = [{bot_reason, count}, ...],用于展示 Bot / 人机校验 / 动态令牌等命中的防护原因证据。

browserrender_hint = categorical_distributionrows = [{browser, count}, ...](6 行 enum:chrome/safari/firefox/edge/wechat/other),饼图。

operatingrender_hint = categorical_distributionrows = [{os, count}, ...](5 行 enum:android/ios/windows/mac/other),饼图。

scatterrender_hint = table(无 scatter hint,table 兜底),rows = [{time, session_id, top_visit_count, top_ip_count, top_ua_count}, ...],由前端解析 X/Y/size 渲染散点图。

sessions/{sid}render_hint = tablerows = [{time, uri, remote_addr, method, status}, ...](5 字段裁剪),是该 session 的全量访问记录时间线;未传 session_id(通过 query order 参数)时返空。


6.9 告警统计页面(Alert)

适用场景:告警总览 + 列表 + 单条详情 + 确认。

API 方法 chart-key 推荐图表 说明
GET /api/analytics/alert/total GET total KPI 数字卡 告警总数
GET /api/analytics/alert/hm GET hm 折线图 / 热力图 告警趋势(按 ctime 聚合 count)
GET /api/analytics/alert/types GET types 饼图 告警类型分布(按 policy_type)
GET /api/analytics/alert/domains GET domains 横向 bar / 表格 告警域名排行
GET /api/analytics/alert/list GET list 表格(分页) 告警列表
GET /api/analytics/alert/{id} GET - 详情卡片 单条告警详情(含处置元信息)
PATCH /api/analytics/alert/{id}/ack PATCH - 不适合图表 确认指定告警

GET /api/analytics/alert/{id} 输出字段

字段 类型 说明
id int 告警 ID
uuid string 告警 UUID
policy_type string 告警类型
title / body string 告警标题 / 内容
domain / domain_id string 关联域名
status int 告警状态(0/1/2/3,参见 D10)
ctime int64 创建时间
last_update_timestamp int64 最后更新时间
process_uid string 处置人(D10 真值)
process_time int64 处置时间(D10 真值)
user_id string 归属用户

非超管按 user_id 强制过滤;越权读取返回 404。所需权限 analytics.alert.view

PATCH /api/analytics/alert/{id}/ack 请求

鉴权analytics.alert.ack

输入:path id,body 可空。

输出datanull


6.10 业务健康(Health · Phase 3)

适用场景:分析业务可用性 + 源站质量 + 慢 URI + 地域质量。

API chart-key 推荐图表 说明
GET /api/analytics/health/summary summary KPI 数字卡(6 个) c2xx/c3xx/c4xx/c5xx/n4xx/n5xx 总数
GET /api/analytics/health/status-breakdown status-breakdown 堆叠柱图(时序) 状态码三层 c/n/a 拆分(c=客户端、n=网关、a=应用)
GET /api/analytics/health/origin-errors origin-errors 表格 / 横向 bar 源站异常排行(ES upstream_addr terms,仅 upstream_status >= 500)
GET /api/analytics/health/origin-latency origin-latency 折线图(3 系列) 源站时延(移动/联通/电信 平均时延)
GET /api/analytics/health/slow-uri slow-uri 表格 慢 URI TOP(第一版按命中次数 TOP;慢请求排序后续按真实耗时数据接入)
GET /api/analytics/health/availability availability KPI / 多线折线 HTTP/Ping/DNS/TCP/Page/IPv6 可用率 + 不可用时长
GET /api/analytics/health/geo-isp-quality geo-isp-quality 表格 / 地图 地域/运营商质量

真值边界:percentile / p50 / p95 / p99 在 chart 与 statistic 包均无字段;时间窗 ≤ 24h 才走 ES 实时 percentile(D4)。可用性表 m_ava_domain.*AvailableDomain 主键是 domain_or_ip(不是 domain_id)。


6.11 平台运维(Ops · Phase 5)

适用场景:平台级运营视角 — 看高流量/高错误用户和域名、源站异常、节点容量。

权限边界analytics.ops.view 只读全平台数据,不允许target_user_id 切换视角;analytics.ops.admin 才能切换。普通客户级账号即使有 view 也不能访问 ops。

API chart-key 推荐图表 说明
GET /api/analytics/ops/summary summary KPI 数字卡(6 个) 全平台容量摘要(请求/字节/峰值带宽/源站带宽/活跃域名/活跃用户)
GET /api/analytics/ops/traffic-users traffic-users 横向 bar / 表格 高流量用户 TOP(按 total_bytes DESC)
GET /api/analytics/ops/traffic-domains traffic-domains 横向 bar / 表格 高流量域名 TOP
GET /api/analytics/ops/error-users error-users 表格 高错误用户 TOP(按 c5xx DESC,含 c4xx/n5xx)
GET /api/analytics/ops/error-domains error-domains 表格 高错误域名 TOP
GET /api/analytics/ops/origin-errors origin-errors 表格 源站异常排行
GET /api/analytics/ops/nodes nodes 表格 / 拓扑图 节点/机房视图(RPC GetWafIpWithMachineRoom + ES server_addr/bind_addr
GET /api/analytics/ops/query-pressure query-pressure 折线图(占位) ES 查询压力(依赖埋点,第一版返回 available:false

真值边界:节点系统指标(CPU/内存/磁盘)chart 不存,需走外部 zabbix。本期不实现。node_id / server_node 是禁字段。


6.12 处置闭环(Closure · Phase 6)

适用场景:在一个页面同时处理告警和风险队列;支持批量确认。

真值字段(D10):process_uid / process_time / status / level禁字段handle_user / handle_time / risk_score / alert_status。AlertRecord.status (0/1/2/3) 与 RiskRecord.status (1/2) 语义不同,前端 i18n key 不可共用。

API 方法 chart-key 推荐图表 说明
GET /api/analytics/closure/summary GET summary KPI 数字卡(5 个) 待处理告警/风险数 + 已处置数 + 平均处置时长(ms)
GET /api/analytics/closure/alerts GET alerts 表格(分页) 待处理告警队列(status=0)
GET /api/analytics/closure/risks GET risks 表格(分页) 待处理风险队列(后续接 RiskRecord 表;当前无数据时返回空列表)
GET /api/analytics/closure/trend GET trend 折线图 / 堆叠柱 处置历史趋势(按 ctime + status 分组)
POST /api/analytics/closure/alerts/confirm POST - 不适合图表 批量确认告警(代理 /api/alert/records/confirm
POST /api/analytics/closure/risks/confirm POST - 不适合图表 批量确认风险(代理 /api/chart/risk/events/:event_id/confirm

summary 输出字段

{
  "alerts_pending": 12,
  "alerts_handled_today": 8,
  "risks_pending": 0,
  "risks_handled_today": 0,
  "avg_handle_time_ms": 0
}

confirm 接口请求体

{
  "ids": ["a1", "a2", "a3"],
  "remark": "已加黑名单"
}

鉴权analytics.closure.confirm

输出datanull


6.13 缓存收益(Cache · Phase 6)

适用场景:分析 CDN/边缘缓存对回源带宽的节省价值。

真值字段(D8):total_cache_count / total_cache_bytes / total_cache_response_bytes禁字段cache_count / cache_bytes / cache_hit 单字段命名(不存在)。

API chart-key 推荐图表 说明
GET /api/analytics/cache/summary summary KPI 数字卡(4 个) 命中率 / 节省回源字节 / 命中次数 / 平均缓存对象大小
GET /api/analytics/cache/trend trend 折线图(双 Y 轴) 命中率趋势(请求数 vs 缓存命中数)
GET /api/analytics/cache/top-uri top-uri 表格 / 横向 bar URI TOP(命中次数/命中率/节省字节)
GET /api/analytics/cache/content-types content-types 饼图 内容类型分布(response_content_type 聚合)

summary 输出字段

字段 类型 说明
hit_rate float 缓存命中率 = total_cache_count / request_count
saved_response_bytes int 节省回源带宽(源站本应承担但缓存挡住的字节)
total_cache_count int 命中次数
total_cache_bytes int 命中字节
avg_object_bytes float 平均缓存对象大小 = total_cache_bytes / total_cache_count
request_count int 总请求数

业务化指标


6.14 访问日志(Logs · Phase 1)

访问日志是明细查询,不是图表单图接口;它使用统一鉴权方式,但请求/响应以列表、详情、导出为主。

API 方法 权限 推荐图表 说明
GET /api/analytics/logs GET analytics.logs.view 表格(分页,最多翻阅 1 万条 分页查询原始访问/攻击日志。query string 直接绑定 15 个筛选字段;完整的 33 个可筛字段走 /searchfield_filters
POST /api/analytics/logs/search POST analytics.logs.view 表格(分页 + 高级筛选) 同上,但入参走 JSON body,可用 field_filters 递归分组
POST /api/analytics/logs/histogram POST analytics.logs.view 时间直方图 与 search 同筛选,按最终动作拆分的日志量分桶
GET /api/analytics/logs/{uuid} GET analytics.logs.view 详情卡片(7 区块) 单条日志详情(basic/request/response/upstream/protection/waf_detail/ai_detail)
POST /api/analytics/logs/export POST analytics.logs.export 不适合图表 字段白名单导出(csv/json,size ≤ 10000;大范围走下面的异步任务)
POST /api/analytics/logs/export/estimate POST analytics.logs.export 不适合图表 导出前预估条数(ES _count,秒级;返回 {total, over_limit, limit}
POST /api/analytics/logs/exports POST analytics.logs.export 不适合图表 创建异步导出任务(PIT + search_after 深分页,上限 100 万条)
GET /api/analytics/logs/exports GET analytics.logs.export 不适合图表 导出任务列表含进度(前端仅在有进行中任务时 2 秒轮询)
GET /api/analytics/logs/exports/{id}/download GET analytics.logs.export 不适合图表 下载产物(明文 CSV,磁盘存 gzip;传输是否压缩按 Accept-Encoding 协商,包信封)
DELETE /api/analytics/logs/exports/{id} DELETE analytics.logs.export 不适合图表 取消进行中的导出任务

日志返回字段(2026-08-06 起收敛)

列表接口(GET /logsPOST /logs/search不再原样返回 ES 文档。此前一条文档 60 个字段
全量返回,其中二十多个没有任何消费方,另有 bind_addr / bot_fp / z_bypass 三个连 ES mapping
都没有(dynamic:false 吞掉),查询恒 0 命中。现在收敛为 35 个字段:

uuid, session_id, host, uri, method, request
args, scheme, protocol, request_length, remote_addr, country
province, city, isp, status, response_length, response_content_type
request_time, upstream_addr, upstream_status, upstream_cache_status, upstream_response_time, server_addr
server_port, z_final_action, z_final_mod, z_final_type, z_final_id, z_white
botd, bot_reason, request_headers, request_body, user_agent

要点:

列表分页上限

受 ES max_result_window 限制,列表最多翻阅 1 万条

导出字段

同步导出(/logs/export)与异步导出(/logs/exports口径已统一(此前分别是 12 列和 45 列)。
不传 fields 时导出全部 36 列 = 上面的 35 个 + ctime

传入 fields 时按白名单取交集,白名单外的字段被静默丢弃、不报错;若全部被丢弃则回退默认全集。
以下旧字段名已失效(传了会被静默忽略,且它们在 ES 里本就不存在或无代码产出):

user_iddomain_idprovince_zhcity_zhserver_protocolupstream_send
upstream_receivedupstream_bytes_sentupstream_bytes_receivedcrawler_category
crawler_reasonsearch_enginescanner_categoryai_predictai_scoreai_segmentai_usage

日志异步导出

突破 ES from + size 的 1 万条上限。约束与理由:

约束 超出时 为什么这么定
条数上限 100 万条 400 再多的 CSV 表格软件打不开,与其让用户等两小时拿到打不开的文件,不如提交前拦住
每用户并发 1 个进行中 409 pending 也算进行中,否则连点两次会排出两个任务
全局并发 2 个在跑 pending 排队 风险不是单用户重复点,是多用户同时打生产 ES
产物保留 3 天且每用户最多 10 个 cron 淘汰最旧 只按时间会让高频用户堆出几十个文件;只按个数会让陈旧文件永久占盘

其它要点:

前端接入注意不能<a href download> 直连下载接口。那是浏览器导航,不带 Authorization 头(token 存在 localStorage 而非 cookie),会拿到 401 JSON 并被浏览器存成 download.json。必须用带认证的 fetch 取回 blob 再触发保存。

GET /api/analytics/logs 筛选字段

15 字段筛选:uuid / session_id / remote_addr / host / uri / method / status / z_final_action / z_final_type / z_final_mod / z_final_id / z_waf_id / z_cc_id / bot_reason / z_white

真值边界z_final_action 整数枚举 0=放行 / 1=拦截 / 2=验证码bot_reason 是 Bot 防护原因取证字段,例如 block botd / token check failedz_white 是独立 bool 字段(白名单命中),不参与 action 枚举。禁字段match_content / match_area / hit_rule / rule_desc(chart 与 statistic 包均无)。

field_filters 高级筛选(递归分组)

field_filters 只在 JSON 请求体里生效:POST /api/analytics/logs/searchPOST /api/analytics/logs/histogramPOST /api/analytics/logs/export(放在 query 子对象里)。GET /api/analytics/logs 只绑定 query string,不支持 field_filters

每个节点只有两种形态,二选一:

形态 字段 说明
叶子 field + op + value logic / children 留空。历史形态,行为完全不变,老调用方不改也能跑
分组 logic + children field / op / value 被忽略;children 里可继续放叶子或分组,任意嵌套

logic 取值与生成的 ES 子句:

logic ES DSL
and {"bool":{"filter":[...]}}
or {"bool":{"should":[...],"minimum_should_match":1}}
not {"bool":{"must_not":[...]}}

语义约定

防爆护栏

限制 上限 超限错误
嵌套深度(顶层节点算第 1 层) 5 err.chart.field_filters_too_deep
叶子条件总数(跨分组累计) 50 err.chart.field_filters_too_many
logic 词汇 and / or / not err.chart.field_filters_logic_invalid

三者均返回 HTTP 200 + 业务码 400,message 已按 Accept-Language 本地化。

比较符 op 全表

op ES DSL 说明
留空 / CMPTYPE_01_EQ term / terms 精确匹配
CMPTYPE_08_IN term / terms 多值精确匹配(注意:不是子串包含)
CMPTYPE_02_NE / CMPTYPE_07_NOT bool.must_not 取反
CMPTYPE_03_GT / 04_GTE / 05_LT / 06_LTE range 仅数值字段:status / server_port / request_time / request_length / response_length / z_final_action
CMPTYPE_09_EXISTS exists 字段存在性判断(等价 KQL 的 field:*),不需要 value
CMPTYPE_10_WILDCARD wildcard 真正的子串包含;值不含 * / ? 时自动包成 *x*,自带通配符原样透传;多值按 OR 合并;数值 / 布尔字段拒绝

字段名直接用裸字段(不追加 .keyword):zcloud-access-* 的 mapping 是 dynamic:false + keyword 直映射,没有 .keyword 子字段。

OR 示例status=403status=404

{
  "window": "last_24h",
  "field_filters": [
    {
      "logic": "or",
      "children": [
        { "field": "status", "op": "CMPTYPE_01_EQ", "value": ["403"] },
        { "field": "status", "op": "CMPTYPE_01_EQ", "value": ["404"] }
      ]
    }
  ]
}

生成的 ES 子句:

{"bool":{"minimum_should_match":1,"should":[{"term":{"status":403}},{"term":{"status":404}}]}}

嵌套示例(host=a.com AND status=403) OR remote_addr=1.2.3.4

{
  "window": "last_24h",
  "field_filters": [
    {
      "logic": "or",
      "children": [
        {
          "logic": "and",
          "children": [
            { "field": "host", "op": "CMPTYPE_01_EQ", "value": ["a.com"] },
            { "field": "status", "op": "CMPTYPE_01_EQ", "value": ["403"] }
          ]
        },
        { "field": "remote_addr", "op": "CMPTYPE_01_EQ", "value": ["1.2.3.4"] }
      ]
    }
  ]
}

生成的 ES 子句:

{"bool":{"minimum_should_match":1,"should":[
  {"bool":{"filter":[{"term":{"host":"a.com"}},{"term":{"status":403}}]}},
  {"term":{"remote_addr":"1.2.3.4"}}]}}

exists / wildcard 示例uri 包含 admin 且命中过 WAF 规则)

{
  "window": "last_24h",
  "field_filters": [
    { "field": "uri", "op": "CMPTYPE_10_WILDCARD", "value": ["admin"] },
    { "field": "z_waf_id", "op": "CMPTYPE_09_EXISTS" }
  ]
}

生成的 ES 子句(顶层数组隐式 AND):

{"wildcard":{"uri":{"value":"*admin*"}}}
{"exists":{"field":"z_waf_id"}}

第一版兜底响应

第一版 stub 返回:

{ "available": false, "reason": "访问日志 ES 查询待接入..." }

列表/详情/导出契约稳定,后续接 ES zcloud-access-*


6.15 报表中心(Reports · Phase 4)

报表中心是模板、生成、下载类接口,不使用单图响应结构。列表和详情仍使用统一 JSON 信封;下载接口返回文件流。

API 方法 权限 推荐图表 说明
GET /api/analytics/reports/templates GET analytics.reports.view 表格 / 卡片网格 模板列表(含 platform_only 标记)
GET /api/analytics/reports GET analytics.reports.view 表格(分页) 报表历史列表
GET /api/analytics/reports/{id} GET analytics.reports.view 详情卡片 单条报表详情(状态、参数、产物 URL)
POST /api/analytics/reports/generate POST analytics.reports.generate 不适合图表 触发生成(platform-summary 模板需 analytics.reports.platform
GET /api/analytics/reports/{id}/download GET analytics.reports.download 不适合图表 下载产物(pdf/csv/json/html)

模板枚举(D7):protection-value / asset-risk / attack-source / business-health / platform-summary仅平台运维/超管) / raw-log-export

异步阈值:预估行数 ≤ 100k 走同步;超出强制异步返回 task_id,前端轮询 /reports/:id 获取状态 + 下载链接。第一版同步生成超时 30 秒,超时降级为异步。

generate 请求体

{
  "template": "protection-value",
  "format": "pdf",
  "window": "last_30d",
  "stime": 1735660800000,
  "etime": 1738339200000,
  "filters": {}
}
字段 类型 必填 说明
template string 模板名(D7 闭集)
format string pdf / csv / json / html
window string 时间窗别名
stime / etime int64 自定义时间戳
filters object 模板专属筛选条件

6.16 CLI 对应关系

Analytics API 均由 zcloud analytics 命令适配:

# 原 6 page
zcloud analytics overview kpi --format json
zcloud analytics access status --window last_24h --format json
zcloud analytics protect waf/types --format json
zcloud analytics ai logs --page 1 --size 20 --format json
zcloud analytics bot session <session-id> --format json
zcloud analytics alert ack <alert-id>

# 2026-04-30 chart-rebuild 6 phase 扩展(13 条新命令)
zcloud analytics health summary --window last_24h --format json
zcloud analytics ops traffic-users --top 20 --format json
zcloud analytics closure summary --format json
zcloud analytics cache summary --format json
zcloud analytics logs list --window last_24h --status 403 --format json
zcloud analytics logs detail req-abc123 --format json
zcloud analytics logs export --format csv --fields ctime,uuid,host,uri,status > logs.csv
zcloud analytics closure alerts confirm --ids a1,a2,a3
zcloud analytics closure risks confirm --ids ev_001,ev_002
zcloud analytics reports templates --format json
zcloud analytics reports list --format json
zcloud analytics reports describe r-001 --format json
zcloud analytics reports generate --template protection-value --window last_30d --format pdf
zcloud analytics reports download r-001 --output report.pdf

后续如果新增 Analytics API,必须同步增加或确认已有 CLI 适配;如果只是新增 chart-key,至少要更新 CLI chart-key 清单与文档。


§7 套餐目录(Plan)

对外开放范围说明:仅以下两个只读接口对外开放,用于查询套餐目录。套餐的创建/编辑/删除、为用户开通、订阅查询,以及订单的续费/变更/退款/审核/api/plan/orders/*),均属平台控制台管理操作,直接操作在线计费数据,不在对外对接 API/CLI 范围(仅平台运维经控制台 + RBAC 使用)。

GET /api/plan/plans — 套餐列表(分页)

查询套餐目录,支持按产品类型和关键词过滤,分页返回。

所需权限plan.plan.list

Query 参数

参数 类型 必填 默认值 说明
page int 1 页码(从 1 开始)
size int 20 每页条数
prod_type int 0 (全部) 产品类型过滤:2=WAF · 4=Monitor · 32=GFIP
keyword string 套餐名称关键词模糊搜索

响应 data 字段

{
  "list": [ { "plan_id": "...", "name": "基础版", "prod_type": 2, "price": 99.00, "valid": 365, "level": 1, "open_status": true, ... } ],
  "total": 10,
  "page": 1,
  "size": 20
}

示例

# Bearer Session
curl -H "Authorization: Bearer $TOKEN" \
  "$API/api/plan/plans?prod_type=2&keyword=基础&page=1&size=20"

# API Key
curl -H "Authorization: ApiKey zck_prefix.secret" \
  "$API/api/plan/plans?prod_type=2"

GET /api/plan/plans/{id} — 套餐详情

按套餐 ID 查询单个套餐的完整信息(含 content 配额 JSON)。

所需权限plan.plan.view

Path 参数

参数 类型 必填 说明
id string 套餐 ID(UUID)

响应 data 字段(PlanVO

字段 类型 说明
plan_id string 套餐唯一 ID(UUID)
name string 套餐名称
content object 套餐配额 JSON(各产品类型对应字段不同)
price float 套餐价格(保留 2 位小数)
scene string 适用场景描述
comment string 备注
open_status bool 是否公开售卖
valid int64 有效期(天数)
effect int32 生效方式
level int64 套餐等级
creator_id string 创建者 ID
ctime int64 创建时间(Unix 毫秒)
utime int64 更新时间(Unix 毫秒)
version string 套餐来源版本(cloud / zmod)
prod_type int32 产品类型(2=WAF 4=Monitor 32=GFIP)

示例

curl -H "Authorization: Bearer $TOKEN" \
  "$API/api/plan/plans/550e8400-e29b-41d4-a716-446655440000"

CLI 等价命令

zcloud plan list --prod-type 1 --keyword 基础版
zcloud plan describe <plan_id>

§8 节点安装 / 升级(Node Install)

用途:在防护节点主机上一键安装 / 升级 skynet-node。链路分两侧:管理面(平台登录态 + RBAC)注册安装包、生成一次性命令、查询任务、撤销 token;安装机侧(仅认安装 token)拉脚本、下载包/env、回报结果。
后端实现:src/backend/internal/node/{handler,service,repo}/install.go、路由 src/backend/internal/node/route.go、回收任务 src/backend/internal/app/install_reaper.go

8.0 鉴权与状态码

维度 管理面接口 安装机侧接口
路径 /install/artifacts /commands /upgrades /jobs /tokens/:id/revoke /install/script /package /env /report
鉴权 平台统一登录态(Bearer Session / API Key)+ RBAC Authorization: Bearer <install_token>
RBAC action(perms.NodeNode artifact / install / upgrade / job / revoke 无(token 自鉴权)
鉴权失败 401 / 403(平台统一信封) 401,并带 WWW-Authenticate: Bearer realm="node-install"(challenge)

要点:

8.1 POST /api/node/install/artifacts — 注册本地安装包并预检

注册后端主机上已存在的安装包,扫描并计算 sha256、跑预检。

curl -sS -X POST https://<cloud>/api/node/install/artifacts \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"name":"skynet-node-1.0.0.tar.gz","version":"1.0.0","package_path":"/data/artifacts/skynet-node-1.0.0.tar.gz"}'

约束:

响应(节选,package_path 仅回文件名,不暴露后端绝对路径):

{"code":0,"data":{
  "artifact_id":"8f1c…","name":"skynet-node-1.0.0.tar.gz","version":"1.0.0",
  "sha256":"…64hex…","status":"ready",
  "precheck":{"passed":true,"checks":[
    {"key":"required_env","severity":"error","passed":true,"message":"required env keys found"},
    {"key":"binary_version_drift","severity":"warning","passed":true,"message":"…"}
  ]}
}}

GET /api/node/install/artifacts 列出已注册包;列表里的 package_path 同样只展示文件名,前端/示例不要展示后端真实绝对路径

8.2 POST /api/node/install/commands · POST /api/node/install/upgrades · POST /api/node/install/uninstalls — 生成一次性命令

curl -sS -X POST https://<cloud>/api/node/install/commands \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{
        "artifact_id":"8f1c…",
        "node_id":"可选-目标节点UUID",
        "server_url":"https://<cloud>",
        "ttl_seconds":3600,
        "max_uses":20,
        "env":{
          "ZCLOUD_NGX_ACCESS_TOPIC":"cloud/ngx",
          "ZCLOUD_CC_TOPIC":"cc/sync",
          "ZCLOUD_SYNC_TOPIC":"zcloud-sync",
          "ZCLOUD_DELTA_TOPIC":"zcloud-delta",
          "ZCLOUD_BLOCK_TOPIC":"zcloud-block"
        }
      }'

升级命令用 /upgrades,等价于 action=upgrade,请求体相同。

卸载命令用 /uninstalls,等价于 action=uninstall,请求体相同(同 install/upgrade 的 artifact_id 必填、server_url 必填、env/ttl_seconds/max_uses 可选)。与升级一样作用于已有节点,node_id 必填(缺省 → 400「卸载必须指定目标节点」)。响应同样是一次性 commandcurl … | sudo bash 一行,明文 token 只出现一次)。权限:node.node.uninstall

⚠️ 破坏性、不可恢复:引导脚本据 GET /api/node/install/package 返回的服务端权威响应头 X-Install-Action(值取自 token 绑定 job 的动作,此处为 uninstall)决定跑包内 uninstall.sh 而非 install.sh,并用 here-string 自动应答其交互式 [y/N] 确认。uninstall.sh停止并移除该节点上的全部防护服务(nginx / agent / waf-spoa 等)及其数据目录,操作不可恢复。

范围:卸载只移除节点主机上的服务不删除云端节点列表里的节点记录。如需同时清掉节点记录,运维另行调用 DELETE /api/node/nodes/:id

响应:

{"code":0,"data":{
  "job_id":"…","token_id":"…","token_prefix":"nit_xxxxxxx",
  "expires_at":1735900000000,
  "command":"curl -fsSL --connect-timeout 10 --max-time 60 -H 'Authorization: Bearer nit_…' 'https://<cloud>/api/node/install/script' | sudo bash -s -- --token 'nit_…' --server 'https://<cloud>'"
}}

约束 / 行为:

8.3 安装机侧:script / package / env / report

command 拉取并执行的脚本(GET /install/script)会:set -euo pipefail + 退出清理临时目录 → 带 --connect-timeout/--max-time/--retry 下载 package、env → 校验 X-Artifact-SHA256 与本地 sha256sum 一致(不一致上报 failed 并退出)→ 用云端 env 覆盖包内 env.conf若注册变量含 AGENT_PORT 则注入解压出的 install*.sh(把 agent 监听/注册端口从默认 33020 改为该值,仅改解压副本,不动已校验包文件)→ 执行包内 install.sh → 经 /report 回报 running / success / failed

token 配额语义(关键)

接口 鉴权方式 是否消耗 max_usesuse_count 计数 / 副作用
GET /install/script 校验 token 有效性 ValidateBearerNoUse 不消耗,便于脚本可被重复拉取
GET /install/package 校验并占用 use_count+1 响应头 X-Artifact-SHA256 / X-Install-Job-ID;job 置 running
GET /install/env 校验并占用 use_count+1 返回 env 文本;env 载荷不可用时 403
POST /install/report ValidateBearerForReport 独立 report_count+1消耗 max_uses

安装机侧手动联调:

TOKEN=nit_xxx; BASE=https://<cloud>
curl -fsSL -H "Authorization: Bearer $TOKEN" "$BASE/api/node/install/script"
curl -fsSL -D - -o pkg.tar.gz -H "Authorization: Bearer $TOKEN" "$BASE/api/node/install/package"
curl -fsSL -H "Authorization: Bearer $TOKEN" "$BASE/api/node/install/env"
curl -fsS -X POST "$BASE/api/node/install/report" \
  -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
  -d '{"status":"success","message":"done","hostname":"node-1","node_version":"1.0.0"}'

8.4 GET /api/node/install/jobs · /jobs/:id — 查询任务

curl -sS -H "Authorization: Bearer $TOKEN" https://<cloud>/api/node/install/jobs        # 最近 50 条
curl -sS -H "Authorization: Bearer $TOKEN" https://<cloud>/api/node/install/jobs/<job_id>  # 含 reports[]

statuspendingrunningsuccess / failedstarted_at / finished_at 为毫秒时间戳,0 表示未发生。详情接口附最近 reports(最多 20 条)与 recent_report

8.5 POST /api/node/install/tokens/:id/revoke — 撤销 token

curl -sS -X POST -H "Authorization: Bearer $TOKEN" \
  https://<cloud>/api/node/install/tokens/<token_id>/revoke

8.6 Stale job 回收(reaper)

后端单进程后台扫帚 install_reaper.go:启动时立即跑一次,之后每 10m 一次。把 ctime 早于 now - 24hStaleJobMaxAge)且仍处于 pending/running 的 job 原子置为 failedfinished_at=nowmessage="install job timed out without report"),并在同一事务内 revoke 指向这些 job 的仍 active 的 token —— 防止被强杀的安装机事后再用 bearer token 复活已关闭的 job。终态 job 不会被改写;多副本下事务 WHERE 子句保证幂等(仅首个进程命中,其余 RowsAffected=0 静默退出)。

8.7 前端展示约定

8.8 POST /api/node/reg — 节点 agent 自注册

节点 agent 自注册端点。公共端点:不挂 RBAC、不需要 Bearer 鉴权,与 POST /api/node/install/report 同类(属 agent 基础设施,无 zcloud CLI 命令)。唯一访问门槛是请求体里的共享口令 dummy_token——必须等于 agent 端硬编码的固定常量值,不匹配即拒绝。

后端实现:src/backend/internal/node/{handler,service}/reg.go、路由 src/backend/internal/node/route.gorg.POST("/reg", h.RegNode))。

请求体字段(注意 manger_addr 是 agent 既有拼写,缺 a,不能改成 manager_addr):

字段 类型 必填 说明
node_id string 首次安装为空;非空表示 agent 已持有节点 ID(命中既有节点则复用,不新建)
manger_addr string 管理地址 host:port,agent 用 ip route get 自动探测后上报。端口缺失时回落默认端口 33020
node_type string proto 枚举名字符串,如 "NODE_1_WAF"仅支持 WAF 防护节点(空 / "NODE_1_WAF" / "waf" / "1" 均映射为 WAF,其它值返回非零 code
extend_config string url-escape 后的扩展配置,自注册暂不消费
dummy_token string 共享口令,必须等于 agent 硬编码的固定常量值,否则拒绝
plugin string 插件列表,如 "waf,detect,agent,ebpf"
only_acl bool 仅 ACL 模式标记,自注册节点不走该路径
ip string only_acl 关联参数,标准注册为空
acl_tags string only_acl 关联参数
ip_groups string only_acl 关联参数
curl -sS -X POST https://<cloud>/api/node/reg \
  -H 'Content-Type: application/json' \
  -d '{
        "node_id":"",
        "manger_addr":"192.168.14.171:33020",
        "node_type":"NODE_1_WAF",
        "dummy_token":"<agent 硬编码共享口令>",
        "plugin":"waf,detect,agent,ebpf"
      }'

响应:标准信封 {code, message, data}dataRegNodeResponse

{"code":0,"message":"success","data":{
  "node_id":"3f2c…",
  "listen_addr":":33020",
  "tls":false,
  "cert":"",
  "key":"",
  "settings":{},
  "plugin":{}
}}
响应字段 类型 说明
node_id string 节点 ID。幂等命中既有节点时返回既有 ID;首次注册返回新建 ID
listen_addr string 监听地址,形如 ":33020",取自管理地址端口(缺失回落默认 33020
tls bool 新平台(NSQ)固定 false(不再用 etcd 下发每节点证书)
cert string 新平台返回空串
key string 新平台返回空串
settings object 下发配置。新平台配置走 NSQ 发布订阅,固定返回空映射 {}
plugin object 插件配置。新平台固定返回空映射 {}

语义要点


§9 全能网络诊断(Netdiag)

对域名跑全链路体检或使用 Ping 单项工具。全部为只读探测,从管理端发起,不写任何数据。所有接口需 netdiag.tool.run 权限。客户级账号(role_id ≥ 10)只能诊断名下域名,对不可见的平台域名一律按"未接入"处理。

接口 说明
GET /api/netdiag/dns?domain=<domain> DNS 解析检查
GET /api/netdiag/icp?domain=<domain> ICP 备案查询
GET /api/netdiag/ssl?domain=<domain> SSL 证书检查
GET /api/netdiag/access?domain=<domain> 云防护接入配置检查
GET /api/netdiag/nodes?domain=<domain> 节点连通性检查(仅平台接入域名)
GET /api/netdiag/origin?domain=<domain> 源站健康检查(仅平台接入域名)
GET /api/netdiag/ping?target=<target> Ping 检测(域名或 IP)

GET /api/netdiag/dns — DNS 解析检查

比对系统公共解析、直查权威 NS 的解析结果,以及当前 CNAME 是否指向平台接入别名。

响应 data 字段

{
  "domain": "www.example.com", "main_domain": "example.com",
  "ns": ["ns1.dnspod.net", "ns2.dnspod.net"],
  "public_ips": ["203.0.113.10"], "authoritative_ips": ["203.0.113.10"],
  "cname": "example-com.u2x8.wafcname.com",
  "expected_cname": "example-com.u2x8.wafcname.com",
  "cname_matched": true, "on_platform": true
}

GET /api/netdiag/icp — ICP 备案查询

调用外部开放接口查主域备案。接口异常时返回 checked=false(不阻断体检)。

{ "checked": true, "filed": true, "main_domain": "example.com",
  "site_name": "示例科技有限公司", "site_no": "京ICP备2024012345号-1", "subject_no": "京ICP备2024012345号" }

GET /api/netdiag/ssl — SSL 证书检查

平台接入域名经防护节点探测(检查平台下发证书),否则直连域名 443。via 标注探测路径。

{ "found": true, "issuer": "Let's Encrypt · R3", "subject_cn": "www.example.com",
  "not_before": 1747094400000, "not_after": 1754870400000, "days_left": 30,
  "sans": ["www.example.com", "example.com"], "hostname_match": true, "via": "node" }

GET /api/netdiag/access — 云防护接入配置检查

{ "on_platform": true, "domain_id": "...", "cname": "...",
  "audit_status": 4, "parsing_state": 2,
  "ports": [ { "port": 80, "scheme": "http" }, { "port": 443, "scheme": "https" } ],
  "node_count": 12, "origin_count": 1 }

GET /api/netdiag/nodes — 节点连通性检查

以域名身份逐节点、逐对外端口发起 HTTP 探测。仅平台接入域名,未接入返回 404。

{ "total": 12, "reachable": 12, "avg_latency_ms": 21,
  "nodes": [ { "node_id": "...", "name": "华东-BGP-01", "line": 4, "ip": "10.0.1.1", "ok": true,
    "checks": [ { "port": 80, "scheme": "http", "ok": true, "status_code": 200, "latency_ms": 12 } ] } ] }

GET /api/netdiag/origin — 源站健康检查

管理端直连源站,先 TCP 连通、通了再发 HTTP(Host 带域名)。status_code:-1=连接失败,0=纯 TCP 连通。仅平台接入域名

{ "origins": [ { "addr": "203.0.113.10", "checks": [
    { "port": 80, "scheme": "http", "ok": true, "status_code": 0, "latency_ms": 46 },
    { "port": 443, "scheme": "https", "ok": false, "status_code": -1, "latency_ms": 3000, "error": "timeout" } ] } ] }

GET /api/netdiag/ping — Ping 检测

管理端 ping 目标(固定 4 包)。目标严格校验后作为参数直传,不经 shell。

{ "target": "www.example.com", "ok": true, "sent": 4, "received": 4, "loss_pct": 0,
  "rtt_min_ms": 11.2, "rtt_avg_ms": 13.4, "rtt_max_ms": 15.8, "output": "..." }

§A Analytics 通用查询参数

适用于所有单图 GET 接口(GET /api/analytics/<page>/<chart>)。未传参数时后端使用默认值;传入无权限的 target_user_id 或跨 OEM 资源时返回 403。

参数 类型 必填 取值/示例 说明
window string last_1h / last_24h / last_7d 时间窗口别名;为空时后端默认 last_24h
stime int64 1746748800000 自定义起始时间 Unix 毫秒(与 etime 配对,比 window 优先级高)
etime int64 1746835200000 自定义结束时间 Unix 毫秒
site_id string site-001 站点过滤
domain_id string d_8a3b1c 域名过滤
target_user_id string u-tenant-001 客户级切换被查看用户;后端统一做越权校验
compare bool false 是否启用上一周期对比(仅部分 chart 支持)
top int 10 TopN,默认 10,最大 100
order string bytes_desc 排序方式,具体含义由图表定义(如 top-url 支持 request_count_desc/bytes_desc/cache_desc
page int 1 列表类图表分页
size int 20 列表类分页每页条数,最大 100

单图响应骨架

{
  "code": 0,
  "message": "ok",
  "data": {
    "chart_key": "access/status",
    "render_hint": "categorical_distribution_over_time",
    "schema": {
      "dimensions": [
        { "name": "status_class", "type": "enum", "values": ["2xx", "3xx", "4xx", "5xx"] },
        { "name": "time", "type": "timestamp", "unit": "ms" }
      ],
      "measures": [
        { "name": "count", "type": "integer", "unit": "requests" }
      ]
    },
    "rows": [],
    "meta": {
      "cache": "miss",
      "source": "postgres",
      "latency_ms": 12,
      "window": {
        "stime": 1777526400000,
        "etime": 1777530000000,
        "granularity": "5m",
        "bucket_table": "tfs_flow_domains"
      }
    }
  }
}

window.granularity响应字段,描述实际命中的聚合粒度(5m / 1h / 1d),不是用户输入参数。客户传 window=last_24h,后端按窗口大小自动选表。


§B 可视化建议总表

B.1 Chart 统一契约 — render_hint 速查

所有 chart-key 由前端按 render_hint 自动分发到 6 个 chart 组件之一。这是契约真值(docs/specs/chart-contract.md §2),不可自创新词。

render_hint schema 形状 推荐前端组件 典型场景
kpi 0~1 dim + 1+ measure KpiGroupCard 多指标数字卡组(如 overview/kpi 的 6 测度)
categorical_distribution 1 categorical dim + 1 measure PieCard(≤8 类)/ BarCard(>8 类) 一维占比(如 overview/event-type)
categorical_distribution_over_time 1 categorical + 1 timestamp + 1 measure StackedBarCard / LineCard 多 series 多分类按时间堆叠(如 access/status)
time_series_single 1 timestamp + 1 measure LineCard 单测度时序(如 access/request-hm 无 compare)
time_series_multi 1 timestamp + ≥2 measures, 1 categorical + 1 timestamp + 1 measure LineCard 多线 多测度时序(如 access/flow-hm 5 测度);compare 走子形态 B
topn 1 string dim + 1 measure BarCard 横向 / RankingCard 已排序 TOP-N(如 protect/waf/top-ip)
geo 1 geo dim + 1 measure GeoHeatmapCard 地理分布(如 protect/waf/geo)
table 任意 TableCard 不适合可视化的兜底

新增 hint 词汇必须双方评审通过(cloud + Aegeon),不可单边扩词汇表。

B.2 数据形态速查

下表把"输出数据形态 → 推荐图表"的映射汇总在一起,对接前端时可作为速查表。实际响应仍以 Chart 统一契约的 schema + rows 为准。

数据形态 典型字段示例 推荐图表 不推荐
单值(标量) {count: 12345} KPI 数字卡 折线 / 饼图
多 KPI(4-6 个标量) {domain_count, requests, blocked, block_rate, qps} 多 KPI 卡阵 / 雷达图 单饼图
时序单系列 [{ctime, value}, ...] 折线图 / 面积图 饼图
时序多系列 [{ctime, requests, attacks}, ...] 多线折线 / 堆叠面积 饼图
时序对比(compare) {current:[...], previous:[...]} 双线对比折线(实虚线) 单折线
维度分布(少类 ≤ 8) [{key, count}, ...] 饼图 / 环形图 表格
维度分布(多类 > 8) [{key, count}, ...] 横向 bar / 柱图 饼图(碎片化)
地理分布 [{region, count}, ...] 中国/世界地图热力 表格
排行 TOP [{key, count, ...}, ...] 横向 bar / 表格(含明细列) 折线 / 饼图
二维矩阵 [[v11, v12], [v21, v22]] 热力图 折线
散点 [{x, y, size, ...}, ...] 散点图 / 气泡图 饼图
列表分页 {list, total, page, size} 表格(分页) 任何图表
时间线明细 {items: [{ctime, ...}]} 时间线(vertical timeline) 饼图
占位 {available: false, ...} 不渲染图表,渲染 n-empty 占位卡片 假数据

配色建议


§C 相关文档


完整 API 索引

REST 全集导出为 OpenAPI v3:

GET /api/openapi.json

可直接导入 Postman / Insomnia / Swagger UI。所有路由含完整 schema、参数、响应示例与所需权限 key。


红网云 · REST API Documentation · 完整索引以 /api/openapi.json 为准

POST /api/guard/bwlist/ips/check-conflicts — 添加前的 IP 冲突检测(只读)

用途:cloud 增值能力(zmod 无)。引擎侧白名单优先于黑名单——把一个已在白名单里的 IP 加进黑名单,拦截不会生效且无任何提示。本接口在添加前显式检测:往黑名单组加 → 查同用户的白名单(2)/CDN白名单(3);往白名单组加 → 查黑名单(1)。

鉴权guard.bwlist.list输入{"set_id"?, "ip_set_type"?, "ips":[...]}(二选一:详情/编辑带 set_id;新建组带 ip_set_type;ips 去重后 ≤1000)。输出checkedconflicts[]ip_addr/set_id/set_name/ip_set_type)。

口径ip_addr 精确匹配(对齐平台 CMPTYPE_01_EQ),不做 CIDR 包含判断;结果仅提示,不阻止添加。


GET /api/guard/bwlist/sets/{id}/domains — 名单组关联的域名查询(域名关联模式)

用途:对齐 zmod「关联策略&域名」弹窗的域名关联:名单组除了绑策略(该策略下所有域名生效),还可以直接绑定到若干域名(只对所选域名生效)。两条路径互不影响,命中任一即生效。

鉴权guard.bwlist.list

输出字段data.bound / data.available(元素 {domain_id, domain}),均为名单组归属用户的域名。

PUT /api/guard/bwlist/sets/{id}/domains — 覆盖设置名单组绑定的域名集合

鉴权guard.bwlist.edit

输入{"domain_ids": ["..."]} 目标全集(覆盖式;空数组=解绑全部;必须属于名单组归属用户)。

落库语义(与 zmod addIPSetToData/delIPSetToData 一致):diff 后把组 ID 追加/移出各域名 guard_domain_settings.domain_bwl_config_settingblacks(黑名单)或 Whites(其余类型,注意大写 W);blob 其余字段(dis_list/names/hash/minio_flag)原样保留。变更后对受影响域名触发下发。