Реализация — app/models/ + миграция alembic/versions/20261003_0001_initial_schema.py (7 таблиц). Постгрес в докере (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 |
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). Креды каналов (токен бота, SMTP) — не здесь, а в .env |
routing_rules |
Правила «условия → действия» | conditions JSONB (source, subjects[], actions[], priority_min), template (Jinja2 {{ payload.x }}), weight (порядок; в MVP применяется первое подошедшее), 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 |
deliveries |
Доставка в конкретную цель | event_id (CASCADE), rule_id (SET NULL — правило удалим, историю оставим), channel, channel_target_id, status ∈ pending/delivered/failed, attempts, last_error, next_retry_at (+индекс status,next_retry_at — скан ретраев), rendered_message (аудит: что реально ушло в канал) |
gnexus-creds, не БД (TELEGRAM_BOT_TOKEN, SMTP и т.д.).tags события — убраны из v1 контракта (см. docs/05); вернутся, когда появится правило, требующее их.attempts/next_retry_at; поэлементный лог попыток не нужен MVP.alembic upgrade head поднимает всё из пустой базы (docker: api применяет миграции на старте).