Files
mono/components/email-relay/README.md
T
coopops 7e2b26a02e
Typecheck / desktop (pull_request) Has been cancelled
Typecheck / controller (pull_request) Has been cancelled
[C28-28][@ant] feat(email-relay): HTTP→SMTP релей для коопов с закрытым SMTP
Хостинги коопов режут исходящий SMTP → письма controller'а уходят в timeout
(раньше хабом был Novu, его убрали). Новый компонент @coopenomics/email-relay
принимает письма по HTTPS с Bearer-токеном и форвардит по SMTP. Ставится на
сервер с открытыми портами (плейбук в playbooks/email-relay).

- components/email-relay: express + nodemailer, POST /send + GET /health,
  Bearer-токен (constant-time), опц. IP-allowlist, конфиг из ENV
- controller: email-channel.adapter получил relay-режим — если задан
  EMAIL_RELAY_URL, письмо уходит POST'ом на релей; иначе прямой SMTP как
  раньше (opt-in, без регрессии)
- docker-compose: дев-сервисы email-relay + mailpit (SMTP-перехватчик для E2E)
- release.yaml: сборка образа dicoop/email-relay

E2E проверен локально: controller-формат письма → релей → SMTP → mailpit,
тело с <br> и заголовки целы; 401 на отсутствие/неверный токен, 400 на пустое тело.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 07:42:15 +00:00

2.3 KiB
Raw Blame History

@coopenomics/email-relay

Минимальный HTTP→SMTP релей. Принимает письма по HTTPS с Bearer-токеном и отправляет их по SMTP.

Зачем

На хостингах многих кооперативов закрыт исходящий SMTP (25/465/587) — письма из controller уходят в connection timeout. Открывать порты по тикету на каждом сервере неудобно. Решение: один релей на сервере с открытыми портами (voskhod), все коопы шлют письма ему по HTTPS, он форвардит по SMTP.

Раньше эту роль выполнял Novu — после его удаления нужен свой сервис.

API

  • GET /health{ "status": "ok" }
  • POST /send (требует Authorization: Bearer <RELAY_TOKEN>)
    { "from": "...", "to": "...", "subject": "...", "html": "...", "text": "..." }
    
    from необязателен (берётся EMAIL_FROM_DEFAULT); нужно тело html или text. Ответ: { "delivered": true, "messageId": "..." } либо 502 с error.

Аутентификация

Статичный Bearer-токен поверх TLS (nginx + Let's Encrypt). Сравнение токена — constant-time. Опционально IP-allowlist (RELAY_IP_ALLOWLIST). Ротация = смена RELAY_TOKEN на релее и у всех коопов.

Запуск

cp .env.example .env   # заполнить RELAY_TOKEN + SMTP_*
pnpm -F @coopenomics/email-relay dev    # разработка
pnpm -F @coopenomics/email-relay start  # прод

ENV — см. .env.example.

Интеграция с controller

В controller/.env кооператива:

EMAIL_RELAY_URL=https://mail-relay.coopenomics.world/send
EMAIL_RELAY_TOKEN=<тот же RELAY_TOKEN>

Если EMAIL_RELAY_URL не задан — controller шлёт напрямую по SMTP (старое поведение, без изменений).

Деплой

  • Образ: dicoop/email-relay (release-пайплайн, как остальные mono-сервисы).
  • Плейбук: playbooks/email-relay/setup.yaml (nginx TLS + docker compose).