Транспорт хранит ровно одно: настройки SMTP. Singleton
smtp_settings в общем ядре, пароль — шифротекстом,
готовность к отправке — предикатом на модели. Сервис, режимы и ошибки
доставки — в доставке.
| 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 |
platform_settings: секрет
и runtime-состояние (last_test_*) не раздувают общие
настройки. ORM-модель — в core/models.py (общее ядро,
как PlatformSettings): её читают и Email, и
потребители (инвайт, сброс), без циклов между модулями. Поведением
владеет Email; экран редактирования и кнопка проверки —
Admin Panel.
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 может лечь в момент отправки —
путь ошибок.
Как именно потребитель деградирует (инвайт заблокирован, сброс —
временным паролём) — решает он, описано у него.
alembic/versions/NNN_core_smtp_settings.py
smtp_settings и тем же шагом заселяет
singleton строкой is_enabled = false с NULL-полями —
«не настроен» из коробки; приложение всегда UPDATE,
не INSERT. downgrade() —
DROP TABLE.