vasyansk 27d70a987e fix(store): scope SetDomainStatus by project (IDOR); scheduler reuses DeriveStatus
handleCheck's error branch wrote last_check_status via an id-only UPDATE, so
an authenticated caller's own valid project id paired with a foreign domain
id in the URL could flip a stranger's domain to "error" even though Check
itself is project-scoped and would 404/error out first. Add project_id to
the WHERE clause (queries/domains.sql + generated db/domains.sql.go), thread
projectID through Store/TenantStore/SchedStore SetDomainStatus, and pass pid
from context at both call sites in handleCheck plus the scheduler.

Also collapse checkDomain's inline status derivation in scheduler.go into a
call to service.DeriveStatus, the same helper handleCheck already uses, so
there's a single source of truth for "drift vs in_sync" instead of two
copies that could drift apart.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
2026-07-05 14:40:13 +07:00

DNS Autoresolver

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

Возможности

  • Multi-tenant: проекты, аккаунты провайдера, домены — с авторизацией (регистрация/логин, сессии).
  • Провайдер Selectel: чтение зон/RRSet, диф против шаблона, ручной apply.
  • Шаблоны записей: неймспейс-независимая модель Record, движок диффа шаблон ↔ зона.
  • Диф + ручной apply: изменения показываются перед применением, apply — явное действие оператора.
  • Расписание проверок: планировщик периодически гоняет read-only check+notify (без Apply), пишет историю проверок и статус drift.
  • Уведомления: каналы Telegram и Webhook, per-channel статус доставки.
  • Метрики: Prometheus /metrics (публичный, без auth, только агрегаты).
  • Health-check: /healthz — liveness-проба, используется как Docker HEALTHCHECK через встроенный CLI-режим app -healthcheck.

Стек

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 ./...
make web         # сборка SPA (npm ci && npm run build) в internal/web/dist
make build-all   # web + build

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

S
Description
No description provided
Readme 535 KiB
Languages
Go 61%
TypeScript 37.5%
CSS 1.2%
Dockerfile 0.1%