Skip to content

Пошта ​

Бекенд надсилає листи через SMTP за допомогою nodemailer. Канал вимкнено, доки не задано змінні SMTP_*: їх перелік і значення за замовчуванням описані в Конфігурації. Push-сповіщення описано окремо: Web Push сповіщення.

Транспорт ​

Сервіс email-service.ts створює один лінивий nodemailer-транспорт (createTransport) з host, port, secure, опційними requireTLS/ignoreTLS та auth (лише якщо задані і SMTP_USER, і SMTP_PASSWORD). Налаштування беруться з config/mail.ts.

Публічний API — об'єкт emailService:

  • isConfigured(): boolean — чи пройдена перевірка конфігурації;
  • send(input: SendEmailInput): Promise<AppResult<SendEmailOutput>> — повертає Result (better-result), не кидає винятків.

SendEmailInput підтримує to, cc, bcc, replyTo (рядок, { email, name? } або масив), subject, text/html, from та attachments. Якщо from не задано, використовується SMTP_FROM_NAME <SMTP_FROM_EMAIL>; якщо replyTo не задано — SMTP_REPLY_TO з конфігу.

Поведінка, коли SMTP вимкнено ​

Пошта не підміняється логуванням. Перед кожною відправкою send() перевіряє конфігурацію і при помилці пише logger.warn та повертає Result.err(internalMessage(...)). Можливі причини:

  • Email service is disabled — SMTP_ENABLED не true;
  • SMTP_HOST is not configured;
  • SMTP_FROM_EMAIL is not configured;
  • SMTP_USER and SMTP_PASSWORD must be configured together.

Далі валідуються вхідні дані (порожній subject, відсутні і text, і html, порожній список отримувачів, порожній відправник) — це badRequest. Помилка SMTP логується (Failed to send email) і повертається як internal. Успіх логується як Email sent з messageId, accepted, rejected.

Наслідок для auth-флоу

Оскільки виклики нижче перетворюють помилку send() на internal, при вимкненому SMTP запити на верифікацію email і скидання пароля через email завершуються помилкою сервера, а не «тихо» успіхом. Для локальної розробки без SMTP цей шлях не працює — див. Локальне тестування.

Які листи надсилаються ​

Шаблонів-файлів і i18n немає: тема, text та html будуються в коді, англійською. Верстка в одному місці — renderBrandedEmail у email-templates.ts (палітра EMAIL_BRAND, екранування імені й посилання через escapeHtml); кожна з функцій нижче лише передає слова й тип дії (кнопка з запасним посиланням або одне посилання). Побайтово лист закріплено файлами в __snapshots__.

ЛистФункція-шаблонДе викликається
Підтвердження email («Confirm your email»)buildEmailContentemail-verification-service.ts
Прив'язка нового email до акаунтаbuildLinkEmailContentemail-verification-service.ts
Скидання пароляbuildResetEmailContentpassword-reset-service.ts

Посилання в листах містять сирий токен, а в БД зберігається лише його хеш. Деталі флоу — у Автентифікації.

Тестовий лист: mail:test ​

Скрипт send-test-email.ts (tsx, підвантажує .env через dotenv/config) надсилає простий лист через той самий emailService:

bash
pnpm --filter backend mail:test you@example.com "Optional subject"

Без отримувача скрипт друкує usage і завершується з кодом 1. При помилці друкує Failed to send test email: <причина> (наприклад Email service is disabled) і теж повертає 1; при успіху — Message ID та прийнятих отримувачів. Див. також Скрипти.

Юніт-тести сервісу — email-service.spec.ts.

Локальне тестування ​

підніміть локальний SMTP-перехоплювач (наприклад Mailpit або MailHog на порту 1025), задайте SMTP_ENABLED=true, SMTP_HOST=localhost, SMTP_PORT=1025, SMTP_REQUIRE_TLS=false, SMTP_FROM_EMAIL=noreply@example.com без логіна/пароля і перевірте pnpm --filter backend mail:test you@example.com. Ці інструменти не входять до репозиторію.

Юніт-тести: pnpm --filter backend test.

Автор: Олександр Блажейко · blazheiko@gmail.com