@application-kit/mailer
v0.2.1
Published
Отправка писем по шаблонам для NestJS: SMTP или дамп в лог, идемпотентность по журналу отправленного, гейт подписок; шаблоны и тексты приносит приложение
Readme
@application-kit/mailer
Отправка писем по шаблонам для NestJS: рендер с подстановкой параметров, SMTP через nodemailer или дамп письма в
лог, идемпотентность по журналу отправленных писем (таблица sent_emails) и гейт подписок получателя. Какие письма
бывают, их тексты, языки и категории подписок пакет не знает — их приносит приложение.
Установка
yarn add @application-kit/mailerPeer-зависимости: @libs-for-dev/nestjs-config-module, @nestjs/common, @nestjs/typeorm, class-transformer,
class-validator, nestjs-pino, oxide.ts, typeorm.
Подключение
import { MailerModule } from '@application-kit/mailer'
@Module({
imports: [MailerModule.register({ subscriptions: NotificationPreferencesModule, templates: EmailTemplateCatalog })],
providers: [AccountEmailsService],
})
class AccountConsumersModule {}templates— класс каталога (EmailTemplateCatalogInterface): по имени письма и локали отдаёт тему и обе половины тела с плейсхолдерами{параметр}. Имя и локаль для пакета — строки; набор имён и языков держит тип каталога.subscriptions— модуль владельца настроек, экспортирующийEMAIL_SUBSCRIPTIONS_PORT(EmailSubscriptionsInterface).MAILER_LOGGERприложение приносит глобально, например из корневого модуля:{ provide: MAILER_LOGGER, useClass: MailerLogger }(MailerLogger— реализация наnestjs-pino, нужен глобальныйLoggerModule).
Наружу модуль отдаёт EmailDispatcherService с политикой отказа на выбор письма: sendOrRethrow (отказ пробрасывается —
обработчик события уйдёт в retry), sendOrLog (отказ только в лог) и sendIfSubscribed (письмо необязательной
категории уходит, только если получатель от неё не отписался).
Категории подписок объявляются module augmentation:
declare module '@application-kit/mailer' {
interface EmailCategoryRegistryInterface { NEWSLETTER: true }
}Окружение
| Переменная | По умолчанию | Смысл |
|---|---|---|
| MAIL_FROM | no-reply@localhost | адрес в поле From |
| MAIL_SMTP_HOST | пусто | SMTP-релей; пусто — режим без сети, письмо целиком уходит warning'ом в лог |
| MAIL_SMTP_PORT | 587 | на 465 — неявный TLS, на остальных обязателен STARTTLS |
| MAIL_SMTP_USER, MAIL_SMTP_PASSWORD | пусто | учётка релея; пустой пользователь — без аутентификации |
Окружение валидируется в MailerModule.register, то есть при сборке графа процесса, а не на импорте пакета.
Миграция и сущность
Таблицу журнала создаёт миграция пакета. Приложение подключает её и сущность классами — объектом orm — так один путь
работает и в dev, и в собранном образе, а migration:generate не предлагает удалить таблицу:
import { orm as mailerOrm } from '@application-kit/mailer'
createCliDataSource({ ..., packages: [mailerOrm] }) // @application-kit/typeorm/nestИмя класса миграции записано в таблице migrations уже мигрированных баз и не меняется.
Тесты потребителя
Вход @application-kit/mailer/testing — фейки портов: FakeEmailSender, FakeEmailSubscriptions,
FakeEmailTemplateCatalog, FakeEmailTemplateRenderer, FakeMailer, FakeSentEmailLog.
