commit db62c8c0c726e5a3b41831930193ebe8e6365914 Author: Vassiliy Yegorov Date: Fri Jul 3 12:07:13 2026 +0700 docs: дизайн DNS Autoresolver (Фаза 1 — ядро) Co-Authored-By: Claude Opus 4.8 (1M context) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d93cd6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# deps & build +/node_modules/ +/bin/ +*.env +.env diff --git a/docs/superpowers/specs/2026-07-03-dns-autoresolver-design.md b/docs/superpowers/specs/2026-07-03-dns-autoresolver-design.md new file mode 100644 index 0000000..30bf599 --- /dev/null +++ b/docs/superpowers/specs/2026-07-03-dns-autoresolver-design.md @@ -0,0 +1,153 @@ +# DNS Autoresolver — дизайн + +**Дата:** 2026-07-03 +**Статус:** черновик на ревью + +## Контекст и цель + +Утилита для автонастройки и проверки DNS-зон у внешних провайдеров. Пользователь заранее +готовит шаблоны базовых настроек зоны. Утилита подключается к учётной записи провайдера, +получает список доменов (их может быть несколько), сверяет текущие настройки каждого домена +с привязанным шаблоном, подсвечивает отклонения (diff) и предлагает привести зону в соответствие +после ручного подтверждения. + +Провайдеры расширяемы; первый — **Selectel** (DNS API v2 "actual", +https://docs.selectel.ru/api/dns-actual/). + +Проектируется как **мультитенантный веб-сервис**: UI/UX-сайт + Go API, авторизация, +разделение по пользователям и их проектам, в перспективе — периодические проверки, +уведомления и метрики для алертов. + +## Общая архитектура + +``` + ┌─────────────────┐ + React SPA ◄────►│ Go API (REST) │ + ├─────────────────┤ + │ Domain core │ диф-движок шаблон↔зона (чистые функции) + │ Provider layer │ интерфейс Provider (Selectel — первый) + │ Repository │ PostgreSQL, multi-tenant + └────────┬────────┘ + │ + ┌────────▼────────┐ + │ PostgreSQL │ users/projects/provider_accounts/ + │ │ templates/domains/check_runs + └─────────────────┘ +``` + +Каждый модуль имеет одну ответственность и общается через явные интерфейсы: + +- **Provider layer** — единственный, кто знает про конкретный API провайдера. Мапит его + ответы в нейтральную модель `Record`. Домен-ядро не знает про Selectel. +- **Domain core** — диф-движок: чистые функции без сайд-эффектов, тестируются изолированно. +- **Repository** — доступ к PostgreSQL, все запросы размечены `user_id`/`project_id`. +- **Go API** — REST/JSON, транспорт; оркестрирует provider + core + repository. +- **React SPA** — весь UI; общается только через REST API. + +## Ключевая абстракция — Provider + +Точка расширяемости. Новый провайдер = новая реализация интерфейса + маппинг его API +в нейтральную модель. + +```go +type Provider interface { + Name() string + ListZones(ctx context.Context, creds Credentials) ([]Zone, error) + GetRecords(ctx context.Context, creds Credentials, zoneID string) ([]Record, error) + ApplyChanges(ctx context.Context, creds Credentials, zoneID string, cs Changeset) error +} +``` + +Нейтральная модель: + +```go +type Record struct { + Type string // A, AAAA, CNAME, MX, TXT, SRV, NS, SOA + Name string // относительное или FQDN имя + Values []string // значения (RRset) + TTL int + Priority *int // MX/SRV + // SRV: разбирается из Values (priority weight port target) +} +``` + +## Диф-движок + +Чистая функция без сайд-эффектов: + +```go +func Diff(template []Record, actual []Record) Changeset +// Changeset{ ToAdd, ToUpdate, ToDelete, InSync []RecordDiff } +``` + +- Сравнение по ключу `(Type, Name)`, затем по нормализованным значениям/TTL. +- Каждое отклонение помечается для подсветки в UI/API. +- Применение (`ApplyChanges`) — **только после ручного подтверждения**, гранулярно + по домену/записи. + +## Управляемые типы записей + +| Тип | Режим | Комментарий | +|---|---|---| +| A, AAAA, CNAME, MX, TXT | Управляемые (diff + apply) | ядро шаблонов | +| SRV | Управляемые (diff + apply) | почтовый автодискавери: `_autodiscover._tcp`, `_submission._tcp`, `_imaps._tcp`, `_pop3s._tcp` и т.п. — задаются в шаблонах | +| NS, SOA | Read-only | отклонения показываются, но не применяются автоматически | + +## Модель данных (multi-tenant с первого дня) + +``` +users + └─ projects + ├─ provider_accounts (provider, зашифрованные креды) + ├─ templates (набор Record, versioned) + └─ domains (zone, provider_account_id, template_id) + └─ check_runs (история сверок: результат diff, timestamp) +``` + +- Креды провайдера **шифруются** в БД (например, AES-GCM с ключом из env/секрет-менеджера). +- Даже в Фазе 1 без логина строки размечены `user_id`/`project_id` (единый системный + владелец) — авторизация «включается» в Фазе 2 без миграции данных. +- Шаблоны версионируются, чтобы `check_runs` ссылались на конкретную версию. + +## Фазы + +Проект большой — режется на подсистемы, каждая со своим spec → план → реализация. + +- **Фаза 1 (MVP-ядро):** Provider-интерфейс + реализация Selectel, диф-движок, + PostgreSQL-схема и repository, REST API, ручной apply. Мультитенантная схема, + но единый владелец (без регистрации). React-UI тонкий; ядро покрыто тестами. +- **Фаза 2:** Авторизация (регистрация/логин, сессии/JWT), полноценный React UI + с визуальным дифом и управлением шаблонами/учётками. +- **Фаза 3:** Периодические проверки по расписанию, уведомления, метрики для алертов. + +## Обработка ошибок + +- Provider layer оборачивает ошибки API (таймауты, 4xx/5xx, rate limit) в типизированные + ошибки; retries с backoff для идемпотентных операций. +- `ApplyChanges` не должен оставлять зону в промежуточном состоянии: применять changeset + атомарно, где API позволяет; иначе — фиксировать частичный результат в `check_runs`. +- API возвращает структурированные ошибки (code + message) для UI. + +## Тестирование + +- **Диф-движок** — модульные тесты на все ветки (add/update/delete/in-sync, SRV, NS/SOA RO). +- **Provider Selectel** — тесты против замоканного HTTP (записанные ответы API). +- **Repository** — интеграционные тесты на PostgreSQL (testcontainers или локальная БД). +- **API** — тесты хендлеров с мок-провайдером. + +## Открытые вопросы (не блокируют Фазу 1) + +1. Аутентификация в Selectel: статический API-ключ vs Keystone-токен проекта. Уточнить + по докам (context7 / Selectel DNS "actual") при реализации. +2. Формат хранения шаблонов в БД: JSONB-набор записей vs нормализованные таблицы записей. +3. Механизм шифрования кредов: ключ из env vs внешний секрет-менеджер. + +## Решённые развилки + +- Язык: **Go** (backend), **React SPA** (frontend). +- БД: **PostgreSQL**. +- Применение: **diff + ручное подтверждение**. +- Хранение: всё в БД, разделение по пользователям и проектам. +- Типы: A/AAAA/CNAME/MX/TXT/SRV управляемые, NS/SOA read-only. +- Git: репозиторий инициализирован. +- Scope Фазы 1: надёжное API + диф-движок, минимальный UI.