Генерируем api.gen.ts из docs/openapi.yaml (openapi-typescript), добавляем scripts/check-openapi-gen.sh в CI. Закрываем решение по X-Tenant-Id как unimplemented; обновляем docs/README под React/ReUI и apps/web. Co-authored-by: Cursor <cursoragent@cursor.com>
5050 lines
160 KiB
YAML
5050 lines
160 KiB
YAML
openapi: 3.1.0
|
||
info:
|
||
title: EvoBGP Control Plane API
|
||
version: 1.0.0
|
||
description: |
|
||
REST API управления префиксами, модулями ingest, ревизиями конфигурации BIRD и задачами (async jobs).
|
||
|
||
**Актуальная semver-сборка:** `GET /version` или `GET /v1/version` (поле `version`; совпадает с git-тегом `vX.Y.Z`).
|
||
|
||
**Соглашения:** префикс `/v1`; идентификаторы - UUID v7 или ULID (строки); время - ISO 8601 UTC.
|
||
Ошибки - `application/problem+json` ([RFC 9457](https://www.rfc-editor.org/rfc/rfc9457)).
|
||
Пагинация списков - `cursor` + `limit`; ответ содержит `items`, `next_cursor`, `has_more`.
|
||
|
||
**Аутентификация (dual):**
|
||
- **API key** — `Authorization: Bearer <token>` из `EVOBGP_API_KEYS` / таблицы `api_key` (роли `viewer`/`editor`/`operator`/`node`/`firewall`).
|
||
- **Portal JWT** — HS256 от auth-portal; claim `apps` должен содержать `bgp`; права `bgp:<section>:<action>`; tenant из `tenants.bgp` / `bgp_tenant_id` или fallback `EVOBGP_PORTAL_TENANT_ID`.
|
||
Публично: `GET /v1/auth/config` → `{ required, portal_url }`.
|
||
|
||
**Роли API key** (матрица): `viewer`, `editor`, `operator`, `node`. Нода использует отдельные пути и ключ с ролью `node`.
|
||
JWT permissions мапятся на ту же лестницу (`:read`→viewer, `:write`→editor, `:admin`→operator).
|
||
|
||
Заголовок `X-Tenant-Id`: **не реализован в handlers** (документировано в docs/access.md).
|
||
Tenant всегда из API-ключа / portal JWT claim / `EVOBGP_PORTAL_TENANT_ID`. Спецификация сохраняет заголовок как reserved future; не полагаться на него в клиентах.
|
||
license:
|
||
name: Proprietary
|
||
identifier: LicenseRef-Proprietary
|
||
|
||
servers:
|
||
- url: https://api.example.com
|
||
description: Базовый URL инсталляции (замените на свой)
|
||
|
||
tags:
|
||
- name: System
|
||
description: Liveness, readiness и метаданные сборки. Обычно без чувствительных данных; доступ может быть шире.
|
||
- name: Modules
|
||
description: Экземпляры модулей префиксов (AS, CDN, домены, статические IP-диапазоны) и вложенные записи. Чтение - viewer+; изменение - editor+.
|
||
- name: Lookup
|
||
description: Быстрая проверка membership IP/FQDN в списках (entries + module prefix snapshots) и community. Чтение - viewer+.
|
||
- name: DoH profiles
|
||
description: Профили DNS-over-HTTPS для модулей типа домены. Секрет в ответах не возвращается.
|
||
- name: Communities
|
||
description: Справочник BGP community в скоупе tenant.
|
||
- name: Peers
|
||
description: BGP-пиры и политики; привязка к спикеру или ко всем спикерам.
|
||
- name: Speakers
|
||
description: Регистрация и метаданные экземпляров BIRD (master/replica/canary).
|
||
- name: Revisions
|
||
description: История ревизий конфигурации, снимки префиксов, превью и откат (часто асинхронно).
|
||
- name: Deploy
|
||
description: Применение ревизий на спикерах и опциональный reload BIRD. Требуется роль operator.
|
||
- name: Jobs
|
||
description: Асинхронные задачи; статус и отмена (best-effort).
|
||
- name: Node
|
||
description: "API для evobgp-node (бандлы ревизий и enrollment). Отдельный ключ или mTLS, роль node."
|
||
- name: Settings
|
||
description: Глобальные настройки и feature flags; изменение - только operator.
|
||
- name: API keys
|
||
description: Управление API-ключами tenant (operator). Секрет возвращается только при создании и ротации.
|
||
- name: Auth
|
||
description: Сессия текущего API-ключа (tenant и роль).
|
||
- name: Monitoring
|
||
description: Наблюдаемость PostgreSQL и корреляция (instance-level, viewer+). Maintenance — operator.
|
||
- name: Audit
|
||
description: Журнал CRUD-изменений tenant (локально + опциональный push в auth-portal). Чтение — bgp:monitoring:read.
|
||
- name: Maintenance
|
||
description: Политики обслуживания PostgreSQL (instance-scoped). CRUD и запуск — operator.
|
||
- name: RuntimeLogs
|
||
description: |
|
||
Файловые runtime-логи Docker-сервисов (каталог EVOBGP_RUNTIME_LOGS_DIR).
|
||
Доступно только в процессе evobgp-all с примонтированным volume; иначе 503.
|
||
Просмотр — viewer+; очистка — operator+ (синхронно, с audit).
|
||
- name: Firewall
|
||
description: Linux firewall blocklist clients, policy rules (block/accept), and data-plane sync.
|
||
|
||
security:
|
||
- bearerAuth: []
|
||
|
||
externalDocs:
|
||
description: Черновик и контекст API
|
||
url: ./evobgp-api-sketches.md
|
||
|
||
components:
|
||
securitySchemes:
|
||
bearerAuth:
|
||
type: http
|
||
scheme: bearer
|
||
description: API-ключ в формате `Bearer <token>`. На edge может заменяться или дополняться mTLS.
|
||
|
||
parameters:
|
||
TenantId:
|
||
name: X-Tenant-Id
|
||
in: header
|
||
required: false
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
description: >
|
||
Явный tenant (только супер-роли). Без заголовка tenant определяется по API-ключу.
|
||
**Реализация v1:** заголовок в Go handlers не обрабатывается; tenant только из Bearer-токена (см. docs/access.md).
|
||
IdempotencyKey:
|
||
name: Idempotency-Key
|
||
in: header
|
||
required: false
|
||
schema:
|
||
type: string
|
||
maxLength: 256
|
||
description: Идемпотентность мутаций. Для `POST` apply и части refresh рекомендуется или обязателен по политике продукта.
|
||
Cursor:
|
||
name: cursor
|
||
in: query
|
||
schema:
|
||
type: string
|
||
description: Непрозрачный курсор из предыдущего ответа.
|
||
Limit:
|
||
name: limit
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 500
|
||
default: 50
|
||
description: Размер страницы (по умолчанию 50).
|
||
ModuleId:
|
||
name: module_id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
SpeakerId:
|
||
name: speaker_id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
RevisionId:
|
||
name: revision_id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
JobId:
|
||
name: job_id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
PeerId:
|
||
name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
DohProfileId:
|
||
name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
CommunityId:
|
||
name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
ApiKeyId:
|
||
name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
SourceId:
|
||
name: source_id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
EntryId:
|
||
name: entry_id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
RevisionA:
|
||
name: revision_a
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
RevisionB:
|
||
name: revision_b
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
ModuleTypeFilter:
|
||
name: type
|
||
in: query
|
||
schema:
|
||
$ref: "#/components/schemas/ModuleType"
|
||
ModuleEnabledFilter:
|
||
name: enabled
|
||
in: query
|
||
schema:
|
||
type: boolean
|
||
SpeakerFilter:
|
||
name: speaker_id
|
||
in: query
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
RevisionModuleFilter:
|
||
name: module_id
|
||
in: query
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
JobStatusFilter:
|
||
name: status
|
||
in: query
|
||
schema:
|
||
$ref: "#/components/schemas/JobStatus"
|
||
JobKindFilter:
|
||
name: kind
|
||
in: query
|
||
schema:
|
||
type: string
|
||
description: >
|
||
Фильтр по виду задачи; точный перечень расширяем.
|
||
Основные значения: `module_refresh`, `peer_reconcile`, `deploy_apply`, `revision_rollback`, `bird_reload`.
|
||
|
||
responses:
|
||
Unauthorized:
|
||
description: Нет или невалидный Bearer-токен.
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
BadRequest:
|
||
description: Некорректный запрос (пустой или невалидный параметр).
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
Forbidden:
|
||
description: Недостаточно прав для операции.
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
NotFound:
|
||
description: Ресурс не найден в скоупе tenant.
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
Conflict:
|
||
description: Конфликт состояния (например, удаление DoH-профиля, который ещё используется).
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
UnprocessableEntity:
|
||
description: Семантическая ошибка запроса или валидации полей.
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
DefaultProblem:
|
||
description: Ошибка (см. тело Problem).
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
|
||
schemas:
|
||
ResourceId:
|
||
type: string
|
||
description: UUID v7 или ULID.
|
||
pattern: "^[0-9A-Za-z_-]{20,36}$"
|
||
examples:
|
||
- 01JQXYZABCDEFGHIJKLMNOPQRS
|
||
|
||
Problem:
|
||
type: object
|
||
description: Problem Details (RFC 9457).
|
||
required:
|
||
- title
|
||
- status
|
||
properties:
|
||
type:
|
||
type: string
|
||
format: uri-reference
|
||
description: URI типа проблемы.
|
||
title:
|
||
type: string
|
||
status:
|
||
type: integer
|
||
detail:
|
||
type: string
|
||
instance:
|
||
type: string
|
||
format: uri-reference
|
||
errors:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/FieldError"
|
||
additionalProperties: true
|
||
|
||
FieldError:
|
||
type: object
|
||
properties:
|
||
field:
|
||
type: string
|
||
description: Имя поля (или JSON Pointer).
|
||
pointer:
|
||
type: string
|
||
description: JSON Pointer к значению.
|
||
message:
|
||
type: string
|
||
additionalProperties: true
|
||
|
||
AsyncJobAccepted:
|
||
type: object
|
||
required:
|
||
- job_id
|
||
- status
|
||
properties:
|
||
job_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
status:
|
||
type: string
|
||
enum: [queued]
|
||
description: Начальный статус задачи в очереди.
|
||
|
||
ModuleType:
|
||
type: string
|
||
enum:
|
||
- AS_PREFIXES
|
||
- CDN_CIDRS
|
||
- DOMAINS
|
||
- IP_RANGES
|
||
|
||
DohResolverPolicy:
|
||
type: string
|
||
description: |
|
||
Политика резолва доменов через DoH при нескольких профилях.
|
||
`primary_only` — только первый профиль; `failover` — по порядку до первого успешного;
|
||
`union` — объединение A/AAAA со всех профилей.
|
||
enum:
|
||
- primary_only
|
||
- failover
|
||
- union
|
||
default: primary_only
|
||
|
||
JobStatus:
|
||
type: string
|
||
description: Статус задачи; перечень может расширяться.
|
||
enum:
|
||
- queued
|
||
- running
|
||
- succeeded
|
||
- failed
|
||
- cancelled
|
||
|
||
ApplyStrategy:
|
||
type: string
|
||
description: Стратегия выбора спикеров для apply.
|
||
enum:
|
||
- all_speakers
|
||
|
||
Module:
|
||
type: object
|
||
required:
|
||
- id
|
||
- type
|
||
- name
|
||
- enabled
|
||
- priority
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
type:
|
||
$ref: "#/components/schemas/ModuleType"
|
||
name:
|
||
type: string
|
||
enabled:
|
||
type: boolean
|
||
priority:
|
||
type: integer
|
||
doh_profile_id:
|
||
type: ["string", "null"]
|
||
description: Первый DoH-профиль (legacy); предпочтительно `doh_profile_ids`.
|
||
doh_profile_ids:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
description: Упорядоченный список DoH-профилей для модулей `DOMAINS`.
|
||
doh_resolver_policy:
|
||
$ref: "#/components/schemas/DohResolverPolicy"
|
||
refresh_interval_sec:
|
||
type: ["integer", "null"]
|
||
minimum: 0
|
||
cron_expr:
|
||
type: ["string", "null"]
|
||
default_community_id:
|
||
type: ["string", "null"]
|
||
last_refreshed_at:
|
||
type: ["string", "null"]
|
||
format: date-time
|
||
description: Время последнего успешного обновления данных модуля.
|
||
additionalProperties: true
|
||
|
||
ModuleCreate:
|
||
type: object
|
||
required:
|
||
- type
|
||
- name
|
||
properties:
|
||
type:
|
||
$ref: "#/components/schemas/ModuleType"
|
||
name:
|
||
type: string
|
||
enabled:
|
||
type: boolean
|
||
default: true
|
||
priority:
|
||
type: integer
|
||
default: 0
|
||
doh_profile_id:
|
||
type: ["string", "null"]
|
||
description: Первый DoH-профиль (legacy); предпочтительно `doh_profile_ids`.
|
||
doh_profile_ids:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
description: Упорядоченный список DoH-профилей для модулей `DOMAINS`.
|
||
doh_resolver_policy:
|
||
$ref: "#/components/schemas/DohResolverPolicy"
|
||
refresh_interval_sec:
|
||
type: ["integer", "null"]
|
||
last_refreshed_at:
|
||
type: ["string", "null"]
|
||
format: date-time
|
||
description: Время последнего успешного обновления этого CDN-источника.
|
||
cron_expr:
|
||
type: ["string", "null"]
|
||
default_community_id:
|
||
type: ["string", "null"]
|
||
|
||
ModulePatch:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
enabled:
|
||
type: boolean
|
||
priority:
|
||
type: integer
|
||
doh_profile_id:
|
||
type: ["string", "null"]
|
||
description: Первый DoH-профиль (legacy); предпочтительно `doh_profile_ids`.
|
||
doh_profile_ids:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
description: Упорядоченный список DoH-профилей для модулей `DOMAINS`.
|
||
doh_resolver_policy:
|
||
$ref: "#/components/schemas/DohResolverPolicy"
|
||
refresh_interval_sec:
|
||
type: ["integer", "null"]
|
||
cron_expr:
|
||
type: ["string", "null"]
|
||
default_community_id:
|
||
type: ["string", "null"]
|
||
|
||
CdnSource:
|
||
type: object
|
||
required:
|
||
- id
|
||
- url
|
||
- source_kind
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
url:
|
||
type: string
|
||
format: uri
|
||
source_kind:
|
||
type: string
|
||
description: Формат скачанного списка / парсер.
|
||
prefix_path:
|
||
type: string
|
||
description: Путь до поля с префиксами для source_kind=json (например data.items[].cidr).
|
||
community_id:
|
||
type: ["string", "null"]
|
||
refresh_interval_sec:
|
||
type: ["integer", "null"]
|
||
|
||
CdnSourceCreate:
|
||
type: object
|
||
required:
|
||
- url
|
||
- source_kind
|
||
properties:
|
||
url:
|
||
type: string
|
||
format: uri
|
||
source_kind:
|
||
type: string
|
||
prefix_path:
|
||
type: string
|
||
community_id:
|
||
type: ["string", "null"]
|
||
|
||
CdnSourcePatch:
|
||
type: object
|
||
properties:
|
||
url:
|
||
type: string
|
||
format: uri
|
||
source_kind:
|
||
type: string
|
||
prefix_path:
|
||
type: string
|
||
community_id:
|
||
type: ["string", "null"]
|
||
refresh_interval_sec:
|
||
type: ["integer", "null"]
|
||
|
||
CdnPreviewRequest:
|
||
type: object
|
||
required: [url, source_kind]
|
||
properties:
|
||
url:
|
||
type: string
|
||
format: uri
|
||
source_kind:
|
||
type: string
|
||
prefix_path:
|
||
type: string
|
||
|
||
CdnPreviewResponse:
|
||
type: object
|
||
required: [items, total, truncated, source_url]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
type: string
|
||
total:
|
||
type: integer
|
||
truncated:
|
||
type: boolean
|
||
source_url:
|
||
type: string
|
||
|
||
AsEntry:
|
||
type: object
|
||
required:
|
||
- id
|
||
- asn
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
asn:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 4294967295
|
||
community_id:
|
||
type: ["string", "null"]
|
||
additionalProperties: true
|
||
|
||
AsEntryCreate:
|
||
type: object
|
||
required:
|
||
- asn
|
||
properties:
|
||
asn:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 4294967295
|
||
community_id:
|
||
type: ["string", "null"]
|
||
additionalProperties: true
|
||
|
||
AsEntryPatch:
|
||
type: object
|
||
properties:
|
||
asn:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 4294967295
|
||
community_id:
|
||
type: ["string", "null"]
|
||
additionalProperties: true
|
||
|
||
DomainEntry:
|
||
type: object
|
||
required:
|
||
- id
|
||
- fqdn
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
fqdn:
|
||
type: string
|
||
community_id:
|
||
type: ["string", "null"]
|
||
|
||
DomainEntryCreate:
|
||
type: object
|
||
required:
|
||
- fqdn
|
||
properties:
|
||
fqdn:
|
||
type: string
|
||
community_id:
|
||
type: ["string", "null"]
|
||
|
||
IpRangeEntry:
|
||
type: object
|
||
required:
|
||
- id
|
||
- prefix
|
||
- community_id
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
prefix:
|
||
type: string
|
||
description: IPv4/IPv6 CIDR.
|
||
example: 203.0.113.0/24
|
||
community_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
|
||
IpRangeEntryCreate:
|
||
type: object
|
||
required:
|
||
- prefix
|
||
- community_id
|
||
properties:
|
||
prefix:
|
||
type: string
|
||
community_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
|
||
DohProfile:
|
||
type: object
|
||
required:
|
||
- id
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
name:
|
||
type: string
|
||
description: Отображаемое имя профиля (опционально).
|
||
url:
|
||
type: string
|
||
format: uri
|
||
timeout_ms:
|
||
type: integer
|
||
vault_secret_ref:
|
||
type: ["string", "null"]
|
||
description: Ссылка на секрет в vault; сырое значение не отдаётся в GET.
|
||
additionalProperties: true
|
||
|
||
DohProfileCreate:
|
||
type: object
|
||
required:
|
||
- url
|
||
properties:
|
||
name:
|
||
type: string
|
||
description: Отображаемое имя профиля (опционально).
|
||
url:
|
||
type: string
|
||
format: uri
|
||
timeout_ms:
|
||
type: integer
|
||
vault_secret_ref:
|
||
type: ["string", "null"]
|
||
|
||
AuthSession:
|
||
type: object
|
||
required: [tenant_id, kind]
|
||
properties:
|
||
tenant_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
kind:
|
||
type: string
|
||
enum: [apikey, jwt]
|
||
description: apikey — Bearer API key; jwt — portal SSO token.
|
||
role:
|
||
type: string
|
||
description: >
|
||
API-key role (viewer|editor|operator|node). Empty string for portal JWT sessions.
|
||
user_id:
|
||
type: string
|
||
description: JWT sub (portal sessions only).
|
||
email:
|
||
type: string
|
||
description: JWT email claim (portal sessions only).
|
||
permissions:
|
||
type: array
|
||
items:
|
||
type: string
|
||
description: JWT permissions (bgp:*); portal sessions only.
|
||
is_admin:
|
||
type: boolean
|
||
description: Portal is_admin claim; grants all bgp permissions.
|
||
|
||
ApiKey:
|
||
type: object
|
||
required: [id, name, role, prefix, created_at, updated_at]
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
name:
|
||
type: string
|
||
role:
|
||
type: string
|
||
enum: [viewer, editor, operator, node]
|
||
prefix:
|
||
type: string
|
||
description: Первые 8 символов токена для идентификации в UI.
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
updated_at:
|
||
type: string
|
||
format: date-time
|
||
expires_at:
|
||
type: ["string", "null"]
|
||
format: date-time
|
||
revoked_at:
|
||
type: ["string", "null"]
|
||
format: date-time
|
||
last_used_at:
|
||
type: ["string", "null"]
|
||
format: date-time
|
||
additionalProperties: true
|
||
|
||
ApiKeyCreate:
|
||
type: object
|
||
required: [name, role]
|
||
properties:
|
||
name:
|
||
type: string
|
||
role:
|
||
type: string
|
||
enum: [viewer, editor, operator, node]
|
||
expires_at:
|
||
type: ["string", "null"]
|
||
format: date-time
|
||
|
||
ApiKeyPatch:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
role:
|
||
type: string
|
||
enum: [viewer, editor, operator, node]
|
||
expires_at:
|
||
type: ["string", "null"]
|
||
format: date-time
|
||
|
||
ApiKeyCreated:
|
||
allOf:
|
||
- $ref: "#/components/schemas/ApiKey"
|
||
- type: object
|
||
required: [token]
|
||
properties:
|
||
token:
|
||
type: string
|
||
description: Полный Bearer-токен; показывается один раз.
|
||
|
||
BgpCommunity:
|
||
type: object
|
||
required:
|
||
- id
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
community:
|
||
type: string
|
||
description: Техническое значение BGP community (строка для BIRD, например 65001:120 или large JSON в value_json).
|
||
title:
|
||
type: string
|
||
description: Человекочитаемое название для UI и фильтров.
|
||
additionalProperties: true
|
||
|
||
LookupQueryKind:
|
||
type: string
|
||
enum: [ip, domain]
|
||
description: Определённый тип запроса после нормализации.
|
||
|
||
LookupLayer:
|
||
type: string
|
||
enum: [entry, snapshot]
|
||
description: |
|
||
`entry` — сырые IP_RANGES / DOMAINS entries;
|
||
`snapshot` — материализованные префиксы `module_prefix_snapshot`.
|
||
|
||
LookupMatchKind:
|
||
type: string
|
||
enum: [ip_range, domain, prefix]
|
||
description: Вид совпадения (entry CIDR, entry FQDN или snapshot prefix).
|
||
|
||
LookupMatch:
|
||
type: object
|
||
required:
|
||
- layer
|
||
- module_id
|
||
- module_name
|
||
- module_type
|
||
- match_kind
|
||
- matched_value
|
||
properties:
|
||
layer:
|
||
$ref: "#/components/schemas/LookupLayer"
|
||
module_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
module_name:
|
||
type: string
|
||
module_type:
|
||
$ref: "#/components/schemas/ModuleType"
|
||
match_kind:
|
||
$ref: "#/components/schemas/LookupMatchKind"
|
||
matched_value:
|
||
type: string
|
||
description: CIDR, FQDN или prefix, с которым совпал запрос.
|
||
entry_id:
|
||
type: string
|
||
description: ID entry (только для layer=entry).
|
||
source:
|
||
type: string
|
||
description: Источник строки snapshot (ip_range, domain, as, cdn, …).
|
||
community_id:
|
||
type: ["string", "null"]
|
||
community:
|
||
type: string
|
||
description: Техническое значение BGP community.
|
||
community_title:
|
||
type: string
|
||
description: Человекочитаемое название community.
|
||
resolved_ip:
|
||
type: string
|
||
description: |
|
||
IP, полученный DNS-resolve domain-запроса, из-за которого появился этот матч.
|
||
Пусто для прямого IP-запроса и для FQDN entry/snapshot без resolve.
|
||
|
||
LookupResponse:
|
||
type: object
|
||
required:
|
||
- query
|
||
- query_kind
|
||
- normalized
|
||
- matched
|
||
- match_count
|
||
- matches
|
||
properties:
|
||
query:
|
||
type: string
|
||
description: Исходная строка запроса.
|
||
query_kind:
|
||
$ref: "#/components/schemas/LookupQueryKind"
|
||
normalized:
|
||
type: string
|
||
description: Нормализованный IP или FQDN.
|
||
matched:
|
||
type: boolean
|
||
description: true, если есть хотя бы одно совпадение.
|
||
match_count:
|
||
type: integer
|
||
minimum: 0
|
||
matches:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/LookupMatch"
|
||
resolved_ips:
|
||
type: array
|
||
description: IP-адреса после live DNS resolve (только для query_kind=domain; A/AAAA).
|
||
items:
|
||
type: string
|
||
|
||
BgpPeer:
|
||
type: object
|
||
required:
|
||
- id
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
neighbor:
|
||
type: string
|
||
description: IP соседа.
|
||
remote_asn:
|
||
type: integer
|
||
bgp_speaker_id:
|
||
type: ["string", "null"]
|
||
description: "`null` - политика для всех спикеров."
|
||
connected_speaker_id:
|
||
type: ["string", "null"]
|
||
description: >
|
||
Live (GET /v1/peers?live=1): спикер, на котором сессия Established; опрос CP birdc + agent /v1/agent/bird/protocols.
|
||
connected_speaker_label:
|
||
type: string
|
||
description: Человекочитаемая метка ноды из live-опроса.
|
||
session_on_speakers:
|
||
type: array
|
||
description: Состояние протокола пира на каждой опрошенной ноде.
|
||
items:
|
||
type: object
|
||
properties:
|
||
speaker_id:
|
||
type: string
|
||
label:
|
||
type: string
|
||
state:
|
||
type: string
|
||
established_on_speakers:
|
||
type: array
|
||
description: Ноды, где сессия в состоянии Established (один пир может быть на нескольких).
|
||
items:
|
||
type: object
|
||
properties:
|
||
speaker_id:
|
||
type: string
|
||
label:
|
||
type: string
|
||
state:
|
||
type: string
|
||
session_mismatch:
|
||
type: boolean
|
||
description: >
|
||
true если bgp_speaker_id задан, но на этой ноде нет Established
|
||
(сессия может быть на других нодах — это не ошибка для tenant-wide пиров).
|
||
policies_json:
|
||
type: string
|
||
description: >
|
||
JSON-объект (строка). Поля `local_ipv4`, `local_ipv6`, `local_asn` переопределяют
|
||
глобальные `bird_local_ipv4` / `bird_local_ipv6` / `bird_local_asn` тенанта.
|
||
Если эффективный локальный адрес или ASN пира отличается от дефолтов тенанта (`bird_local_*`),
|
||
в блок `protocol bgp … from bgp_template` добавляется строка `local … as …`.
|
||
additionalProperties: true
|
||
|
||
BgpSpeaker:
|
||
type: object
|
||
required:
|
||
- id
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
role:
|
||
type: string
|
||
description: master, replica, canary и т.д.
|
||
endpoint:
|
||
type: string
|
||
last_applied_revision_id:
|
||
type: ["string", "null"]
|
||
published_revision_id:
|
||
type: ["string", "null"]
|
||
description: Последняя опубликованная на CP ревизия для этого спикера.
|
||
published_at:
|
||
type: ["string", "null"]
|
||
format: date-time
|
||
agent_domain:
|
||
type: string
|
||
description: FQDN agent API за Traefik (Address в UI, Remnawave-style).
|
||
node_ipv4:
|
||
type: string
|
||
description: IPv4 VPS; default для bird_bgp_source_ipv4.
|
||
bird_bgp_source_ipv4:
|
||
type: string
|
||
description: Per-speaker override router id / BGP local (см. pipeline overlay).
|
||
dispatch_status:
|
||
type: string
|
||
description: ok, error, skipped — последний Panel→Node wake-up.
|
||
sync_status:
|
||
type: string
|
||
description: synced, error — состояние sync на реплике.
|
||
last_dispatch_at:
|
||
type: string
|
||
format: date-time
|
||
last_dispatch_error:
|
||
type: string
|
||
meta_json:
|
||
type: object
|
||
description: >
|
||
Расширяемый объект. Ключи agent_domain, agent_secret (только при создании),
|
||
agent_port, node_ipv4, bird_bgp_source_ipv4, bird_bgp_source_ipv6.
|
||
live:
|
||
$ref: "#/components/schemas/SpeakerLiveStatus"
|
||
description: >
|
||
При GET /v1/speakers?live=1 — runtime-статус agent и BGP-опроса на ноде.
|
||
additionalProperties: true
|
||
|
||
SpeakerLiveStatus:
|
||
type: object
|
||
description: Live runtime snapshot for one speaker (GET /v1/speakers?live=1).
|
||
properties:
|
||
label:
|
||
type: string
|
||
description: Человекочитаемая метка ноды (agent domain или CP master).
|
||
agent_ok:
|
||
type: boolean
|
||
description: true если agent /v1/agent/health успешен (master — local birdc poll).
|
||
agent_error:
|
||
type: string
|
||
agent_last_sync_at:
|
||
type: string
|
||
format: date-time
|
||
agent_last_applied_revision_id:
|
||
type: string
|
||
bgp_poll_ok:
|
||
type: boolean
|
||
description: true если birdc (CP) или GET /v1/agent/bird/protocols (replica) успешен.
|
||
bgp_poll_error:
|
||
type: string
|
||
bgp_sessions_total:
|
||
type: integer
|
||
bgp_established:
|
||
type: integer
|
||
sessions:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/BgpSessionLive"
|
||
additionalProperties: true
|
||
|
||
BgpSessionLive:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
neighbor:
|
||
type: string
|
||
neighbor_as:
|
||
type: integer
|
||
description: Remote ASN from birdc (`Neighbor AS:`).
|
||
neighbor_id:
|
||
type: string
|
||
description: BGP Identifier / Neighbor ID from birdc (`Neighbor ID:`).
|
||
state:
|
||
type: string
|
||
additionalProperties: true
|
||
|
||
LiveSpeakerPoll:
|
||
type: object
|
||
description: Метаданные опроса одной ноды в GET /v1/peers?live=1.
|
||
properties:
|
||
speaker_id:
|
||
type: string
|
||
label:
|
||
type: string
|
||
ok:
|
||
type: boolean
|
||
session_count:
|
||
type: integer
|
||
poll_error:
|
||
type: string
|
||
additionalProperties: true
|
||
|
||
PostgresOverview:
|
||
type: object
|
||
description: Instance-level PostgreSQL snapshot (GET /v1/monitoring/postgres/overview).
|
||
additionalProperties: true
|
||
|
||
PostgresQueriesResponse:
|
||
type: object
|
||
properties:
|
||
collected_at:
|
||
type: string
|
||
format: date-time
|
||
source:
|
||
type: string
|
||
enum: [live, snapshot]
|
||
items:
|
||
type: array
|
||
items:
|
||
type: object
|
||
additionalProperties: true
|
||
|
||
PostgresRecommendations:
|
||
type: object
|
||
properties:
|
||
collected_at:
|
||
type: string
|
||
format: date-time
|
||
items:
|
||
type: array
|
||
items:
|
||
type: object
|
||
properties:
|
||
severity:
|
||
type: string
|
||
code:
|
||
type: string
|
||
title:
|
||
type: string
|
||
detail:
|
||
type: string
|
||
refs:
|
||
type: array
|
||
items:
|
||
type: string
|
||
|
||
PostgresMaintenanceBody:
|
||
type: object
|
||
properties:
|
||
table:
|
||
type: string
|
||
dry_run:
|
||
type: boolean
|
||
default: false
|
||
policy:
|
||
type: string
|
||
description: Deprecated; use maintenance policies API.
|
||
limit:
|
||
type: integer
|
||
|
||
MaintenancePolicy:
|
||
type: object
|
||
required: [name, table_name, schedule, vacuum_strategy]
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
name:
|
||
type: string
|
||
table_name:
|
||
type: string
|
||
condition:
|
||
type: string
|
||
default: "true"
|
||
retention_period_sec:
|
||
type: integer
|
||
minimum: 1
|
||
max_rows:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 100000
|
||
vacuum_strategy:
|
||
type: string
|
||
enum: [none, vacuum, analyze, vacuum_analyze, reindex]
|
||
schedule:
|
||
type: string
|
||
description: Cron expression (5-field, UTC).
|
||
enabled:
|
||
type: boolean
|
||
default: true
|
||
dry_run_enabled:
|
||
type: boolean
|
||
default: false
|
||
last_run_at:
|
||
type: string
|
||
format: date-time
|
||
last_status:
|
||
type: string
|
||
last_error:
|
||
type: string
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
updated_at:
|
||
type: string
|
||
format: date-time
|
||
|
||
MaintenancePolicyPatch:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
table_name:
|
||
type: string
|
||
condition:
|
||
type: string
|
||
retention_period_sec:
|
||
type: integer
|
||
max_rows:
|
||
type: integer
|
||
vacuum_strategy:
|
||
type: string
|
||
enum: [none, vacuum, analyze, vacuum_analyze, reindex]
|
||
schedule:
|
||
type: string
|
||
enabled:
|
||
type: boolean
|
||
dry_run_enabled:
|
||
type: boolean
|
||
|
||
MaintenanceRunBody:
|
||
type: object
|
||
required: [policy_id]
|
||
properties:
|
||
policy_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
|
||
MaintenancePolicyList:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/MaintenancePolicy"
|
||
next_cursor:
|
||
type: string
|
||
has_more:
|
||
type: boolean
|
||
|
||
RuntimeLogCleanupMode:
|
||
type: string
|
||
enum: [truncate, delete]
|
||
description: |
|
||
truncate — обнулить файл (по умолчанию); delete — удалить файл с диска.
|
||
|
||
RuntimeLogFile:
|
||
type: object
|
||
required: [name, size_bytes, modified_at]
|
||
properties:
|
||
name:
|
||
type: string
|
||
description: Basename файла (*.log) в каталоге runtime-логов.
|
||
pattern: '^[a-z0-9][a-z0-9_.-]*\.log$'
|
||
size_bytes:
|
||
type: integer
|
||
format: int64
|
||
minimum: 0
|
||
modified_at:
|
||
type: string
|
||
format: date-time
|
||
|
||
RuntimeLogFileList:
|
||
type: object
|
||
required: [items]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/RuntimeLogFile"
|
||
|
||
RuntimeLogTail:
|
||
type: object
|
||
required: [filename, content, truncated, lines_returned]
|
||
properties:
|
||
filename:
|
||
type: string
|
||
content:
|
||
type: string
|
||
description: UTF-8 текст хвоста файла.
|
||
truncated:
|
||
type: boolean
|
||
description: true если применён лимит bytes/lines.
|
||
lines_returned:
|
||
type: integer
|
||
minimum: 0
|
||
|
||
RuntimeLogCleanupResult:
|
||
type: object
|
||
required: [audit_id, filename, action, size_before]
|
||
properties:
|
||
audit_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
filename:
|
||
type: string
|
||
action:
|
||
$ref: "#/components/schemas/RuntimeLogCleanupMode"
|
||
size_before:
|
||
type: integer
|
||
format: int64
|
||
size_after:
|
||
type: ["integer", "null"]
|
||
format: int64
|
||
|
||
RuntimeLogCleanupAudit:
|
||
type: object
|
||
required: [id, tenant_id, actor_prefix, filename, action, size_before, created_at]
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
tenant_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
actor_prefix:
|
||
type: string
|
||
filename:
|
||
type: string
|
||
action:
|
||
$ref: "#/components/schemas/RuntimeLogCleanupMode"
|
||
size_before:
|
||
type: integer
|
||
format: int64
|
||
size_after:
|
||
type: ["integer", "null"]
|
||
format: int64
|
||
detail:
|
||
type: object
|
||
additionalProperties: true
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
|
||
RuntimeLogCleanupAuditList:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/RuntimeLogCleanupAudit"
|
||
next_cursor:
|
||
type: string
|
||
has_more:
|
||
type: boolean
|
||
|
||
AuditSeverity:
|
||
type: string
|
||
enum: [info, warning, critical]
|
||
|
||
AuditLogEntry:
|
||
type: object
|
||
required:
|
||
[id, tenant_id, event_id, source_app, action, severity, summary, created_at]
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
tenant_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
event_id:
|
||
type: string
|
||
description: Stable id for portal ingest deduplication (prefix bgp-).
|
||
source_app:
|
||
type: string
|
||
enum: [bgp]
|
||
action:
|
||
type: string
|
||
description: Machine action key (e.g. bgp.module.create).
|
||
severity:
|
||
$ref: "#/components/schemas/AuditSeverity"
|
||
actor_user_id:
|
||
type: ["string", "null"]
|
||
actor_email:
|
||
type: ["string", "null"]
|
||
actor_name:
|
||
type: ["string", "null"]
|
||
actor_api_key_prefix:
|
||
type: ["string", "null"]
|
||
target_type:
|
||
type: ["string", "null"]
|
||
enum: [app_resource, null]
|
||
target_id:
|
||
type: ["string", "null"]
|
||
summary:
|
||
type: string
|
||
details:
|
||
type: ["object", "null"]
|
||
additionalProperties: true
|
||
ip:
|
||
type: ["string", "null"]
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
portal_pushed_at:
|
||
type: ["string", "null"]
|
||
format: date-time
|
||
|
||
AuditLogList:
|
||
type: object
|
||
required: [items]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/AuditLogEntry"
|
||
next_cursor:
|
||
type: string
|
||
has_more:
|
||
type: boolean
|
||
|
||
RuntimeLogAutoPolicy:
|
||
type: object
|
||
properties:
|
||
enabled:
|
||
type: boolean
|
||
max_file_bytes:
|
||
type: integer
|
||
format: int64
|
||
schedule:
|
||
type: string
|
||
description: UTC cron (minute hour dom month dow).
|
||
mode:
|
||
$ref: "#/components/schemas/RuntimeLogCleanupMode"
|
||
|
||
RuntimeLogAutoEstimateItem:
|
||
type: object
|
||
required: [filename, size_bytes, would_cleanup]
|
||
properties:
|
||
filename:
|
||
type: string
|
||
size_bytes:
|
||
type: integer
|
||
format: int64
|
||
would_cleanup:
|
||
type: boolean
|
||
skip_reason:
|
||
type: string
|
||
description: under_threshold, too_large, или текст ошибки.
|
||
|
||
RuntimeLogAutoEstimate:
|
||
type: object
|
||
properties:
|
||
policy:
|
||
$ref: "#/components/schemas/RuntimeLogAutoPolicy"
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/RuntimeLogAutoEstimateItem"
|
||
would_count:
|
||
type: integer
|
||
minimum: 0
|
||
|
||
RuntimeLogAutoRunResult:
|
||
type: object
|
||
additionalProperties: true
|
||
description: |
|
||
dry_run, trigger, policy, cleaned[], skipped[], cleaned_count, skipped_count.
|
||
|
||
BirdLocalStatus:
|
||
type: object
|
||
description: Статус локального BIRD на хосте API (GET /v1/bird/status).
|
||
properties:
|
||
birdc_configured:
|
||
type: boolean
|
||
message:
|
||
type: string
|
||
error:
|
||
type: string
|
||
protocols_excerpt:
|
||
type: string
|
||
bgp_sessions_total:
|
||
type: integer
|
||
bgp_established:
|
||
type: integer
|
||
healthy:
|
||
type: ["boolean", "null"]
|
||
additionalProperties: true
|
||
|
||
BundleSigningPublicKey:
|
||
type: object
|
||
required: [public_key_base64]
|
||
properties:
|
||
public_key_base64:
|
||
type: string
|
||
description: Ed25519 public key (base64) для verify-bundle на реплике.
|
||
|
||
ConfigRevision:
|
||
type: object
|
||
required:
|
||
- id
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
parent_revision_id:
|
||
type: ["string", "null"]
|
||
content_hash:
|
||
type: string
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
additionalProperties: true
|
||
|
||
RevisionPruneEstimate:
|
||
type: object
|
||
required:
|
||
- retention_minutes
|
||
- cutoff_at
|
||
- revision_count
|
||
- prefix_row_count
|
||
- orphan_snapshot_count
|
||
- bytes_estimate
|
||
properties:
|
||
retention_minutes:
|
||
type: integer
|
||
minimum: 15
|
||
maximum: 43200
|
||
cutoff_at:
|
||
type: string
|
||
format: date-time
|
||
revision_count:
|
||
type: integer
|
||
minimum: 0
|
||
prefix_row_count:
|
||
type: integer
|
||
minimum: 0
|
||
description: Строки prefix_snapshot_row в освобождаемых снимках.
|
||
orphan_snapshot_count:
|
||
type: integer
|
||
minimum: 0
|
||
bytes_estimate:
|
||
type: integer
|
||
format: int64
|
||
minimum: 0
|
||
description: Ориентировочный логический объём данных (байты).
|
||
|
||
RevisionPruneResult:
|
||
type: object
|
||
required:
|
||
- deleted_revisions
|
||
- deleted_prefix_snapshots
|
||
- deleted_prefix_rows
|
||
- bytes_estimate
|
||
properties:
|
||
deleted_revisions:
|
||
type: integer
|
||
minimum: 0
|
||
deleted_prefix_snapshots:
|
||
type: integer
|
||
minimum: 0
|
||
deleted_prefix_rows:
|
||
type: integer
|
||
minimum: 0
|
||
bytes_estimate:
|
||
type: integer
|
||
format: int64
|
||
minimum: 0
|
||
|
||
RevisionPruneRequest:
|
||
type: object
|
||
properties:
|
||
retention_minutes:
|
||
type: integer
|
||
minimum: 15
|
||
maximum: 43200
|
||
description: TTL в минутах; если не задан — из revision_retention_minutes tenant settings.
|
||
|
||
PrefixSnapshotItem:
|
||
type: object
|
||
description: >
|
||
Элемент материализованного снимка. Поле prefix обычно содержит CIDR;
|
||
для модулей AS_PREFIXES допускается ключ вида as:<номер_asn> (не CIDR).
|
||
additionalProperties: true
|
||
|
||
Job:
|
||
type: object
|
||
required:
|
||
- job_id
|
||
- kind
|
||
- status
|
||
- created_at
|
||
properties:
|
||
job_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
kind:
|
||
type: string
|
||
status:
|
||
$ref: "#/components/schemas/JobStatus"
|
||
idempotency_key:
|
||
type: ["string", "null"]
|
||
created_at:
|
||
type: string
|
||
format: date-time
|
||
started_at:
|
||
type: ["string", "null"]
|
||
description: ISO 8601 UTC, если задача уже стартовала.
|
||
finished_at:
|
||
type: ["string", "null"]
|
||
description: ISO 8601 UTC по завершении.
|
||
error:
|
||
type: ["string", "null"]
|
||
meta:
|
||
type: object
|
||
additionalProperties: true
|
||
|
||
ApplyRequest:
|
||
type: object
|
||
properties:
|
||
revision_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
strategy:
|
||
$ref: "#/components/schemas/ApplyStrategy"
|
||
dry_run:
|
||
type: boolean
|
||
default: false
|
||
|
||
VersionInfo:
|
||
type: object
|
||
properties:
|
||
version:
|
||
type: string
|
||
description: Semver сборки (git tag без префикса v).
|
||
example: "1.2.3"
|
||
api_version:
|
||
type: string
|
||
deprecated: true
|
||
description: Alias поля `version` (сохранён для обратной совместимости).
|
||
git_sha:
|
||
type: string
|
||
build_time:
|
||
type: string
|
||
format: date-time
|
||
additionalProperties: true
|
||
|
||
HealthStatus:
|
||
type: object
|
||
properties:
|
||
status:
|
||
type: string
|
||
example: ok
|
||
additionalProperties: true
|
||
|
||
ReadyStatus:
|
||
type: object
|
||
properties:
|
||
status:
|
||
type: string
|
||
example: ready
|
||
checks:
|
||
type: object
|
||
additionalProperties: true
|
||
additionalProperties: true
|
||
|
||
Settings:
|
||
type: object
|
||
description: |
|
||
KV настройки tenant/глобальные лимиты и feature flags.
|
||
Параметры BIRD (строки в `global_settings`, JSON-значения — обычно строка или число):
|
||
`bird_router_id`, `bird_local_ipv4`, `bird_local_ipv6`, `bird_local_asn`;
|
||
если задан `bird_bgp_source_ipv4`, он подставляется как BIRD `router id` (перекрывает `bird_router_id`).
|
||
Ключи `bird_bgp_source_ipv4` / `bird_bgp_source_ipv6` в сгенерированном BGP для пиров не используются (оставлены для совместимости API).
|
||
Шаблон BGP в конфиге: `local as <bird_local_asn>;` без локального IP.
|
||
properties:
|
||
bird_router_id:
|
||
type: string
|
||
description: BIRD `router id` (IPv4 dotted quad), если не задан `bird_bgp_source_ipv4`.
|
||
bird_local_ipv4:
|
||
type: string
|
||
bird_local_ipv6:
|
||
type: string
|
||
bird_local_asn:
|
||
type: integer
|
||
bird_bgp_source_ipv4:
|
||
type: string
|
||
description: |
|
||
Если задан — используется как BIRD `router id` (IPv4). В блоках `protocol bgp … from bgp_template` строка `source address` не генерируется.
|
||
bird_bgp_source_ipv6:
|
||
type: string
|
||
description: Зарезервировано; в текущей генерации BGP не используется.
|
||
revision_retention_minutes:
|
||
type: integer
|
||
minimum: 15
|
||
maximum: 43200
|
||
runtime_logs_auto_enabled:
|
||
type: boolean
|
||
description: Автоочистка *.log на evobgp-all по расписанию (только при FS volume).
|
||
runtime_logs_max_file_mb:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 512
|
||
description: Truncate/delete файлов строго больше порога (MiB).
|
||
runtime_logs_auto_schedule:
|
||
type: string
|
||
description: UTC cron для автоочистки (по умолчанию `0 */6 * * *`).
|
||
runtime_logs_auto_mode:
|
||
$ref: "#/components/schemas/RuntimeLogCleanupMode"
|
||
ui_show_quick_actions:
|
||
type: boolean
|
||
description: Показывать блок «Быстрые действия» на дашборде (UI preference).
|
||
default: true
|
||
additionalProperties: true
|
||
|
||
RevisionDiff:
|
||
type: object
|
||
description: |
|
||
Сравнение двух ревизий. Конкретный формат (JSON Patch, табличный diff и т.д.) задаётся реализацией - контракт может уточняться.
|
||
additionalProperties: true
|
||
|
||
NodeEnrollRequest:
|
||
type: object
|
||
description: Тело регистрации ноды; протокол обмена ключами уточняется отдельно.
|
||
properties:
|
||
public_key:
|
||
type: string
|
||
speaker_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
additionalProperties: true
|
||
|
||
NodeEnrollResponse:
|
||
type: object
|
||
description: Подтверждение записи enrollment (метаданные спикера обновлены).
|
||
properties:
|
||
status:
|
||
type: string
|
||
example: enrolled
|
||
speaker_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
tenant_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
additionalProperties: true
|
||
|
||
DohProfilePatch:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
url:
|
||
type: string
|
||
format: uri
|
||
timeout_ms:
|
||
type: integer
|
||
vault_secret_ref:
|
||
type: ["string", "null"]
|
||
|
||
BgpCommunityCreate:
|
||
type: object
|
||
required: [community]
|
||
properties:
|
||
community:
|
||
type: string
|
||
title:
|
||
type: string
|
||
additionalProperties: true
|
||
|
||
BgpCommunityPatch:
|
||
type: object
|
||
properties:
|
||
community:
|
||
type: string
|
||
title:
|
||
type: string
|
||
additionalProperties: true
|
||
|
||
BgpPeerDiscovery:
|
||
type: object
|
||
required: [id, neighbor, status]
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
speaker_id:
|
||
type: ["string", "null"]
|
||
neighbor_id:
|
||
type: string
|
||
description: BGP Identifier (Neighbor ID / router ID) from birdc.
|
||
neighbor:
|
||
type: string
|
||
description: Neighbor IP address.
|
||
remote_asn:
|
||
type: integer
|
||
protocol_name:
|
||
type: string
|
||
description: BIRD protocol name (evobgp_dyn_*).
|
||
session_state:
|
||
type: string
|
||
status:
|
||
type: string
|
||
enum: [pending, approved, rejected]
|
||
first_seen_at:
|
||
type: string
|
||
format: date-time
|
||
last_seen_at:
|
||
type: string
|
||
format: date-time
|
||
approved_peer_id:
|
||
type: ["string", "null"]
|
||
additionalProperties: true
|
||
|
||
BgpPeerDiscoveryApprove:
|
||
type: object
|
||
properties:
|
||
name:
|
||
type: string
|
||
bgp_speaker_id:
|
||
type: ["string", "null"]
|
||
enabled:
|
||
type: boolean
|
||
additionalProperties: false
|
||
|
||
BgpPeerCreate:
|
||
type: object
|
||
required: [neighbor, remote_asn]
|
||
properties:
|
||
neighbor:
|
||
type: string
|
||
remote_asn:
|
||
type: integer
|
||
bgp_speaker_id:
|
||
type: ["string", "null"]
|
||
additionalProperties: true
|
||
|
||
BgpPeerPatch:
|
||
type: object
|
||
properties:
|
||
neighbor:
|
||
type: string
|
||
remote_asn:
|
||
type: integer
|
||
bgp_speaker_id:
|
||
type: ["string", "null"]
|
||
additionalProperties: true
|
||
|
||
BgpSpeakerCreate:
|
||
type: object
|
||
required: [endpoint]
|
||
properties:
|
||
role:
|
||
type: string
|
||
default: replica
|
||
endpoint:
|
||
type: string
|
||
description: URL agent или https://AGENT_DOMAIN
|
||
meta_json:
|
||
type: string
|
||
description: >
|
||
JSON-объект. Ключи node_ipv4, bird_bgp_source_ipv4 (default = node_ipv4),
|
||
agent_domain, agent_secret (генерируется при создании если пуст).
|
||
additionalProperties: true
|
||
|
||
BgpSpeakerPatch:
|
||
type: object
|
||
properties:
|
||
role:
|
||
type: string
|
||
endpoint:
|
||
type: string
|
||
meta_json:
|
||
type: string
|
||
description: JSON-объект с ключами agent_domain, node_ipv4, bird_bgp_source_ipv4 и др.
|
||
additionalProperties: true
|
||
|
||
LatestRevisionPointer:
|
||
type: object
|
||
required:
|
||
- revision_id
|
||
properties:
|
||
revision_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
published_at:
|
||
type: string
|
||
format: date-time
|
||
|
||
FirewallClient:
|
||
type: object
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
name:
|
||
type: string
|
||
hostname:
|
||
type: string
|
||
token_prefix:
|
||
type: string
|
||
status:
|
||
type: string
|
||
enum: [pending, approved, revoked]
|
||
last_seen_at:
|
||
type: string
|
||
format: date-time
|
||
last_apply_at:
|
||
type: string
|
||
format: date-time
|
||
last_apply_status:
|
||
type: string
|
||
last_apply_prefix_count:
|
||
type: integer
|
||
last_apply_packets_dropped:
|
||
type: integer
|
||
format: int64
|
||
description: Cumulative packets dropped by blocklist rule (from client kernel counter).
|
||
last_apply_packets_accepted:
|
||
type: integer
|
||
format: int64
|
||
description: Cumulative packets accepted past blocklist chain (nft counter accept rule).
|
||
client_version:
|
||
type: string
|
||
|
||
FirewallInstallContext:
|
||
type: object
|
||
description: Контекст для one-liner установки firewall-клиента (только operator).
|
||
properties:
|
||
bundle_seed:
|
||
type: string
|
||
description: Значение EVOBGP_BUNDLE_SEED_HEX на control plane.
|
||
bundle_seed_configured:
|
||
type: boolean
|
||
suggested_cp_url:
|
||
type: string
|
||
format: uri
|
||
install_sh_url:
|
||
type: string
|
||
format: uri
|
||
|
||
FirewallRule:
|
||
type: object
|
||
properties:
|
||
id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
client_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
nullable: true
|
||
priority:
|
||
type: integer
|
||
action:
|
||
type: string
|
||
enum: [block, accept]
|
||
community_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
nullable: true
|
||
comment:
|
||
type: string
|
||
|
||
FirewallBlocklist:
|
||
type: object
|
||
properties:
|
||
client_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
revision_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
prefixes:
|
||
type: array
|
||
items:
|
||
type: string
|
||
total:
|
||
type: integer
|
||
hash:
|
||
type: string
|
||
|
||
paths:
|
||
/v1/health:
|
||
get:
|
||
tags: [System]
|
||
summary: Liveness
|
||
description: Проверка, что процесс API жив. Не обязана проверять БД или брокер.
|
||
operationId: getHealth
|
||
responses:
|
||
"200":
|
||
description: Процесс отвечает.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/HealthStatus"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/ready:
|
||
get:
|
||
tags: [System]
|
||
summary: Readiness
|
||
description: Готовность к трафику (БД, брокер сообщений при reference-архитектуре и т.д.).
|
||
operationId: getReady
|
||
responses:
|
||
"200":
|
||
description: Сервис готов принимать запросы.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ReadyStatus"
|
||
"503":
|
||
description: Не готов (зависимости недоступны).
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/version:
|
||
get:
|
||
tags: [System]
|
||
summary: Версия сборки (корневой путь)
|
||
description: |
|
||
Аналог `GET /v1/version`. Публичный маршрут без аутентификации.
|
||
Semver в поле `version` задаётся при сборке Docker-образов из git-тега.
|
||
operationId: getVersionRoot
|
||
responses:
|
||
"200":
|
||
description: Метаданные сборки.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/VersionInfo"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/version:
|
||
get:
|
||
tags: [System]
|
||
summary: Версия сборки
|
||
description: |
|
||
Semver control-plane и метаданные сборки (`git_sha`, `build_time`).
|
||
Дублирует `GET /version`.
|
||
operationId: getVersion
|
||
responses:
|
||
"200":
|
||
description: Метаданные сборки.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/VersionInfo"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules:
|
||
get:
|
||
tags: [Modules]
|
||
summary: Список модулей
|
||
description: Модули tenant с опциональными фильтрами по типу и флагу `enabled` (фильтры применяются сервером).
|
||
operationId: listModules
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
- $ref: "#/components/parameters/ModuleTypeFilter"
|
||
- $ref: "#/components/parameters/ModuleEnabledFilter"
|
||
responses:
|
||
"200":
|
||
description: Страница модулей.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/Module"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/lookup:
|
||
get:
|
||
tags: [Lookup]
|
||
summary: Проверка IP или домена в списках
|
||
description: |
|
||
Быстрая membership-проверка по tenant:
|
||
|
||
- **IP** — слой `entry` (`IP_RANGES`, `CIDR.Contains`) и слой `snapshot`
|
||
(все module prefix snapshots, `Prefix.Contains`);
|
||
- **Domain** — слой `entry` (нормализованный FQDN в `DOMAINS`) и слой `snapshot`
|
||
(префиксы `source=domain` у matched DOMAINS-модулей, если snapshot есть);
|
||
затем **live DNS resolve** (A/AAAA через системный резолвер) и проверка
|
||
каждого полученного IP так же, как для IP-запроса (ranges + все snapshots).
|
||
|
||
Community на матче: `entry.community_id || module.default_community_id` (entry)
|
||
или `PrefixRow.community_id` (snapshot), с join к справочнику communities.
|
||
|
||
Поля `resolved_ips` / `resolved_ip` заполняются только для domain-запросов
|
||
(после успешного DNS). Ошибка DNS не даёт 5xx: FQDN-слой всё равно возвращается.
|
||
operationId: lookupMembership
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- name: q
|
||
in: query
|
||
required: true
|
||
schema:
|
||
type: string
|
||
minLength: 1
|
||
maxLength: 253
|
||
description: IP-адрес или FQDN для проверки.
|
||
responses:
|
||
"200":
|
||
description: Результат проверки (в т.ч. matched=false при отсутствии совпадений).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/LookupResponse"
|
||
"400":
|
||
$ref: "#/components/responses/BadRequest"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/router-lists/catalog:
|
||
get:
|
||
tags: [Modules]
|
||
summary: Агрегированный каталог для router-lists-ui
|
||
description: |
|
||
Возвращает в одном ответе:
|
||
- модули типов `DOMAINS`, `IP_RANGES`, `AS_PREFIXES`;
|
||
- entries по каждому модулю;
|
||
- справочник community (`id`, `community`, `title`).
|
||
operationId: getRouterListsCatalog
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
responses:
|
||
"200":
|
||
description: Агрегированные данные для списков UI.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [modules, domains, asns, ip_ranges, communities]
|
||
properties:
|
||
modules:
|
||
type: object
|
||
required: [items]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/Module"
|
||
domains:
|
||
type: object
|
||
required: [items]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [module_id, entry]
|
||
properties:
|
||
module_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
entry:
|
||
$ref: "#/components/schemas/DomainEntry"
|
||
asns:
|
||
type: object
|
||
required: [items]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [module_id, entry]
|
||
properties:
|
||
module_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
entry:
|
||
$ref: "#/components/schemas/AsEntry"
|
||
ip_ranges:
|
||
type: object
|
||
required: [items]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [module_id, entry]
|
||
properties:
|
||
module_id:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
entry:
|
||
$ref: "#/components/schemas/IpRangeEntry"
|
||
communities:
|
||
type: object
|
||
required: [items]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/BgpCommunity"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [Modules]
|
||
summary: Создать модуль
|
||
description: |
|
||
Создаёт экземпляр модуля. Поле `type` задаёт вид (`AS_PREFIXES`, `CDN_CIDRS`, `DOMAINS`, `IP_RANGES`).
|
||
`module_id` в других путях - идентификатор экземпляра, не имя типа.
|
||
operationId: createModule
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ModuleCreate"
|
||
responses:
|
||
"201":
|
||
description: Модуль создан.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Module"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"422":
|
||
$ref: "#/components/responses/UnprocessableEntity"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules/{module_id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ModuleId"
|
||
get:
|
||
tags: [Modules]
|
||
summary: Получить модуль
|
||
operationId: getModule
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Module"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
patch:
|
||
tags: [Modules]
|
||
summary: Обновить модуль
|
||
description: Частичное обновление (расписание, DoH, приоритет, `enabled` и т.д.).
|
||
operationId: patchModule
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ModulePatch"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Module"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
"422":
|
||
$ref: "#/components/responses/UnprocessableEntity"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
delete:
|
||
tags: [Modules]
|
||
summary: Удалить модуль
|
||
description: Мягкое удаление или перевод в `enabled=false` - конкретное поведение задаётся реализацией.
|
||
operationId: deleteModule
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"204":
|
||
description: Удалено или деактивировано.
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules/{module_id}/cdn-sources:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ModuleId"
|
||
get:
|
||
tags: [Modules]
|
||
summary: Список CDN-источников
|
||
operationId: listCdnSources
|
||
parameters:
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/CdnSource"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [Modules]
|
||
summary: Добавить CDN-источник
|
||
description: URL, `source_kind`, опционально `community_id`.
|
||
operationId: createCdnSource
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CdnSourceCreate"
|
||
responses:
|
||
"201":
|
||
description: Ресурс создан.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CdnSource"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
"422":
|
||
$ref: "#/components/responses/UnprocessableEntity"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules/{module_id}/cdn-sources/{source_id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ModuleId"
|
||
- $ref: "#/components/parameters/SourceId"
|
||
patch:
|
||
tags: [Modules]
|
||
summary: Обновить CDN-источник
|
||
operationId: patchCdnSource
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CdnSourcePatch"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CdnSource"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
delete:
|
||
tags: [Modules]
|
||
summary: Удалить CDN-источник
|
||
operationId: deleteCdnSource
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"204":
|
||
description: Удалено.
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules/{module_id}/as-entries:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ModuleId"
|
||
get:
|
||
tags: [Modules]
|
||
summary: Список AS-записей
|
||
operationId: listAsEntries
|
||
parameters:
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/AsEntry"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [Modules]
|
||
summary: Добавить AS-запись
|
||
operationId: createAsEntry
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsEntryCreate"
|
||
responses:
|
||
"201":
|
||
description: Ресурс создан.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsEntry"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules/{module_id}/as-entries/{entry_id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ModuleId"
|
||
- $ref: "#/components/parameters/EntryId"
|
||
patch:
|
||
tags: [Modules]
|
||
summary: Обновить AS-запись
|
||
operationId: patchAsEntry
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsEntryPatch"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsEntry"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
delete:
|
||
tags: [Modules]
|
||
summary: Удалить AS-запись
|
||
operationId: deleteAsEntry
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"204":
|
||
description: Удалено.
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules/{module_id}/domain-entries:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ModuleId"
|
||
get:
|
||
tags: [Modules]
|
||
summary: Список доменных записей
|
||
description: FQDN и привязка к community.
|
||
operationId: listDomainEntries
|
||
parameters:
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/DomainEntry"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [Modules]
|
||
summary: Добавить доменную запись
|
||
operationId: createDomainEntry
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DomainEntryCreate"
|
||
responses:
|
||
"201":
|
||
description: Ресурс создан.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DomainEntry"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules/{module_id}/domain-entries/{entry_id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ModuleId"
|
||
- $ref: "#/components/parameters/EntryId"
|
||
patch:
|
||
tags: [Modules]
|
||
summary: Обновить доменную запись
|
||
operationId: patchDomainEntry
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DomainEntryCreate"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DomainEntry"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
delete:
|
||
tags: [Modules]
|
||
summary: Удалить доменную запись
|
||
operationId: deleteDomainEntry
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"204":
|
||
description: Удалено.
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules/{module_id}/ip-range-entries:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ModuleId"
|
||
get:
|
||
tags: [Modules]
|
||
summary: Список IP-диапазонов
|
||
description: "Только для модулей с типом IP_RANGES (статические CIDR и community_id)."
|
||
operationId: listIpRangeEntries
|
||
parameters:
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/IpRangeEntry"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [Modules]
|
||
summary: Добавить IP-диапазон
|
||
operationId: createIpRangeEntry
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/IpRangeEntryCreate"
|
||
responses:
|
||
"201":
|
||
description: Ресурс создан.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/IpRangeEntry"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules/{module_id}/ip-range-entries/{entry_id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ModuleId"
|
||
- $ref: "#/components/parameters/EntryId"
|
||
patch:
|
||
tags: [Modules]
|
||
summary: Обновить IP-диапазон
|
||
operationId: patchIpRangeEntry
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/IpRangeEntryCreate"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/IpRangeEntry"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
delete:
|
||
tags: [Modules]
|
||
summary: Удалить IP-диапазон
|
||
operationId: deleteIpRangeEntry
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"204":
|
||
description: Удалено.
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules/{module_id}/refresh:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ModuleId"
|
||
post:
|
||
tags: [Modules]
|
||
summary: Запустить ingest
|
||
description: |
|
||
Запуск обновления данных модуля (CDN / DoH / AS в зависимости от типа).
|
||
Итоговая ревизия и фрагменты BIRD строятся **по всем включённым модулям тенанта**:
|
||
обновляемый модуль пересчитывается заново, остальные — по текущему состоянию в БД/источниках,
|
||
так что префиксы ASN, CDN, IP и т.д. суммируются в одном конфиге.
|
||
Для `IP_RANGES` данные обычно только в БД: сервер может вернуть **204** (no-op) или **400**, если refresh не поддерживается - поведение фиксируется в реализации.
|
||
Рекомендуется передавать `Idempotency-Key`.
|
||
operationId: postModuleRefresh
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"202":
|
||
description: Задача поставлена в очередь.
|
||
headers:
|
||
Location:
|
||
description: URL задачи `/v1/jobs/{job_id}`
|
||
schema:
|
||
type: string
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsyncJobAccepted"
|
||
"204":
|
||
description: Нет операции (например модуль `IP_RANGES` и no-op).
|
||
"400":
|
||
description: Тип модуля не поддерживает refresh.
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules/{module_id}/entries.csv:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ModuleId"
|
||
get:
|
||
tags: [Modules]
|
||
summary: Экспорт записей модуля в CSV
|
||
description: |
|
||
Доступно для типов модулей `AS_PREFIXES`, `DOMAINS`, `IP_RANGES`.
|
||
Возвращает CSV с колонками:
|
||
- AS: `asn,community`
|
||
- Домены: `domain,community`
|
||
- IP ranges: `ipRange,community`
|
||
operationId: exportModuleEntriesCsv
|
||
responses:
|
||
"200":
|
||
description: CSV-файл записей модуля.
|
||
content:
|
||
text/csv:
|
||
schema:
|
||
type: string
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
"422":
|
||
$ref: "#/components/responses/UnprocessableEntity"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [Modules]
|
||
summary: Импорт записей модуля из CSV
|
||
description: |
|
||
Импортирует CSV в модуль типов `AS_PREFIXES`, `DOMAINS`, `IP_RANGES`.
|
||
Поддерживаемые заголовки:
|
||
- `asn,community`
|
||
- `domain,community`
|
||
- `ipRange,community`
|
||
|
||
В поле `community` можно передавать либо ID community, либо её значение.
|
||
operationId: importModuleEntriesCsv
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
text/csv:
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: Импорт завершён.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [imported, module_type]
|
||
properties:
|
||
imported:
|
||
type: integer
|
||
minimum: 0
|
||
module_type:
|
||
type: string
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
"422":
|
||
$ref: "#/components/responses/UnprocessableEntity"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/doh-profiles:
|
||
get:
|
||
tags: [DoH profiles]
|
||
summary: Список DoH-профилей
|
||
operationId: listDohProfiles
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/DohProfile"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [DoH profiles]
|
||
summary: Создать DoH-профиль
|
||
description: URL и таймауты; секрет - через vault id или отдельный вызов установки секрета.
|
||
operationId: createDohProfile
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DohProfileCreate"
|
||
responses:
|
||
"201":
|
||
description: Ресурс создан.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DohProfile"
|
||
"422":
|
||
$ref: "#/components/responses/UnprocessableEntity"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/modules/{module_id}/cdn-sources/preview:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ModuleId"
|
||
post:
|
||
tags: [Modules]
|
||
summary: Предпросмотр префиксов из CDN-источника
|
||
description: >
|
||
Синхронный запрос: conditional GET к URL (до 8 MiB тела ответа), парсинг plaintext или JSON,
|
||
возврат до 100 префиксов в `items` (полный счётчик в `total`). Выполняется в HTTP worker;
|
||
при таймауте клиента используйте короткий URL или меньший payload.
|
||
operationId: previewCdnSource
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CdnPreviewRequest"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/CdnPreviewResponse"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
"422":
|
||
$ref: "#/components/responses/UnprocessableEntity"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/doh-profiles/{id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/DohProfileId"
|
||
get:
|
||
tags: [DoH profiles]
|
||
summary: Получить DoH-профиль
|
||
description: Секрет в ответе не возвращается.
|
||
operationId: getDohProfile
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DohProfile"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
patch:
|
||
tags: [DoH profiles]
|
||
summary: Обновить DoH-профиль
|
||
operationId: patchDohProfile
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DohProfilePatch"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/DohProfile"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
delete:
|
||
tags: [DoH profiles]
|
||
summary: Удалить DoH-профиль
|
||
description: Допустимо только если профиль не используется модулями.
|
||
operationId: deleteDohProfile
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"204":
|
||
description: Удалено.
|
||
"409":
|
||
$ref: "#/components/responses/Conflict"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/communities:
|
||
get:
|
||
tags: [Communities]
|
||
summary: Список BGP community
|
||
operationId: listCommunities
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/BgpCommunity"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [Communities]
|
||
summary: Создать community
|
||
operationId: createCommunity
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpCommunityCreate"
|
||
responses:
|
||
"201":
|
||
description: Ресурс создан.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpCommunity"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/communities/{id}/prefixes:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/CommunityId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- name: limit
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
default: 500
|
||
maximum: 5000
|
||
get:
|
||
tags: [Communities]
|
||
summary: Префиксы community (latest revision per module)
|
||
description: |
|
||
Уникальные materialized-префиксы с данным community_id
|
||
из последней ревизии каждого модуля tenant.
|
||
Поле `prefixes` — плоский список для клиентов вроде EvoFirewall.
|
||
operationId: listCommunityPrefixes
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
type: object
|
||
required: [prefix]
|
||
properties:
|
||
prefix:
|
||
type: string
|
||
source:
|
||
type: string
|
||
prefixes:
|
||
type: array
|
||
items:
|
||
type: string
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/communities/{id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/CommunityId"
|
||
get:
|
||
tags: [Communities]
|
||
summary: Получить community
|
||
operationId: getCommunity
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpCommunity"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
patch:
|
||
tags: [Communities]
|
||
summary: Обновить community
|
||
operationId: patchCommunity
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpCommunityPatch"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpCommunity"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
delete:
|
||
tags: [Communities]
|
||
summary: Удалить community
|
||
description: Только при отсутствии ссылок из других сущностей.
|
||
operationId: deleteCommunity
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"204":
|
||
description: Удалено.
|
||
"409":
|
||
$ref: "#/components/responses/Conflict"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/peers:
|
||
get:
|
||
tags: [Peers]
|
||
summary: Список пиров
|
||
operationId: listPeers
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
- $ref: "#/components/parameters/SpeakerFilter"
|
||
- name: live
|
||
in: query
|
||
schema:
|
||
type: string
|
||
enum: ["1"]
|
||
description: >
|
||
Опрос birdc на CP и GET /v1/agent/bird/protocols на репликах; обогащает session_state и connected_speaker_*.
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/BgpPeer"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
live_speaker_poll:
|
||
type: array
|
||
description: >
|
||
При live=1 — результат опроса каждой ноды (CP birdc + agent protocols).
|
||
items:
|
||
$ref: "#/components/schemas/LiveSpeakerPoll"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [Peers]
|
||
summary: Создать пира
|
||
description: >
|
||
Создаёт BGP-пира и инициирует быстрый reconcile пиров (job `peer_reconcile`) без module ingest.
|
||
После reconcile автоматически запускается apply на спикеры.
|
||
operationId: createPeer
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpPeerCreate"
|
||
responses:
|
||
"201":
|
||
description: Ресурс создан.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpPeer"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/peers/discovered:
|
||
get:
|
||
tags: [Peers]
|
||
summary: Список обнаруженных (неодобренных) пиров
|
||
description: >
|
||
Dynamic BGP-сессии (`evobgp_dyn_*`), которых ещё нет в `bgp_peer`.
|
||
По умолчанию возвращает `status=pending`. При листинге выполняет live-опрос birdc/agent и upsert pending.
|
||
operationId: listDiscoveredPeers
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- name: status
|
||
in: query
|
||
schema:
|
||
type: string
|
||
enum: [pending, approved, rejected, all]
|
||
default: pending
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/BgpPeerDiscovery"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/peers/discovered/{id}/approve:
|
||
post:
|
||
tags: [Peers]
|
||
summary: Одобрить обнаруженного пира
|
||
description: >
|
||
Создаёт обычный `bgp_peer` из discovery-записи и запускает `peer_reconcile`.
|
||
operationId: approveDiscoveredPeer
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/PeerId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpPeerDiscoveryApprove"
|
||
responses:
|
||
"200":
|
||
description: Пир создан, discovery → approved.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
peer:
|
||
$ref: "#/components/schemas/BgpPeer"
|
||
discovery:
|
||
$ref: "#/components/schemas/BgpPeerDiscovery"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/peers/discovered/{id}/reject:
|
||
post:
|
||
tags: [Peers]
|
||
summary: Отклонить обнаруженного пира
|
||
description: Помечает discovery как rejected; повторно не всплывает при sync.
|
||
operationId: rejectDiscoveredPeer
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/PeerId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"200":
|
||
description: Discovery → rejected.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpPeerDiscovery"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/peers/{id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/PeerId"
|
||
get:
|
||
tags: [Peers]
|
||
summary: Получить пира
|
||
operationId: getPeer
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpPeer"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
patch:
|
||
tags: [Peers]
|
||
summary: Обновить пира
|
||
description: >
|
||
Политики (`policies_json`: `local_ipv4`, `local_ipv6`, `local_asn`), neighbor, ASN,
|
||
привязка к `bgp_speaker_id` или `null` для всех спикеров.
|
||
Изменение инициирует быстрый reconcile пиров (job `peer_reconcile`) без module ingest
|
||
и затем авто-apply на спикеры.
|
||
operationId: patchPeer
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpPeerPatch"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpPeer"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
delete:
|
||
tags: [Peers]
|
||
summary: Удалить или отключить пира
|
||
description: >
|
||
Удаление/отключение инициирует быстрый reconcile пиров (job `peer_reconcile`) без module ingest
|
||
и затем авто-apply на спикеры.
|
||
operationId: deletePeer
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"204":
|
||
description: Удалено / отключено.
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/bundle/signing-public-key:
|
||
get:
|
||
tags: [Bundles]
|
||
summary: Публичный ключ подписи бандлов
|
||
description: >
|
||
Ed25519 public key (base64) для `evobgp-node verify-bundle` / agent sync на реплике.
|
||
Роль viewer и выше.
|
||
operationId: getBundleSigningPublicKey
|
||
responses:
|
||
"200":
|
||
description: Ключ для env EVOBGP_BUNDLE_PUBKEY_BASE64 на реплике.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BundleSigningPublicKey"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/speakers:
|
||
get:
|
||
tags: [Speakers]
|
||
summary: Список спикеров
|
||
description: Master / replica, endpoint и связанные поля.
|
||
operationId: listSpeakers
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
- name: live
|
||
in: query
|
||
schema:
|
||
type: string
|
||
enum: ["1"]
|
||
description: >
|
||
Live-опрос agent /v1/agent/health и BGP protocols на репликах; CP — local birdc.
|
||
Обогащает каждый item полем `live`.
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/BgpSpeaker"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [Speakers]
|
||
summary: Зарегистрировать спикер
|
||
description: Реплика, canary и т.д.
|
||
operationId: createSpeaker
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpSpeakerCreate"
|
||
responses:
|
||
"201":
|
||
description: Ресурс создан.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpSpeaker"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/speakers/{id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/PeerId"
|
||
get:
|
||
tags: [Speakers]
|
||
summary: Получить спикер
|
||
description: В т.ч. `last_applied_revision_id`.
|
||
operationId: getSpeaker
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpSpeaker"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
patch:
|
||
tags: [Speakers]
|
||
summary: Обновить метаданные спикера
|
||
operationId: patchSpeaker
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpSpeakerPatch"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BgpSpeaker"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
delete:
|
||
tags: [Speakers]
|
||
summary: Удалить спикер
|
||
description: >
|
||
Удаляет BGP-спикер. Пиры с `bgp_speaker_id` этого спикера остаются, привязка сбрасывается (ON DELETE SET NULL).
|
||
operationId: deleteSpeaker
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"204":
|
||
description: Удалено.
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/revisions:
|
||
get:
|
||
tags: [Revisions]
|
||
summary: История ревизий
|
||
operationId: listRevisions
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
- $ref: "#/components/parameters/RevisionModuleFilter"
|
||
responses:
|
||
"200":
|
||
description: Список ревизий с cursor-пагинацией; опциональный фильтр `module_id`.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/ConfigRevision"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/revisions/prune-estimate:
|
||
get:
|
||
tags: [Revisions]
|
||
summary: Оценка очистки ревизий по retention
|
||
description: >
|
||
Считает ревизии и ориентировочный объём данных, которые будут удалены при prune
|
||
(те же правила, что applyRevisionRetention: последняя ревизия tenant и раскатанные на спикерах сохраняются).
|
||
operationId: getRevisionPruneEstimate
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- name: retention_minutes
|
||
in: query
|
||
required: false
|
||
schema:
|
||
type: integer
|
||
minimum: 15
|
||
maximum: 43200
|
||
description: TTL в минутах; если не задан — из tenant settings (default 30d).
|
||
responses:
|
||
"200":
|
||
description: Оценка.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RevisionPruneEstimate"
|
||
"422":
|
||
$ref: "#/components/responses/UnprocessableEntity"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/revisions/prune:
|
||
post:
|
||
tags: [Revisions]
|
||
summary: Очистить старые ревизии (синхронно)
|
||
description: >
|
||
Удаляет ревизии старше cutoff по retention и GC неиспользуемых prefix_snapshot.
|
||
Operator-only.
|
||
operationId: pruneRevisions
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RevisionPruneRequest"
|
||
responses:
|
||
"200":
|
||
description: Результат очистки.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RevisionPruneResult"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"422":
|
||
$ref: "#/components/responses/UnprocessableEntity"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/revisions/{revision_id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/RevisionId"
|
||
get:
|
||
tags: [Revisions]
|
||
summary: Метаданные ревизии
|
||
description: Хэш, родитель, время, артефакты.
|
||
operationId: getRevision
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ConfigRevision"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/revisions/{revision_id}/prefixes:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/RevisionId"
|
||
get:
|
||
tags: [Revisions]
|
||
summary: Снимок префиксов ревизии
|
||
operationId: getRevisionPrefixes
|
||
parameters:
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/PrefixSnapshotItem"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/revisions/{revision_id}/preview:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/RevisionId"
|
||
get:
|
||
tags: [Revisions]
|
||
summary: Превью фрагментов BIRD
|
||
description: Только чтение, без apply.
|
||
operationId: getRevisionPreview
|
||
responses:
|
||
"200":
|
||
description: Текст или структурированное представление - формат задаётся реализацией.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
additionalProperties: true
|
||
text/plain:
|
||
schema:
|
||
type: string
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/revisions/{revision_id}/rollback:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/RevisionId"
|
||
post:
|
||
tags: [Revisions]
|
||
summary: Откат через новую ревизию
|
||
description: |
|
||
Создаёт **новую** ревизию с содержимым отката от указанной. Часто возвращает **202** с `Location` на задачу.
|
||
Рекомендуется `Idempotency-Key`.
|
||
operationId: postRevisionRollback
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"202":
|
||
description: Откат поставлен в очередь.
|
||
headers:
|
||
Location:
|
||
schema:
|
||
type: string
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsyncJobAccepted"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/revisions/{revision_a}/diff/{revision_b}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/RevisionA"
|
||
- $ref: "#/components/parameters/RevisionB"
|
||
get:
|
||
tags: [Revisions]
|
||
summary: Сравнить две ревизии
|
||
description: Формат diff (JSON Patch, табличный и т.д.) определяется реализацией.
|
||
operationId: getRevisionDiff
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RevisionDiff"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/apply:
|
||
post:
|
||
tags: [Deploy]
|
||
summary: Применить конфигурацию
|
||
description: |
|
||
Применить целевую ревизию на спикерах (политика по умолчанию или из тела).
|
||
Ответ **202** с `Location` на `/v1/jobs/{job_id}`. **Рекомендуется или обязателен** `Idempotency-Key` по политике продукта.
|
||
operationId: postApply
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApplyRequest"
|
||
responses:
|
||
"202":
|
||
description: Запрос принят, задача поставлена в очередь.
|
||
headers:
|
||
Location:
|
||
schema:
|
||
type: string
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsyncJobAccepted"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/speakers/{id}/apply:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/PeerId"
|
||
post:
|
||
tags: [Deploy]
|
||
summary: Применить на одном спикере
|
||
description: Canary / точечный deploy. **202** + `Location` на job.
|
||
operationId: postSpeakerApply
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: false
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApplyRequest"
|
||
responses:
|
||
"202":
|
||
description: Запрос принят, задача поставлена в очередь.
|
||
headers:
|
||
Location:
|
||
schema:
|
||
type: string
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsyncJobAccepted"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/bird/status:
|
||
get:
|
||
tags: [Deploy]
|
||
summary: Статус локального BIRD на хосте API
|
||
description: >
|
||
Опрос birdc через EVOBGP_BIRDC_SOCKET на процессе API (обычно CP master).
|
||
На репликах без birdc на CP — birdc_configured=false.
|
||
operationId: getBirdStatus
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/BirdLocalStatus"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/bird/reload:
|
||
post:
|
||
tags: [Deploy]
|
||
summary: Мягкий reload BIRD
|
||
description: Опциональный явный reload политики, если отделён от apply; иначе может отсутствовать или быть частью apply.
|
||
operationId: postBirdReload
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"202":
|
||
description: Задача reload поставлена (если реализовано как async).
|
||
headers:
|
||
Location:
|
||
schema:
|
||
type: string
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsyncJobAccepted"
|
||
"204":
|
||
description: Reload выполнен синхронно (если так реализовано).
|
||
"501":
|
||
description: Эндпоинт не используется в данной сборке.
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/jobs:
|
||
get:
|
||
tags: [Jobs]
|
||
summary: Список задач
|
||
operationId: listJobs
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
- $ref: "#/components/parameters/JobStatusFilter"
|
||
- $ref: "#/components/parameters/JobKindFilter"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/Job"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/jobs/{job_id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/JobId"
|
||
get:
|
||
tags: [Jobs]
|
||
summary: Статус задачи
|
||
description: Прогресс, ошибка, связанные сущности в `meta`.
|
||
operationId: getJob
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Job"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/jobs/{job_id}/cancel:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/JobId"
|
||
post:
|
||
tags: [Jobs]
|
||
summary: Запросить отмену задачи
|
||
description: Best-effort; гарантии зависят от вида задачи.
|
||
operationId: postJobCancel
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"202":
|
||
description: Запрос отмены принят.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Job"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/speakers/{speaker_id}/revisions/latest:
|
||
parameters:
|
||
- $ref: "#/components/parameters/SpeakerId"
|
||
get:
|
||
tags: [Node]
|
||
summary: Последняя опубликованная ревизия для ноды
|
||
description: Вызывается **evobgp-node** с ключом роли `node`.
|
||
operationId: getSpeakerLatestRevision
|
||
security:
|
||
- bearerAuth: []
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/LatestRevisionPointer"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/speakers/{speaker_id}/bundle/{revision_id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/SpeakerId"
|
||
- $ref: "#/components/parameters/RevisionId"
|
||
get:
|
||
tags: [Node]
|
||
summary: Скачать подписанный бандл
|
||
description: |
|
||
Архив с `manifest.json` и подписью (например Ed25519). Целостность по SHA-256 в манифесте.
|
||
`Content-Type` - `application/octet-stream` или multipart; детали - в реализации.
|
||
operationId: getSpeakerBundle
|
||
security:
|
||
- bearerAuth: []
|
||
responses:
|
||
"200":
|
||
description: Бинарное тело бандла.
|
||
headers:
|
||
Content-Disposition:
|
||
schema:
|
||
type: string
|
||
content:
|
||
application/octet-stream:
|
||
schema:
|
||
type: string
|
||
format: binary
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/nodes/enroll:
|
||
post:
|
||
tags: [Node]
|
||
summary: Регистрация ноды
|
||
description: Обмен ключами и привязка к `speaker_id`; детали протокола уточняются отдельно.
|
||
operationId: postNodeEnroll
|
||
security:
|
||
- bearerAuth: []
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/NodeEnrollRequest"
|
||
responses:
|
||
"200":
|
||
description: Enrollment записан (node_public_key и время в meta спикера при наличии ключа).
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/NodeEnrollResponse"
|
||
"422":
|
||
$ref: "#/components/responses/UnprocessableEntity"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/auth/session:
|
||
get:
|
||
tags: [Auth]
|
||
summary: Текущая сессия API-ключа
|
||
operationId: getAuthSession
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AuthSession"
|
||
"401":
|
||
$ref: "#/components/responses/Unauthorized"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/api-keys:
|
||
get:
|
||
tags: [API keys]
|
||
summary: Список API-ключей tenant
|
||
description: Только роль **operator**. Секреты не возвращаются.
|
||
operationId: listApiKeys
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [items, has_more]
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/ApiKey"
|
||
next_cursor:
|
||
type: ["string", "null"]
|
||
has_more:
|
||
type: boolean
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [API keys]
|
||
summary: Создать API-ключ
|
||
operationId: createApiKey
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiKeyCreate"
|
||
responses:
|
||
"201":
|
||
description: Ключ создан; token в ответе один раз.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiKeyCreated"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"422":
|
||
$ref: "#/components/responses/UnprocessableEntity"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/api-keys/{id}:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ApiKeyId"
|
||
get:
|
||
tags: [API keys]
|
||
summary: Получить метаданные API-ключа
|
||
operationId: getApiKey
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiKey"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
patch:
|
||
tags: [API keys]
|
||
summary: Обновить API-ключ
|
||
operationId: patchApiKey
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiKeyPatch"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiKey"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
delete:
|
||
tags: [API keys]
|
||
summary: Отозвать API-ключ
|
||
operationId: revokeApiKey
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"204":
|
||
description: Отозван.
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/api-keys/{id}/rotate:
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/ApiKeyId"
|
||
post:
|
||
tags: [API keys]
|
||
summary: Ротировать секрет API-ключа
|
||
description: Выдаёт новый token; старый перестаёт работать сразу.
|
||
operationId: rotateApiKey
|
||
parameters:
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/ApiKeyCreated"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/monitoring/postgres/overview:
|
||
get:
|
||
tags: [Monitoring]
|
||
summary: PostgreSQL overview (instance-level)
|
||
operationId: getPostgresOverview
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PostgresOverview"
|
||
"503":
|
||
description: PostgreSQL backend не подключён.
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/monitoring/postgres/queries:
|
||
get:
|
||
tags: [Monitoring]
|
||
summary: Top queries (pg_stat_statements or snapshot)
|
||
operationId: getPostgresQueries
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PostgresQueriesResponse"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/monitoring/postgres/locks:
|
||
get:
|
||
tags: [Monitoring]
|
||
summary: Active locks
|
||
operationId: getPostgresLocks
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
type: object
|
||
additionalProperties: true
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/monitoring/postgres/tables:
|
||
get:
|
||
tags: [Monitoring]
|
||
summary: Table sizes and scan stats
|
||
operationId: getPostgresTables
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
type: object
|
||
additionalProperties: true
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/monitoring/postgres/recommendations:
|
||
get:
|
||
tags: [Monitoring]
|
||
summary: Heuristic optimization recommendations
|
||
operationId: getPostgresRecommendations
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PostgresRecommendations"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/monitoring/correlation:
|
||
get:
|
||
tags: [Monitoring]
|
||
summary: Timeline correlation (jobs vs cache hit)
|
||
operationId: getMonitoringCorrelation
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- name: window
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
default: 60
|
||
description: Window in minutes (max 1440).
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
additionalProperties: true
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/postgres/vacuum:
|
||
post:
|
||
tags: [Monitoring]
|
||
summary: VACUUM (async job, operator)
|
||
operationId: postPostgresVacuum
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PostgresMaintenanceBody"
|
||
responses:
|
||
"202":
|
||
description: Задача поставлена.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsyncJobAccepted"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/postgres/vacuum-analyze:
|
||
post:
|
||
tags: [Monitoring]
|
||
summary: VACUUM ANALYZE (async job, operator)
|
||
operationId: postPostgresVacuumAnalyze
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PostgresMaintenanceBody"
|
||
responses:
|
||
"202":
|
||
description: Задача поставлена.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsyncJobAccepted"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/postgres/analyze:
|
||
post:
|
||
tags: [Monitoring]
|
||
summary: ANALYZE (async job, operator)
|
||
operationId: postPostgresAnalyze
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PostgresMaintenanceBody"
|
||
responses:
|
||
"202":
|
||
description: Задача поставлена.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsyncJobAccepted"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/postgres/reindex:
|
||
post:
|
||
tags: [Monitoring]
|
||
summary: REINDEX TABLE (async job, operator)
|
||
operationId: postPostgresReindex
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PostgresMaintenanceBody"
|
||
responses:
|
||
"202":
|
||
description: Задача поставлена.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsyncJobAccepted"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/postgres/cleanup:
|
||
post:
|
||
tags: [Monitoring]
|
||
summary: Retention cleanup (async job, operator)
|
||
operationId: postPostgresCleanup
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/PostgresMaintenanceBody"
|
||
responses:
|
||
"202":
|
||
description: Задача поставлена.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsyncJobAccepted"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/postgres/maintenance/logs:
|
||
get:
|
||
tags: [Monitoring]
|
||
summary: Maintenance audit log
|
||
operationId: listPostgresMaintenanceLogs
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
type: object
|
||
additionalProperties: true
|
||
next_cursor:
|
||
type: string
|
||
has_more:
|
||
type: boolean
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/maintenance/policies:
|
||
get:
|
||
tags: [Maintenance]
|
||
summary: List maintenance policies
|
||
operationId: listMaintenancePolicies
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MaintenancePolicyList"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [Maintenance]
|
||
summary: Create maintenance policy
|
||
operationId: createMaintenancePolicy
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MaintenancePolicy"
|
||
responses:
|
||
"201":
|
||
description: Создано.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MaintenancePolicy"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/maintenance/policies/{id}:
|
||
get:
|
||
tags: [Maintenance]
|
||
summary: Get maintenance policy
|
||
operationId: getMaintenancePolicy
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MaintenancePolicy"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
patch:
|
||
tags: [Maintenance]
|
||
summary: Update maintenance policy
|
||
operationId: patchMaintenancePolicy
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MaintenancePolicyPatch"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MaintenancePolicy"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
delete:
|
||
tags: [Maintenance]
|
||
summary: Delete maintenance policy
|
||
operationId: deleteMaintenancePolicy
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
responses:
|
||
"204":
|
||
description: Удалено.
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/maintenance/policies/{id}/hints:
|
||
get:
|
||
tags: [Maintenance]
|
||
summary: PostgreSQL hints for policy table
|
||
operationId: getMaintenancePolicyHints
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
additionalProperties: true
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/maintenance/config-audit:
|
||
get:
|
||
tags: [Maintenance]
|
||
summary: Maintenance policy configuration audit log
|
||
operationId: listMaintenanceConfigAudit
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
type: object
|
||
additionalProperties: true
|
||
next_cursor:
|
||
type: string
|
||
has_more:
|
||
type: boolean
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/maintenance/run:
|
||
post:
|
||
tags: [Maintenance]
|
||
summary: Run maintenance policy (async job)
|
||
operationId: postMaintenanceRun
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MaintenanceRunBody"
|
||
responses:
|
||
"202":
|
||
description: Задача поставлена.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsyncJobAccepted"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/maintenance/dry-run:
|
||
post:
|
||
tags: [Maintenance]
|
||
summary: Dry-run maintenance policy (async job)
|
||
operationId: postMaintenanceDryRun
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/MaintenanceRunBody"
|
||
responses:
|
||
"202":
|
||
description: Задача поставлена.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AsyncJobAccepted"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/runtime-logs/files:
|
||
get:
|
||
tags: [RuntimeLogs]
|
||
summary: Список runtime log-файлов
|
||
description: |
|
||
Список *.log в EVOBGP_RUNTIME_LOGS_DIR (размер и mtime).
|
||
Требуется evobgp-all с примонтированным volume.
|
||
operationId: listRuntimeLogFiles
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RuntimeLogFileList"
|
||
"503":
|
||
description: Runtime logs недоступны (не evobgp-all или каталог не настроен).
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/runtime-logs/files/{filename}:
|
||
get:
|
||
tags: [RuntimeLogs]
|
||
summary: Хвост runtime log-файла
|
||
operationId: getRuntimeLogTail
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- name: filename
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
pattern: '^[a-z0-9][a-z0-9_.-]*\.log$'
|
||
description: Basename файла (без пути).
|
||
- name: lines
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 2000
|
||
default: 200
|
||
- name: bytes
|
||
in: query
|
||
schema:
|
||
type: integer
|
||
minimum: 1
|
||
maximum: 262144
|
||
description: Альтернатива lines; при указании обоих — более строгий лимит.
|
||
- name: grep
|
||
in: query
|
||
schema:
|
||
type: string
|
||
maxLength: 128
|
||
description: Опциональный подстрочный фильтр (после чтения хвоста).
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RuntimeLogTail"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
"503":
|
||
description: Runtime logs недоступны.
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
delete:
|
||
tags: [RuntimeLogs]
|
||
summary: Очистить runtime log-файл
|
||
description: |
|
||
Синхронная очистка (truncate по умолчанию или delete). Запись в cleanup audit.
|
||
Максимальный размер файла для очистки — 512 MiB. Только operator+.
|
||
operationId: deleteRuntimeLogFile
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- name: filename
|
||
in: path
|
||
required: true
|
||
schema:
|
||
type: string
|
||
pattern: '^[a-z0-9][a-z0-9_.-]*\.log$'
|
||
- name: mode
|
||
in: query
|
||
schema:
|
||
$ref: "#/components/schemas/RuntimeLogCleanupMode"
|
||
description: По умолчанию truncate.
|
||
responses:
|
||
"200":
|
||
description: Файл очищен.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RuntimeLogCleanupResult"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
"404":
|
||
$ref: "#/components/responses/NotFound"
|
||
"413":
|
||
description: Файл превышает лимит 512 MiB.
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
"503":
|
||
description: Runtime logs недоступны.
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/runtime-logs/auto-estimate:
|
||
get:
|
||
tags: [RuntimeLogs]
|
||
summary: Оценка автоочистки runtime log-файлов
|
||
description: |
|
||
Список файлов, которые будут затронуты текущей политикой tenant settings.
|
||
Требует evobgp-all с примонтированным каталогом runtime-logs.
|
||
operationId: estimateRuntimeLogAutoCleanup
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RuntimeLogAutoEstimate"
|
||
"503":
|
||
description: FS API недоступен (не evobgp-all или нет volume).
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/runtime-logs/auto-run:
|
||
post:
|
||
tags: [RuntimeLogs]
|
||
summary: Запустить автоочистку runtime log-файлов
|
||
description: |
|
||
Немедленный прогон политики из tenant settings. `dry_run=true` — только оценка без FS-изменений.
|
||
Записи audit с `actor_prefix=auto:scheduler`.
|
||
operationId: runRuntimeLogAutoCleanup
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- name: dry_run
|
||
in: query
|
||
schema:
|
||
type: boolean
|
||
default: false
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RuntimeLogAutoRunResult"
|
||
"503":
|
||
description: FS API недоступен.
|
||
content:
|
||
application/problem+json:
|
||
schema:
|
||
$ref: "#/components/schemas/Problem"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/runtime-logs/cleanup-audit:
|
||
get:
|
||
tags: [RuntimeLogs]
|
||
summary: Audit очистки runtime log-файлов
|
||
operationId: listRuntimeLogCleanupAudit
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/RuntimeLogCleanupAuditList"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/audit:
|
||
get:
|
||
tags: [Audit]
|
||
summary: Журнал CRUD audit tenant
|
||
description: |
|
||
Локальный журнал изменений (modules, peers, settings, API keys и т.д.).
|
||
При настроенных `AUTH_PORTAL_URL` + `AUTH_AUDIT_INGEST_SECRET` события также
|
||
отправляются в auth-portal ingest (`source_app=bgp`).
|
||
operationId: listAuditLog
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/Cursor"
|
||
- $ref: "#/components/parameters/Limit"
|
||
- name: action
|
||
in: query
|
||
schema:
|
||
type: string
|
||
description: Filter by action prefix/key (exact match).
|
||
- name: severity
|
||
in: query
|
||
schema:
|
||
$ref: "#/components/schemas/AuditSeverity"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/AuditLogList"
|
||
"400":
|
||
$ref: "#/components/responses/BadRequest"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/settings:
|
||
get:
|
||
tags: [Settings]
|
||
summary: Получить настройки
|
||
description: KV (лимиты CDN, feature flags и т.д.).
|
||
operationId: getSettings
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Settings"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
patch:
|
||
tags: [Settings]
|
||
summary: Частично обновить настройки
|
||
description: Только роль **operator**.
|
||
operationId: patchSettings
|
||
parameters:
|
||
- $ref: "#/components/parameters/TenantId"
|
||
- $ref: "#/components/parameters/IdempotencyKey"
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Settings"
|
||
responses:
|
||
"200":
|
||
description: Успешно.
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/Settings"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/firewall/install-context:
|
||
get:
|
||
tags: [Firewall]
|
||
summary: Install context for firewall one-liner (operator)
|
||
operationId: getFirewallInstallContext
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/FirewallInstallContext"
|
||
"403":
|
||
$ref: "#/components/responses/Forbidden"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/firewall/enroll:
|
||
post:
|
||
tags: [Firewall]
|
||
summary: Enroll firewall client (public, X-EvoBGP-Seed)
|
||
security: []
|
||
operationId: firewallEnroll
|
||
parameters:
|
||
- name: X-EvoBGP-Seed
|
||
in: header
|
||
required: true
|
||
schema:
|
||
type: string
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
required: [name, client_token]
|
||
properties:
|
||
name:
|
||
type: string
|
||
hostname:
|
||
type: string
|
||
client_token:
|
||
type: string
|
||
client_version:
|
||
type: string
|
||
responses:
|
||
"201":
|
||
description: Client created (pending).
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/firewall/clients:
|
||
get:
|
||
tags: [Firewall]
|
||
summary: List firewall clients
|
||
operationId: listFirewallClients
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
items:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/FirewallClient"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/firewall/clients/{id}/approve:
|
||
post:
|
||
tags: [Firewall]
|
||
summary: Approve pending client
|
||
operationId: approveFirewallClient
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Approved
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/FirewallClient"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/firewall/clients/{id}/revoke:
|
||
post:
|
||
tags: [Firewall]
|
||
summary: Reject pending or revoke approved client
|
||
operationId: revokeFirewallClient
|
||
parameters:
|
||
- name: id
|
||
in: path
|
||
required: true
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: Revoked
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
status:
|
||
type: string
|
||
enum: [revoked]
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/firewall/rules:
|
||
get:
|
||
tags: [Firewall]
|
||
summary: List firewall rules
|
||
operationId: listFirewallRules
|
||
parameters:
|
||
- name: scope
|
||
in: query
|
||
schema:
|
||
type: string
|
||
enum: [tenant, client]
|
||
- name: client_id
|
||
in: query
|
||
schema:
|
||
$ref: "#/components/schemas/ResourceId"
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
post:
|
||
tags: [Firewall]
|
||
summary: Create firewall rule
|
||
operationId: createFirewallRule
|
||
responses:
|
||
"201":
|
||
description: Created
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/firewall/blocklist:
|
||
get:
|
||
tags: [Firewall]
|
||
summary: Get evaluated blocklist (firewall client token)
|
||
operationId: getFirewallBlocklist
|
||
responses:
|
||
"200":
|
||
description: Blocklist
|
||
content:
|
||
application/json:
|
||
schema:
|
||
$ref: "#/components/schemas/FirewallBlocklist"
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|
||
|
||
/v1/firewall/apply-report:
|
||
post:
|
||
tags: [Firewall]
|
||
summary: Report last apply status
|
||
operationId: firewallApplyReport
|
||
requestBody:
|
||
required: true
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
status:
|
||
type: string
|
||
error:
|
||
type: string
|
||
prefix_count:
|
||
type: integer
|
||
ip_count:
|
||
type: integer
|
||
packets_dropped:
|
||
type: integer
|
||
format: int64
|
||
packets_accepted:
|
||
type: integer
|
||
format: int64
|
||
kernel_method:
|
||
type: string
|
||
source:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: OK
|
||
default:
|
||
$ref: "#/components/responses/DefaultProblem"
|