271 lines
14 KiB
Markdown
271 lines
14 KiB
Markdown
# EvoBGP - наброски HTTP API
|
||
|
||
**Статус:** черновик для согласования; не спецификация реализации. Базовый префикс: **`/v1`**. Модель данных и термины - в архитектурном плане (модули, `bgp_speaker`, `config_revision`, `job_audit`).
|
||
|
||
---
|
||
|
||
## 1. Общие соглашения
|
||
|
||
| Тема | Решение (набросок) |
|
||
|------|---------------------|
|
||
| **Аутентификация** | Заголовок `Authorization: Bearer <api_key>` или mTLS на edge; ключи привязаны к tenant и роли. |
|
||
| **Multi-tenant** | Все сущности в скоупе tenant: либо из ключа, либо явный префикс `X-Tenant-Id` (только для супер-ролей). |
|
||
| **Идентификаторы** | UUID v7 или ULID в URL; в JSON - строки. |
|
||
| **Время** | ISO 8601 UTC (`2026-04-03T12:00:00Z`). |
|
||
| **Ошибки** | Тело `application/problem+json` (RFC 9457): `type`, `title`, `status`, `detail`, `instance`, опционально `errors[]` по полям. |
|
||
| **Идемпотентность** | Для мутаций, создающих задачи или побочные эффекты: заголовок `Idempotency-Key` (опционально обязателен для `POST` apply/refresh). |
|
||
| **Пагинация** | `?cursor=<opaque>&limit=50` (cursor-based); ответ: `items`, `next_cursor`, `has_more`. |
|
||
| **Асинхронные операции** | `202 Accepted`, заголовок `Location: /v1/jobs/{job_id}`; тело `{ "job_id", "status": "queued" }`. |
|
||
| **Версионирование** | Несовместимые изменения - новый префикс `/v2`. |
|
||
|
||
---
|
||
|
||
## 2. Системные и служебные
|
||
|
||
| Метод | Путь | Назначение |
|
||
|-------|------|------------|
|
||
| `GET` | `/v1/health` | Liveness (процесс жив). |
|
||
| `GET` | `/v1/ready` | Readiness (БД, брокер при reference, и т.д.). |
|
||
| `GET` | `/v1/version` | Версия сборки API и control-plane (`git_sha`, `build_time`). |
|
||
|
||
---
|
||
|
||
## 3. Модули префиксов (`module`)
|
||
|
||
### Связь с продуктом
|
||
|
||
**ASN / CDN / DOMAINS / IP-диапазоны** в продуктовой формулировке - это **четыре вида модулей**. Поле **`type`** в `POST /v1/modules` выбирает вид: `AS_PREFIXES`, `CDN_CIDRS`, `DOMAINS` или **`IP_RANGES`**. Дочерние ресурсы: **`as-entries`**, **`cdn-sources`**, **`domain-entries`** - как раньше; **`ip-range-entries`** - статические **CIDR + `community_id`** (без ASN и без URL). **`module_id`** в пути - идентификатор **конкретного экземпляра** модуля, а не имя типа.
|
||
|
||
Типы: `AS_PREFIXES`, `CDN_CIDRS`, `DOMAINS`, `IP_RANGES` (как в плане).
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/modules` | Список модулей tenant (фильтры: `?type=`, `?enabled=`). |
|
||
| `POST` | `/v1/modules` | Создать модуль. |
|
||
| `GET` | `/v1/modules/{module_id}` | Детали модуля. |
|
||
| `PATCH` | `/v1/modules/{module_id}` | Частичное обновление (расписание, DoH, приоритет, `enabled`). |
|
||
| `DELETE` | `/v1/modules/{module_id}` | Мягкое удаление или `enabled=false` - зафиксировать в плане реализации. |
|
||
|
||
**CDN-источники модуля**
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/modules/{module_id}/cdn-sources` | Список строк CDN. |
|
||
| `POST` | `/v1/modules/{module_id}/cdn-sources` | Добавить источник (URL + `source_kind` + опционально `community_id`). |
|
||
| `PATCH` | `/v1/modules/{module_id}/cdn-sources/{source_id}` | URL, `source_kind`, `community_id`, свой `refresh_interval_sec`. |
|
||
| `DELETE` | `/v1/modules/{module_id}/cdn-sources/{source_id}` | Удалить. |
|
||
|
||
**Записи AS / домены**
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/modules/{module_id}/as-entries` | Список ASN/префиксов. |
|
||
| `POST` | `/v1/modules/{module_id}/as-entries` | Добавить. |
|
||
| `PATCH` | `/v1/modules/{module_id}/as-entries/{entry_id}` | Обновить. |
|
||
| `DELETE` | `/v1/modules/{module_id}/as-entries/{entry_id}` | Удалить. |
|
||
| `GET` | `/v1/modules/{module_id}/domain-entries` | FQDN + community. |
|
||
| `POST` | `/v1/modules/{module_id}/domain-entries` | Добавить. |
|
||
| `PATCH` | `/v1/modules/{module_id}/domain-entries/{entry_id}` | Обновить. |
|
||
| `DELETE` | `/v1/modules/{module_id}/domain-entries/{entry_id}` | Удалить. |
|
||
|
||
**Записи IP-диапазонов** (только для `type: IP_RANGES`)
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/modules/{module_id}/ip-range-entries` | Список CIDR + `community_id`. |
|
||
| `POST` | `/v1/modules/{module_id}/ip-range-entries` | Добавить запись (`prefix`, `community_id`). |
|
||
| `PATCH` | `/v1/modules/{module_id}/ip-range-entries/{entry_id}` | Обновить. |
|
||
| `DELETE` | `/v1/modules/{module_id}/ip-range-entries/{entry_id}` | Удалить. |
|
||
|
||
**Refresh (ingest)**
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `POST` | `/v1/modules/{module_id}/refresh` | Запуск ingest для модуля (CDN / DoH / AS по типу). Для **`IP_RANGES`** обычно **не требуется** (данные только в БД); возможен **`204`** / no-op или отказ **`400`**, если тип не поддерживает refresh - зафиксировать в реализации. |
|
||
|
||
**Пример тела создания модуля (набросок)**
|
||
|
||
```json
|
||
{
|
||
"type": "CDN_CIDRS",
|
||
"name": "edge-v4",
|
||
"enabled": true,
|
||
"priority": 10,
|
||
"doh_profile_id": null,
|
||
"refresh_interval_sec": 3600,
|
||
"cron_expr": null,
|
||
"default_community_id": "550e8400-e29b-41d4-a716-446655440000"
|
||
}
|
||
```
|
||
|
||
Пример модуля **`IP_RANGES`** (набросок): `type: "IP_RANGES"`, `doh_profile_id: null`, далее строки через `ip-range-entries` с полями `prefix` (например `203.0.113.0/24`) и `community_id`.
|
||
|
||
---
|
||
|
||
## 4. DoH-профили (`doh_profile`)
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/doh-profiles` | Список. |
|
||
| `POST` | `/v1/doh-profiles` | Создать (URL, таймауты; секрет - ссылка на vault id или отдельный `POST .../secret`). |
|
||
| `GET` | `/v1/doh-profiles/{id}` | Детали (без раскрытия секрета). |
|
||
| `PATCH` | `/v1/doh-profiles/{id}` | Обновить. |
|
||
| `DELETE` | `/v1/doh-profiles/{id}` | Удалить, если не используется модулями. |
|
||
|
||
---
|
||
|
||
## 5. BGP community (`bgp_community`)
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/communities` | Список справочника. |
|
||
| `POST` | `/v1/communities` | Создать. |
|
||
| `GET` | `/v1/communities/{id}` | Детали. |
|
||
| `PATCH` | `/v1/communities/{id}` | Обновить. |
|
||
| `DELETE` | `/v1/communities/{id}` | Удалить при отсутствии ссылок. |
|
||
|
||
---
|
||
|
||
## 6. Пиры (`bgp_peer`)
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/peers` | Список (`?speaker_id=`, пагинация). |
|
||
| `POST` | `/v1/peers` | Создать пира. |
|
||
| `GET` | `/v1/peers/{id}` | Детали. |
|
||
| `PATCH` | `/v1/peers/{id}` | Политики, neighbor, ASN, привязка к `bgp_speaker_id` или `null` = все спикеры. |
|
||
| `DELETE` | `/v1/peers/{id}` | Удалить / отключить. |
|
||
|
||
---
|
||
|
||
## 7. Спикеры BIRD (`bgp_speaker`)
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/speakers` | Список (master / replica, endpoint). |
|
||
| `POST` | `/v1/speakers` | Зарегистрировать спикер (реплика, canary). |
|
||
| `GET` | `/v1/speakers/{id}` | Детали + `last_applied_revision_id`. |
|
||
| `PATCH` | `/v1/speakers/{id}` | Метаданные, endpoint. |
|
||
|
||
---
|
||
|
||
## 8. Ревизии конфигурации (`config_revision`)
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/revisions` | История ревизий (`?module_id=`, `?limit=`). |
|
||
| `GET` | `/v1/revisions/{revision_id}` | Метаданные: хэш, родитель, время, артефакты. |
|
||
| `GET` | `/v1/revisions/{revision_id}/prefixes` | Материализованный снимок префиксов (пагинация). |
|
||
| `GET` | `/v1/revisions/{revision_id}/preview` | Превью фрагментов BIRD (read-only, без apply). |
|
||
| `POST` | `/v1/revisions/{revision_id}/rollback` | Создать **новую** ревизию с содержимым отката; часто **`202`**. |
|
||
|
||
**Сравнение ревизий (набросок)**
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/revisions/{a}/diff/{b}` | Diff префиксов / метаданных (формат - зафиксировать: JSON patch или табличный). |
|
||
|
||
---
|
||
|
||
## 9. Применение конфигурации (deploy / BIRD)
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `POST` | `/v1/apply` | Применить текущую целевую ревизию на всех спикерах (или по политике по умолчанию). **`202`**. |
|
||
| `POST` | `/v1/speakers/{id}/apply` | Применить на одном спикере (canary). **`202`**. |
|
||
| `POST` | `/v1/bird/reload` | Опционально: явный мягкий reload политики (если отделён от apply); иначе часть `apply`. |
|
||
|
||
**Тело `POST /v1/apply` (набросок, опционально)**
|
||
|
||
```json
|
||
{
|
||
"revision_id": "01JQXYZ...",
|
||
"strategy": "all_speakers",
|
||
"dry_run": false
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 10. Задачи (`job_audit`)
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/jobs` | Список задач (`?status=`, `?kind=`, cursor). |
|
||
| `GET` | `/v1/jobs/{job_id}` | Статус, прогресс, ошибка, связанные сущности. |
|
||
| `POST` | `/v1/jobs/{job_id}/cancel` | Запрос отмены (best-effort). |
|
||
|
||
**Пример ответа `GET /v1/jobs/{id}`**
|
||
|
||
```json
|
||
{
|
||
"job_id": "01JQXYZ...",
|
||
"kind": "module_refresh",
|
||
"status": "running",
|
||
"idempotency_key": "client-abc-123",
|
||
"created_at": "2026-04-03T10:00:00Z",
|
||
"started_at": "2026-04-03T10:00:01Z",
|
||
"finished_at": null,
|
||
"error": null,
|
||
"meta": { "module_id": "01JQM..." }
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 11. Реплики (`evobgp-node`): бандлы
|
||
|
||
Вызываются **нодой** с отдельным ключом / mTLS (роль `node`).
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/speakers/{speaker_id}/revisions/latest` | Указатель на последнюю опубликованную ревизию для ноды. |
|
||
| `GET` | `/v1/speakers/{speaker_id}/bundle/{revision_id}` | Скачивание подписанного бандла (архив + `manifest.json` + подпись). |
|
||
| `POST` | `/v1/nodes/enroll` | Регистрация ноды (обмен ключами, привязка к `speaker_id`) - детали протокола отдельно. |
|
||
|
||
Заголовки для бандла: `Content-Type: application/octet-stream` или multipart; контроль целостности по `manifest` (SHA-256) и подписи (например Ed25519).
|
||
|
||
---
|
||
|
||
## 12. Глобальные настройки и операторские флаги (опционально)
|
||
|
||
| Метод | Путь | Описание |
|
||
|-------|------|----------|
|
||
| `GET` | `/v1/settings` | KV вроде `global_settings` (лимиты CDN, feature flags). |
|
||
| `PATCH` | `/v1/settings` | Частичное обновление (только роль operator). |
|
||
|
||
---
|
||
|
||
## 13. Набросок матрицы прав (роли)
|
||
|
||
| Ресурс | `viewer` | `editor` | `operator` | `node` |
|
||
|--------|----------|----------|------------|--------|
|
||
| GET модули, ревизии, peers | да | да | да | нет* |
|
||
| PATCH модули, peers | нет | да | да | нет |
|
||
| apply, rollback | нет | нет | да | нет |
|
||
| bundle / enroll | нет | нет | нет | да |
|
||
|
||
\*Нода не ходит в общий CRUD; только §11.
|
||
|
||
---
|
||
|
||
## 14. Что вынести в следующую итерацию
|
||
|
||
- Полная **OpenAPI 3.1** схема (`openapi.yaml`) из этого документа.
|
||
- Webhooks: `POST` на URL клиента по завершении `job` (опционально).
|
||
- SSE/WebSocket для стрима статуса долгих jobs.
|
||
- Rate limits по ключу и по tenant в ответах (`RateLimit-*` заголовки).
|
||
|
||
---
|
||
|
||
## 15. Связь с архитектурным планом
|
||
|
||
| Тема плана | Раздел здесь |
|
||
|------------|----------------|
|
||
| REST, jobs | §1, §10 |
|
||
| refresh, apply, rollback, preview, `IP_RANGES` | §3, §8, §9 |
|
||
| peers, speakers, communities, DoH | §4 - §7 |
|
||
| bundle API, нода | §11 |
|
||
|
||
Файл плана: `.cursor/plans/evobgp_архитектура_0e73ef02.plan.md` (§7 REST API).
|