← Email

Модель данных

email · workzone

Транспорт хранит ровно одно: настройки SMTP. Singleton smtp_settings в общем ядре, пароль — шифротекстом, готовность к отправке — предикатом на модели. Сервис, режимы и ошибки доставки — в доставке.

1 Конфигурация транспорта Singleton. Одна строка на инстанс, секрет at-rest.
smtp_settings настройки SMTP
singleton · CHECK (id = 1)
id BigInteger PKCHECK всегда 1 · singleton
host Text NULL адрес сервера · NULL пока не настроен
port Integer NULL порт · NULL пока не настроен
security Text NOT NULLDEFAULTCHECK none · starttls · ssl_tls · DEFAULT 'starttls'
username Text NULL логин SMTP-аккаунта
password_enc Text NULL шифротекст AES-256-GCM · → шифрование
from_address Text NULL RFC 5322 · display-name + адрес отправителя
is_enabled Boolean NOT NULLDEFAULT мастер-выключатель · DEFAULT false
last_test_ok Boolean NULL итог «Проверить соединение» · NULL = не проверяли
last_test_at DateTime(tz) NULL когда проверяли · NULL = не проверяли
created_at DateTime(tz) DEFAULT server_default=now()
updated_at DateTime(tz) DEFAULT now() + trigger
Singleton в core, отдельный от platform_settings → platform_settings
Своя таблица, не колонки в platform_settings: секрет и runtime-состояние (last_test_*) не раздувают общие настройки. ORM-модель — в core/models.py (общее ядро, как PlatformSettings): её читают и Email, и потребители (инвайт, сброс), без циклов между модулями. Поведением владеет Email; экран редактирования и кнопка проверки — Admin Panel.
Пароль — шифрование at-rest → Крипто-ядро
password_enc хранится шифротекстом, не открытым паролём. Хешировать нельзя — для логина на сервер нужен исходный пароль, значит обратимое AES-256-GCM. Шифр и работа с ключом — крипто-ядро Auth / Security, Email — его потребитель, своего механизма не заводит. Расшифровка только в момент send; наружу (API, экспорт) пароль не уходит — UI получает лишь флаг «задан / не задан». Секрет — сервисный: app-пароль или API-ключ провайдера для системного ящика (no-reply@…), а не личный логин; на экране поле так и подписано — «Пароль или API-ключ», с подсказкой про App Password для Gmail / Office 365.
Опциональность и живучесть — is_available() → Путь ошибок
Готовность транспорта — один предикат, свойство модели: is_enabled И заполнены обязательные поля (host, port, from_address). Живёт на модели в core — Email и потребители (инвайт, сброс) читают одну истину, не пересобирая её из колонок. last_test_okсовет, не ворота: «не проверяли» ≠ «сломано», а настоящая страховка доставки — ретраи очереди. Это best-effort пре-чек для UX (погасить кнопку, выбрать фолбэк), не гарантия доставки: SMTP может лечь в момент отправки — путь ошибок. Как именно потребитель деградирует (инвайт заблокирован, сброс — временным паролём) — решает он, описано у него.
2 Миграция Создаёт таблицу и сразу заселяет singleton.
alembic/versions/NNN_core_smtp_settings.py
Создаёт таблицу smtp_settings и тем же шагом заселяет singleton строкой is_enabled = false с NULL-полями — «не настроен» из коробки; приложение всегда UPDATE, не INSERT. downgrade()DROP TABLE.