vasyanskandClaude Opus 5 dcaf3e65f5 feat(web): clone existing templates
Add a "Clone" action to the templates table: it fills the create form
with a copy of the selected template's name and records instead of
creating the copy right away, so the operator can adjust the name or
records before saving. No new API — the existing POST /templates is
reused.

Cloning resets editingId so the submit goes to createTemplate even when
edit mode was active, and the copy name is picked to be free among the
listed templates ("X (копия)", then "X (копия 2)"). Template names have
no unique constraint in the DB, so this is a UX hint, not a guarantee.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Yr8frsaxBgab1Aa7yfPuU
2026-08-19 17:17:34 +07:00
2026-08-19 17:17:34 +07:00
2026-07-05 17:01:33 +07:00
2026-07-05 17:01:33 +07:00
2026-07-05 17:01:33 +07:00

DNS Autoresolver

Утилита автонастройки и проверки DNS-зон: multi-tenant сервис, который сверяет фактическое состояние зоны у провайдера (Selectel DNS API v2) с шаблоном записей, показывает диф и применяет изменения только вручную — никакого автоматического apply без подтверждения оператора.

Возможности

  • Multi-tenant: проекты, аккаунты провайдера, домены — с авторизацией (регистрация/логин, сессии); всё изолировано по проекту.
  • Провайдер Selectel (Cloud DNS v2): авторизация через project IAM-токен сервисного пользователя (не статический API-ключ — см. ниже), чтение зон/RRSet, импорт зон, диф против шаблона, ручной apply.
  • Шаблоны записей с плейсхолдером {{domain_name}}: один шаблон переиспользуется на многих доменах — при проверке подставляется имя зоны. Шаблон можно завести вручную или снять снимком с существующей зоны («создать шаблон из зоны», с авто-параметризацией имени домена).
  • Просмотр зоны без шаблона: текущие записи зоны видны даже до привязки шаблона; статус домена без шаблона — «без шаблона», а не unknown.
  • Диф + выборочный ручной apply: чекбоксы на каждой записи (updates и prunes), удаления по умолчанию сняты (opt-in). Удаления применяются перед обновлениями — иначе провайдер отвергает конфликт (например CNAME на имени, где ещё жива A-запись). При ошибке показывается реальный ответ провайдера, а не generic-текст.
  • Расписание проверок: планировщик периодически гоняет read-only check+notify (без Apply), пишет историю проверок и статус drift.
  • Уведомления: каналы Telegram и Webhook, per-channel статус доставки.
  • Метрики: Prometheus /metrics (публичный, без auth, только агрегаты).
  • Health-check: /healthz — liveness-проба, используется как Docker HEALTHCHECK через встроенный CLI-режим app -healthcheck.

Учётные данные Selectel

Cloud DNS v2 требует project IAM-токен, а не статический API-ключ. При добавлении аккаунта Selectel в UI указываются данные сервисного пользователя:

  • имя сервисного пользователя,
  • пароль,
  • номер аккаунта (account_id, вида 123456),
  • имя проекта.

Сервисный пользователь создаётся в панели Selectel (раздел Пользователи и роли) и ему выдаётся роль на нужный проект. Приложение само обменивает эти данные на 24-часовой IAM-токен (Identity API cloud.api.selcloud.ru) и кэширует его; данные хранятся зашифрованными (AES-256-GCM), пароль не логируется. Учётные данные проверяются пробным логином прямо при добавлении аккаунта.

Рабочий процесс

  1. Зарегистрироваться (self-registration, автоматически создаётся личный проект).
  2. Добавить аккаунт Selectel (данные сервисного пользователя, см. выше).
  3. Импортировать зоны аккаунта — на каждую зону заводится домен.
  4. Привязать шаблон: создать снимком из зоны или собрать вручную с плейсхолдерами {{domain_name}}; без шаблона доступен только просмотр записей.
  5. Открыть диф домена, отметить нужные изменения/удаления, применить.

Стек

Go 1.26 (statically-linked бинарь, SPA встроена через embed), React + Vite (SPA), PostgreSQL 17, Prometheus client, distroless/static-debian12 рантайм-образ.

Запуск в Docker

Требуется Docker Engine + Docker Compose v2.

  1. Скопировать пример конфигурации:

    cp .env.example .env
    
  2. Сгенерировать ключ шифрования секретов (провайдеров/каналов) — ровно 32 байта в base64 — и вписать его в .env как DNS_AR_ENC_KEY:

    openssl rand -base64 32
    

    Также задать POSTGRES_PASSWORD (без дефолта — сервис не поднимется без явного пароля).

  3. Поднять стек (postgres + app), сборка образа приложения — на лету:

    docker compose up -d --build
    # или: make docker-up
    

    app стартует только после того, как postgres станет healthy; миграции схемы БД приложение накатывает само при старте.

  4. Открыть UI: http://localhost:8080

Остановить стек: docker compose down (или make docker-down). Логи приложения: docker compose logs -f app (или make docker-logs).

Переменные окружения (.env)

Переменная Назначение По умолчанию
POSTGRES_USER пользователь PostgreSQL dnsar
POSTGRES_PASSWORD пароль PostgreSQL — обязателен, без дефолта
POSTGRES_DB имя БД dnsar
APP_PORT порт публикации приложения на хосте 8080
DNS_AR_ENC_KEY ключ шифрования секретов, base64 → ровно 32 байта — обязателен

Секреты передаются только через переменные окружения, никогда — в образ, логи или git.

Локальная разработка (без Docker)

make build       # go build ./...
make test        # go test ./...  (тесты internal/store требуют Docker — testcontainers)
make web         # сборка SPA (npm ci && npm run build) в internal/web/dist
make build-all   # web + build

go test ./internal/service/ -run TestName -v   # один тест / пакет
cd web && npm run test -- --run                # фронт-тесты (Vitest)
cd web && npx tsc --noEmit                     # проверка типов SPA

Для запуска бинаря напрямую нужны те же переменные окружения: DNS_AR_DB_DSN, DNS_AR_ENC_KEY (обязательные), DNS_AR_LISTEN (по умолчанию :8080).

internal/web/dist/ — цель go:embed; в git коммитится только плейсхолдер index.html. npm run build перезаписывает его — перед коммитом выполнить git checkout internal/web/dist/index.html, а реальный бандл собирать через make web.

S
Description
No description provided
Readme
690 KiB
Languages
Go 60.8%
TypeScript 37.8%
CSS 1.1%
Dockerfile 0.1%