## 防重放 (`site-replay-guards`)

单个站点的防重放设置:受保护接口须带一次性令牌或 App 签名头,没有或重放时拦截或验证;可单独设置或恢复按方案;属 WAF 增强。

鉴权: 用户（部分参数仅管理员） · 文档: https://www-demo.kuxitong.com/zh/docs/api/site-replay-guards

### GET `/api/v2/site-replay-guards/{site_id}`

获取站点防重放设置 · 鉴权: 用户（部分参数仅管理员）

```sh
curl "https://panel.example.com/api/v2/site-replay-guards/{site_id}" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### PUT `/api/v2/site-replay-guards/{site_id}`

设置站点防重放 · 鉴权: 用户（部分参数仅管理员）

请求体:

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `action` | ReplayGuardAction |  | 没有令牌或重放时:block 回 403;challenge 出 JS 挑战。不写 = block。 可选值: ["block","challenge"] |
| `app_salt` | string |  | 签名盐(应用密钥,32 字节 hex);app_sign 开而没有它 = 签名头一律不认。 |
| `app_sign` | boolean |  | App 签名头模式:请求带签名头即按签名校验,不需要令牌。 |
| `bind_ip` | boolean |  | 令牌再绑客户端 IP(换 IP 即失效)。 |
| `enabled` | boolean | 有 | 是否开启。 |
| `expected_updated_at` | string |  | 乐观锁:调用方读到的 updated_at(读到为空 = 还没设置过,用 1970-01-01T00:00:00Z)。与库里当前值不等则回 409 stale_version、不保存;不带则不检查。 |
| `methods` | ReplayGuardMethod[] |  | 受保护方法;不写 = POST/PUT/PATCH/DELETE。 |
| `paths` | ReplayGuardPath[] |  | 保护接口路径表,最多 64 条;空 = 不保护。 |
| `ttl_s` | integer |  | 令牌有效期(秒),签名头的时间戳也按它判;不写 = 120。 |

```sh
curl -X PUT "https://panel.example.com/api/v2/site-replay-guards/{site_id}" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

### DELETE `/api/v2/site-replay-guards/{site_id}`

站点防重放恢复按方案 · 鉴权: 用户（部分参数仅管理员）

```sh
curl -X DELETE "https://panel.example.com/api/v2/site-replay-guards/{site_id}" \
  -H "X-API-Key: $GOCDN_API_KEY"
```

防重放对象:

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `action` | ReplayGuardAction |  | 没有令牌或重放时:block 回 403;challenge 出 JS 挑战。不写 = block。 可选值: ["block","challenge"] |
| `app_salt` | string |  | 签名盐(应用密钥,32 字节 hex);app_sign 开而没有它 = 签名头一律不认。 |
| `app_sign` | boolean |  | App 签名头模式:请求带签名头即按签名校验,不需要令牌。 |
| `bind_ip` | boolean |  | 令牌再绑客户端 IP(换 IP 即失效)。 |
| `enabled` | boolean |  | 是否开启。 |
| `expected_updated_at` | string |  | 乐观锁:调用方读到的 updated_at(读到为空 = 还没设置过,用 1970-01-01T00:00:00Z)。与库里当前值不等则回 409 stale_version、不保存;不带则不检查。 |
| `gate` | string |  | 站点租户的 WAF 增强判定档:full 可改;readonly 到期只读缓冲内(License.readonly_days,缺省 7 天)照常生效、设置只读;basic 没开通或缓冲已过,不生效、设置保留。 可选值: ["full","readonly","basic"] (只读) |
| `gate_expired_at` | string |  | 仅管理员响应且 gate_reason=expired 时带:平台 WAF 增强授权的到期时刻;租户响应不带。 (只读) |
| `gate_owner` | WafGateOwner |  | 站点属主,仅管理员响应带;租户响应不带别人的信息。 (只读) |
| `gate_packages` | string[] |  | 仅管理员响应带:属主名下未到期的套餐名(最多 20 个);租户响应不带。 (只读) |
| `gate_reason` | WafGateReason |  | WAF 增强(waf_enhanced)没生效的原因,gate 为 full 或 readonly 时不带。package = 平台已授权但属主没有未到期且含 WAF 增强的套餐;license = 平台没开通(从未授权、服务商撤销、授权被停用);expired = 平台授权已到期且只读缓冲期(License.readonly_days,缺省 7 天)已过,已回落基础。 可选值: ["package","license","expired"] (只读) |
| `methods` | ReplayGuardMethod[] |  | 受保护方法;不写 = POST/PUT/PATCH/DELETE。 |
| `paths` | ReplayGuardPath[] |  | 保护接口路径表,最多 64 条;空 = 不保护。 |
| `site_id` | integer |  | 网站 ID (只读) |
| `source` | ProtectSiteSource |  | 站点这一项的来源:scheme 按方案(保存方案时跟着变);custom 单独设置(含关闭、存量迁移来的行)。 可选值: ["scheme","custom"] (只读) |
| `ttl_s` | integer |  | 令牌有效期(秒),签名头的时间戳也按它判;不写 = 120。 |
| `updated_at` | string |  | 版本(没设过为空);修改时用 expected_updated_at。 (只读) |
