368 lines
14 KiB
YAML
368 lines
14 KiB
YAML
openapi: 3.1.0
|
||
info:
|
||
title: Proxy Pool Admin API
|
||
version: 1.0.0
|
||
description: |
|
||
运维状态与受控变更接口。该入口必须与 Distribution 分端口和权限。
|
||
已认证凭据可配置 `admin:read`(Status、审计)和 `admin:write`(变更);
|
||
未配置权限的旧凭据保留全权限行为。
|
||
servers:
|
||
- url: http://127.0.0.1:8082
|
||
tags:
|
||
- name: Status
|
||
- name: Audit
|
||
- name: Upstreams
|
||
- name: Routing
|
||
- name: Configuration
|
||
security:
|
||
- AdminApiKey: []
|
||
- BasicAuth: []
|
||
- BearerAuth: []
|
||
- {}
|
||
paths:
|
||
/api/v1/status:
|
||
get:
|
||
tags: [Status]
|
||
operationId: getStatus
|
||
summary: 获取控制面摘要状态
|
||
description: 需要 `admin:read`。
|
||
responses:
|
||
'200':
|
||
description: 不含 Proxy 地址、Client 标识或 Secret 的聚合状态
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: '#/components/schemas/Status'
|
||
'401': {$ref: '#/components/responses/Unauthorized'}
|
||
'403': {$ref: '#/components/responses/Forbidden'}
|
||
'400': {$ref: '#/components/responses/BadRequest'}
|
||
'405': {$ref: '#/components/responses/MethodNotAllowed'}
|
||
'500': {$ref: '#/components/responses/InternalServerError'}
|
||
'503': {$ref: '#/components/responses/ServiceUnavailable'}
|
||
/api/v1/audit:
|
||
get:
|
||
tags: [Audit]
|
||
operationId: listAuditRecords
|
||
summary: 按 ID 游标读取管理面审计记录
|
||
description: |
|
||
需要 `admin:read`。
|
||
|
||
仅返回权威管理面的变更审计记录,按 `id` 升序排列。`afterId` 是排他游标:
|
||
后续页面只包含 `id` 大于该值的记录。未传 `limit` 时返回 100 条,单页最多
|
||
1000 条。
|
||
|
||
审计接口不返回 Proxy、Upstream URL、凭据或活动池内容。
|
||
parameters:
|
||
- name: afterId
|
||
in: query
|
||
required: false
|
||
description: 排他游标;仅返回 ID 大于此值的记录。
|
||
schema: {type: integer, minimum: 0, default: 0}
|
||
- name: limit
|
||
in: query
|
||
required: false
|
||
description: 单页记录数;默认 100,最大 1000。
|
||
schema: {type: integer, minimum: 1, maximum: 1000, default: 100}
|
||
responses:
|
||
'200':
|
||
description: 管理面审计记录页
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/json:
|
||
schema: {$ref: '#/components/schemas/AuditPage'}
|
||
'401': {$ref: '#/components/responses/Unauthorized'}
|
||
'403': {$ref: '#/components/responses/Forbidden'}
|
||
'400': {$ref: '#/components/responses/BadRequest'}
|
||
'405': {$ref: '#/components/responses/MethodNotAllowed'}
|
||
'500': {$ref: '#/components/responses/InternalServerError'}
|
||
'503': {$ref: '#/components/responses/ServiceUnavailable'}
|
||
/api/v1/upstreams/{name}/enable:
|
||
post:
|
||
tags: [Upstreams]
|
||
operationId: enableUpstream
|
||
summary: 启用 Upstream
|
||
description: 需要 `admin:write`。
|
||
parameters:
|
||
- $ref: '#/components/parameters/UpstreamName'
|
||
- $ref: '#/components/parameters/RequestID'
|
||
responses:
|
||
'200': {$ref: '#/components/responses/MutationResult'}
|
||
'401': {$ref: '#/components/responses/Unauthorized'}
|
||
'403': {$ref: '#/components/responses/Forbidden'}
|
||
'404': {$ref: '#/components/responses/NotFound'}
|
||
'409': {$ref: '#/components/responses/Conflict'}
|
||
'400': {$ref: '#/components/responses/BadRequest'}
|
||
'405': {$ref: '#/components/responses/MethodNotAllowed'}
|
||
'500': {$ref: '#/components/responses/InternalServerError'}
|
||
'503': {$ref: '#/components/responses/ServiceUnavailable'}
|
||
/api/v1/upstreams/{name}/disable:
|
||
post:
|
||
tags: [Upstreams]
|
||
operationId: disableUpstream
|
||
summary: 禁用 Upstream 并使已有资源自然 Drain
|
||
description: 需要 `admin:write`。
|
||
parameters:
|
||
- $ref: '#/components/parameters/UpstreamName'
|
||
- $ref: '#/components/parameters/RequestID'
|
||
responses:
|
||
'200': {$ref: '#/components/responses/MutationResult'}
|
||
'401': {$ref: '#/components/responses/Unauthorized'}
|
||
'403': {$ref: '#/components/responses/Forbidden'}
|
||
'404': {$ref: '#/components/responses/NotFound'}
|
||
'409': {$ref: '#/components/responses/Conflict'}
|
||
'400': {$ref: '#/components/responses/BadRequest'}
|
||
'405': {$ref: '#/components/responses/MethodNotAllowed'}
|
||
'500': {$ref: '#/components/responses/InternalServerError'}
|
||
'503': {$ref: '#/components/responses/ServiceUnavailable'}
|
||
/api/v1/routing/{name}/switch:
|
||
post:
|
||
tags: [Routing]
|
||
operationId: switchRouting
|
||
summary: 原子切换 Sequential Routing 当前 Upstream
|
||
description: 需要 `admin:write`。
|
||
parameters:
|
||
- name: name
|
||
in: path
|
||
required: true
|
||
schema: {type: string, minLength: 1, maxLength: 128}
|
||
- $ref: '#/components/parameters/RequestID'
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
additionalProperties: false
|
||
required: [expectedCurrent, target]
|
||
properties:
|
||
expectedCurrent: {type: string}
|
||
target: {type: string}
|
||
reason: {type: string, maxLength: 512}
|
||
responses:
|
||
'200': {$ref: '#/components/responses/MutationResult'}
|
||
'401': {$ref: '#/components/responses/Unauthorized'}
|
||
'403': {$ref: '#/components/responses/Forbidden'}
|
||
'404': {$ref: '#/components/responses/NotFound'}
|
||
'409': {$ref: '#/components/responses/Conflict'}
|
||
'400': {$ref: '#/components/responses/BadRequest'}
|
||
'405': {$ref: '#/components/responses/MethodNotAllowed'}
|
||
'413': {$ref: '#/components/responses/RequestEntityTooLarge'}
|
||
'415': {$ref: '#/components/responses/UnsupportedMediaType'}
|
||
'422': {$ref: '#/components/responses/UnprocessableEntity'}
|
||
'500': {$ref: '#/components/responses/InternalServerError'}
|
||
'503': {$ref: '#/components/responses/ServiceUnavailable'}
|
||
/api/v1/config/reload:
|
||
post:
|
||
tags: [Configuration]
|
||
operationId: reloadConfiguration
|
||
summary: 严格校验并原子发布新配置快照
|
||
description: 需要 `admin:write`。
|
||
parameters:
|
||
- $ref: '#/components/parameters/RequestID'
|
||
responses:
|
||
'200': {$ref: '#/components/responses/MutationResult'}
|
||
'401': {$ref: '#/components/responses/Unauthorized'}
|
||
'403': {$ref: '#/components/responses/Forbidden'}
|
||
'409': {$ref: '#/components/responses/Conflict'}
|
||
'400': {$ref: '#/components/responses/BadRequest'}
|
||
'405': {$ref: '#/components/responses/MethodNotAllowed'}
|
||
'422':
|
||
description: 新配置无效,旧配置继续运行
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/problem+json:
|
||
schema: {$ref: '#/components/schemas/Problem'}
|
||
'500': {$ref: '#/components/responses/InternalServerError'}
|
||
'503': {$ref: '#/components/responses/ServiceUnavailable'}
|
||
components:
|
||
securitySchemes:
|
||
AdminApiKey: {type: apiKey, in: header, name: X-Admin-Key}
|
||
BasicAuth: {type: http, scheme: basic}
|
||
BearerAuth: {type: http, scheme: bearer}
|
||
parameters:
|
||
UpstreamName:
|
||
name: name
|
||
in: path
|
||
required: true
|
||
schema: {type: string, minLength: 1, maxLength: 128}
|
||
RequestID:
|
||
name: X-Request-ID
|
||
in: header
|
||
required: false
|
||
schema: {type: string, maxLength: 128}
|
||
headers:
|
||
RequestID:
|
||
description: 服务端最终使用的请求标识。
|
||
schema: {type: string, maxLength: 128}
|
||
schemas:
|
||
AuditPage:
|
||
type: object
|
||
additionalProperties: false
|
||
required: [records]
|
||
properties:
|
||
records:
|
||
type: array
|
||
description: 按 ID 升序的审计记录;没有更多记录时为空数组。
|
||
items: {$ref: '#/components/schemas/AuditRecord'}
|
||
AuditRecord:
|
||
type: object
|
||
additionalProperties: false
|
||
required: [id, requestId, actorId, action, resourceType, resourceName, changed, version, occurredAt]
|
||
properties:
|
||
id: {type: integer, minimum: 0}
|
||
requestId: {type: string}
|
||
actorId: {type: string}
|
||
sourceIp: {type: string}
|
||
action: {type: string}
|
||
resourceType: {type: string}
|
||
resourceName: {type: string}
|
||
changed: {type: boolean}
|
||
version: {type: integer, minimum: 0}
|
||
reason: {type: string}
|
||
occurredAt: {type: string, format: date-time}
|
||
Status:
|
||
type: object
|
||
additionalProperties: false
|
||
required: [configVersion, snapshotVersion, upstreams, workers]
|
||
properties:
|
||
configVersion: {type: string}
|
||
snapshotVersion: {type: integer, minimum: 0}
|
||
upstreams:
|
||
type: array
|
||
items:
|
||
type: object
|
||
additionalProperties: false
|
||
required: [name, enabled, available, checking, suspect, draining, extracted]
|
||
properties:
|
||
name: {type: string}
|
||
enabled: {type: boolean}
|
||
available: {type: integer, minimum: 0}
|
||
checking: {type: integer, minimum: 0}
|
||
suspect: {type: integer, minimum: 0}
|
||
draining: {type: integer, minimum: 0}
|
||
extracted: {type: integer, minimum: 0}
|
||
consecutiveEmptyFetch: {type: integer, minimum: 0}
|
||
fetchErrorCount: {type: integer, minimum: 0}
|
||
workers:
|
||
type: array
|
||
items:
|
||
type: object
|
||
additionalProperties: false
|
||
required: [id, zone, connected, snapshotVersion]
|
||
properties:
|
||
id: {type: string}
|
||
zone: {type: string}
|
||
connected: {type: boolean}
|
||
snapshotVersion: {type: integer, minimum: 0}
|
||
staleSeconds: {type: integer, minimum: 0}
|
||
MutationResult:
|
||
type: object
|
||
additionalProperties: false
|
||
required: [requestId, changed, version]
|
||
properties:
|
||
requestId: {type: string}
|
||
changed: {type: boolean}
|
||
version: {type: integer, minimum: 0}
|
||
message: {type: string}
|
||
Problem:
|
||
type: object
|
||
required: [type, title, status, code]
|
||
properties:
|
||
type: {type: string, format: uri}
|
||
title: {type: string}
|
||
status: {type: integer}
|
||
code: {type: string}
|
||
detail: {type: string}
|
||
requestId: {type: string}
|
||
responses:
|
||
MutationResult:
|
||
description: 操作已提交或目标状态原本已满足
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/json:
|
||
schema: {$ref: '#/components/schemas/MutationResult'}
|
||
Unauthorized:
|
||
description: 管理入口认证失败
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
WWW-Authenticate: {schema: {type: string}}
|
||
content:
|
||
application/problem+json:
|
||
schema: {$ref: '#/components/schemas/Problem'}
|
||
Forbidden:
|
||
description: 调用主体无该管理权限
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/problem+json:
|
||
schema: {$ref: '#/components/schemas/Problem'}
|
||
NotFound:
|
||
description: Upstream 或 Routing 不存在
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/problem+json:
|
||
schema: {$ref: '#/components/schemas/Problem'}
|
||
Conflict:
|
||
description: 预期版本或 expectedCurrent 与权威状态不一致
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/problem+json:
|
||
schema: {$ref: '#/components/schemas/Problem'}
|
||
BadRequest:
|
||
description: 请求标识或请求体无效
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/problem+json:
|
||
schema: {$ref: '#/components/schemas/Problem'}
|
||
MethodNotAllowed:
|
||
description: 端点不支持该 HTTP 方法
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
Allow: {schema: {type: string}}
|
||
content:
|
||
application/problem+json:
|
||
schema: {$ref: '#/components/schemas/Problem'}
|
||
RequestEntityTooLarge:
|
||
description: 请求体超过管理入口限制
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/problem+json:
|
||
schema: {$ref: '#/components/schemas/Problem'}
|
||
UnsupportedMediaType:
|
||
description: Content-Type 不是 application/json
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/problem+json:
|
||
schema: {$ref: '#/components/schemas/Problem'}
|
||
UnprocessableEntity:
|
||
description: 命令字段或配置内容违反业务约束
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/problem+json:
|
||
schema: {$ref: '#/components/schemas/Problem'}
|
||
InternalServerError:
|
||
description: 未分类的控制面错误
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/problem+json:
|
||
schema: {$ref: '#/components/schemas/Problem'}
|
||
ServiceUnavailable:
|
||
description: 权威控制面暂时不可用
|
||
headers:
|
||
X-Request-ID: {$ref: '#/components/headers/RequestID'}
|
||
content:
|
||
application/problem+json:
|
||
schema: {$ref: '#/components/schemas/Problem'}
|