Реализация — app/models/ + миграции alembic/versions/ (7 таблиц; 0001 — исходная схема, 0002/0003 — description и полный Routing Engine). Постгрес в докере (postgres:17-alpine, том pgdata), Redis 7 (том redisdata).
Два контура данных: конфигурация (человек заводит через админку, меняется редко) и поток событий (пишется автоматически, растёт всегда).
sources ──< api_keys │ └──< notification_types (уникальность: source_id + subject + action) │ routing_rules ──< routing_rule_actions ──> channel_targets
| Таблица | Что хранит | Ключевые поля |
|---|---|---|
sources |
Источники событий (регистрирует админ) | name (slug, unique: monitoring), label, description («что это за сервис» для человека и ИИ-агента) |
api_keys |
Ключи syn_… к источникам |
token_hash (sha256, unique), token_hint (последние 4 для UI), revoked_at (ротация: новый ключ, старая строка маркируется). Ключ — удостоверение; правила матчают source, не токен |
notification_types |
Реестр типов из контракта | тройка (source_id, subject, action) уникальна, payload_schema JSONB (опц., валидирует воркер) |
channel_targets |
Цели каналов: куда доставлять | channel ∈ telegram/email/s2s/internal_log (CHECK), config JSONB (chat_id, адрес, s2s-endpoint; s2s ещё token_ref — ссылка на секрет), description («что это за клиент-сервис»). Креды каналов (токен бота, SMTP, HMAC-секреты) — не здесь, а в .env |
routing_rules |
Правила «условия → действия» | conditions JSONB (source, subjects[], actions[], priority_min, payload — точный матч по top-level ключам), template (Jinja2 {{ payload.x }}), weight (порядок, важен в режиме first), throttle_seconds (шумодав: окно на (channel, target), critical проходит всегда), enabled |
routing_rule_actions |
Действия правила | rule_id, channel (CHECK), target_id → channel_targets (SET NULL при удалении цели), template — переопределение шаблона правила |
events 1 ──< deliveries
| Таблица | Что хранит | Ключевые поля |
|---|---|---|
events |
Конверт события (контракт docs/05) | UUID pk, source_id (RESTRICT — события переживают удаление источника), subject/action/priority (CHECK по шкале), payload JSONB, dedup_key (+индекс с created_at — окно дедупликации), expires_at (= created+ttl_seconds), scheduled_at (резерв), status ∈ queued/processing/done/failed (done — замаршрутизировано; живые статусы в deliveries) |
deliveries |
Доставка в конкретную цель | event_id (CASCADE), rule_id (SET NULL — правило удалим, историю оставим), channel, channel_target_id, status ∈ pending/delivered/failed/skipped (skipped — срезано шумодавом), attempts, last_error, next_retry_at (+индекс status,next_retry_at — скан ретраей beat'ом), rendered_message (аудит: что реально уйдёт в канал) |
gnexus-creds, не БД (TELEGRAM_BOT_TOKEN, SMTP и т.д.).tags события — убраны из v1 контракта (см. docs/05); вернутся, когда появится правило, требующее их.attempts/next_retry_at; поэлементный лог попыток не нужен MVP.alembic upgrade head поднимает всё из пустой базы (docker: api применяет миграции на старте).