Email-сервис Ecosystem: worker очереди писем + wire-контракт шины (пакет по тегу)
- C# 88.4%
- TypeScript 7%
- Vue 2.3%
- Dockerfile 1%
- CSS 0.7%
- Other 0.6%
Почта экосистемы ходит через сервис уведомлений (решение владельца 2026-09-03), а не
отдельным путём из каждого сервиса: у уведомления должен быть один журнал и одни
правила адресации. Сервису уведомлений нужен generic-транспорт письма — контракт
SendAccountEmail для этого не годится (там тип письма выбирает тему и текст).
- Контракт SendEmail {RequestId, To, Subject, Body, Link, Source}: тему и текст
составил издатель, Email-сервис только оформляет письмом и доставляет.
SendAccountEmail остаётся как был — письма учётки с одноразовыми ссылками идут
напрямую из Identity, им в общий журнал уведомлений нельзя.
- Консьюмер SendEmailConsumer: та же очередь, тот же пинок доставки и ретраи.
Идемпотентность по RequestId (проверка + уникальный индекс против гонки двух
экземпляров), пустой адрес или тема — предупреждение и выход, а не «ядовитое»
сообщение в очереди шины.
- Тип строки очереди — свой enum OutboxEmailKind вместо AccountEmailKind контракта:
«уведомление» не вид письма учётки, и тащить его в замороженный wire-контракт
незачем. Номера 0..2 совпадают с контрактом — тип лежит в БД числом.
- IEmailSender.SendNotificationAsync: текст приходит простым, отправитель экранирует
его целиком и делает абзацы, кнопка и подпись подвала — только когда есть что
показать.
- Миграция NotificationEmails (оба набора): Subject/Body/Source и RequestId.
Ключ добавляется тремя шагами (NULL → раздача значений → NOT NULL + уникальный
индекс): одной колонкой со значением по умолчанию миграция упала бы на проде —
там есть строки, и у всех оказался бы один и тот же пустой Guid.
Ядро 0.56.0.
|
||
|---|---|---|
| .forgejo/workflows | ||
| api | ||
| docs | ||
| ui | ||
| .gitignore | ||
| CLAUDE.md | ||
| Directory.Packages.props | ||
| Ecosystem-Email.sln | ||
| nuget.config | ||