docs: детализация дизайна Фазы 3 (расписание, уведомления, метрики)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -385,3 +385,102 @@ internal/api/middleware.go — RequireAuth (session→userID в контек
|
|||||||
### Разбивка
|
### Разбивка
|
||||||
|
|
||||||
Один план `phase2-auth`.
|
Один план `phase2-auth`.
|
||||||
|
|
||||||
|
## Фаза 3 — детализация (расписание · уведомления · метрики)
|
||||||
|
|
||||||
|
Периодические проверки доменов по расписанию, уведомления о смене статуса, Prometheus-метрики.
|
||||||
|
Планировщик **только проверяет и уведомляет** — apply остаётся ручным (prune-guard из 1A/1B).
|
||||||
|
|
||||||
|
### Решения
|
||||||
|
|
||||||
|
- **Планировщик — встроенный in-process**: goroutine-тикер в `cmd/server`, интервал из БД, `last_run_at`
|
||||||
|
персистентен (переживает рестарт). Без внешних зависимостей.
|
||||||
|
- **Гранулярность — на проект**: единый интервал проверки всех доменов проекта.
|
||||||
|
- **Каналы уведомлений — Telegram + Webhook** (чистый `net/http`, без внешних lib). Telegram —
|
||||||
|
Bot API `sendMessage`; Webhook — HTTP POST JSON.
|
||||||
|
- **Уведомление при СМЕНЕ статуса домена** (in_sync ↔ drift ↔ error), не спам каждый тик —
|
||||||
|
`domains.last_check_status`.
|
||||||
|
- **Метрики — Prometheus** (`client_golang`, custom registry), `/metrics` **публичный** (scrape),
|
||||||
|
без секретов.
|
||||||
|
- **bot_token Telegram шифруется** (тот же `crypto.Cipher`, что provider-секреты); в ответах не отдаётся.
|
||||||
|
|
||||||
|
### БД (миграция 0004)
|
||||||
|
|
||||||
|
```
|
||||||
|
CREATE TABLE schedules (
|
||||||
|
id uuid PRIMARY KEY,
|
||||||
|
project_id uuid NOT NULL UNIQUE REFERENCES projects(id) ON DELETE CASCADE,
|
||||||
|
interval_seconds int NOT NULL DEFAULT 3600,
|
||||||
|
enabled boolean NOT NULL DEFAULT false,
|
||||||
|
last_run_at timestamptz,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
CREATE TABLE notification_channels (
|
||||||
|
id uuid PRIMARY KEY,
|
||||||
|
project_id uuid NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
|
||||||
|
type text NOT NULL, -- telegram | webhook
|
||||||
|
config jsonb NOT NULL, -- telegram: {chat_id}; webhook: {url}
|
||||||
|
secret_enc text NOT NULL DEFAULT '', -- telegram: bot_token (шифр); webhook: пусто/подпись
|
||||||
|
enabled boolean NOT NULL DEFAULT true,
|
||||||
|
created_at timestamptz NOT NULL DEFAULT now()
|
||||||
|
);
|
||||||
|
ALTER TABLE domains ADD COLUMN last_check_status text NOT NULL DEFAULT 'unknown'; -- unknown|in_sync|drift|error
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
|
||||||
|
```
|
||||||
|
internal/notify
|
||||||
|
├─ notify.go — интерфейс Notifier{Send(ctx, Event) error}, тип Event{Project,Domain,Status,Summary,At}
|
||||||
|
├─ telegram.go — TelegramNotifier (Bot API sendMessage, net/http)
|
||||||
|
├─ webhook.go — WebhookNotifier (HTTP POST JSON)
|
||||||
|
└─ dispatch.go — Dispatcher: по каналам проекта шлёт Event через нужный Notifier (расшифровка secret)
|
||||||
|
internal/scheduler
|
||||||
|
└─ scheduler.go — Scheduler{Run(ctx)}: тикер → due-проекты (enabled && now-last_run>=interval)
|
||||||
|
→ для каждого домена service.Check → сохранить check_run + обновить last_check_status
|
||||||
|
→ при смене статуса → Dispatcher.Send; обновить last_run_at
|
||||||
|
internal/metrics
|
||||||
|
└─ metrics.go — Metrics{registry, counters/histogram/gauge}; Handler() для /metrics
|
||||||
|
```
|
||||||
|
|
||||||
|
- **Метрики** (custom `prometheus.Registry`, `promauto`): `dns_ar_checks_total{status}` (counter),
|
||||||
|
`dns_ar_check_duration_seconds` (histogram), `dns_ar_drift_domains` (gauge — текущее число в дрейфе),
|
||||||
|
`dns_ar_notifications_total{channel,status}` (counter). Инструментируются scheduler/service/notify.
|
||||||
|
`/metrics` — `promhttp.HandlerFor(reg, ...)`, публичный.
|
||||||
|
- **Планировщик**: последовательная обработка проектов (без гонок по проекту), graceful shutdown по
|
||||||
|
`context`. Ошибка проверки домена → статус `error` + метрика + уведомление.
|
||||||
|
|
||||||
|
### REST API (`/api/v1/projects/{pid}`, под RequireAuth+RequireProjectAccess)
|
||||||
|
|
||||||
|
| Метод/путь | Назначение |
|
||||||
|
|---|---|
|
||||||
|
| `GET/PUT /schedule` | получить/задать расписание проекта (interval_seconds, enabled) |
|
||||||
|
| `POST/GET/DELETE /channels` | CRUD каналов уведомлений (secret на вход, не на выход) |
|
||||||
|
| `POST /channels/{cid}/test` | тест-отправка уведомления в канал |
|
||||||
|
| `GET /domains/{did}/history` | история проверок (check_runs) домена |
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
|
||||||
|
- **SchedulePage** (или блок в проекте): интервал + вкл/выкл.
|
||||||
|
- **ChannelsPage**: CRUD каналов (Telegram: chat_id + bot_token; Webhook: url), тест-отправка. Секрет только на вход.
|
||||||
|
- **История проверок** домена (список check_runs с результатом/временем).
|
||||||
|
- **drift-badge** в списке доменов: `last_check_status` (in_sync=emerald, drift=amber, error=rose, unknown=muted).
|
||||||
|
|
||||||
|
### Инварианты
|
||||||
|
|
||||||
|
- Планировщик не применяет изменения (только check + notify).
|
||||||
|
- `bot_token`/секреты каналов не в ответах/логах; `/metrics` без секретов и PII (только агрегаты по status/channel).
|
||||||
|
- Уведомления идемпотентны по смене статуса (нет спама при неизменном дрейфе).
|
||||||
|
- Всё scoped по проекту (мультитенантность/IDOR из Фазы 2 сохраняется).
|
||||||
|
|
||||||
|
### Тестирование Фазы 3
|
||||||
|
|
||||||
|
- `notify` — Telegram/Webhook против `httptest` (корректный запрос, обработка ошибки); Dispatcher (выбор канала, расшифровка).
|
||||||
|
- `scheduler` — мок store/service/notifier, управляемый «тик»: due-выбор, смена статуса → уведомление, идемпотентность (нет уведомления при неизменном статусе), ошибка → error-статус.
|
||||||
|
- `metrics` — `testutil` (счётчики/гистограмма инкрементируются), `/metrics` отдаёт.
|
||||||
|
- API — httptest + store (CRUD schedule/channels, secret не в ответе, история).
|
||||||
|
- Frontend — Vitest (клиент/хуки, drift-badge, формы каналов, тест-отправка).
|
||||||
|
|
||||||
|
### Разбивка
|
||||||
|
|
||||||
|
Один план `phase3-scheduler-notify-metrics`.
|
||||||
|
|||||||
Reference in New Issue
Block a user