Appearance
Email System
Overview
packages/email (@mtl-rent/email) provides transactional email templates, rendering, and delivery via a RabbitMQ queue. Emails are rendered with React Email and sent through Resend.
Architecture
API route (POST /inquiries, POST /saved-searches)
→ fastify.publishEmail({ action, to, locale, data })
→ RabbitMQ exchange "email" → queue "email.send"
→ Scraper worker email consumer
→ @mtl-rent/email sendEmail() → renderEmail() → Resend API
Scraper periodic task (saved-search-alerts)
→ publishEmailMessage() → same RabbitMQ queue → same consumer- Publisher: API uses
@mtl-rent/email/publish(Fastify plugin atservices/api/src/plugins/email.ts) - Consumer: Scraper worker (
services/scraper/src/consumers/email-consumer.ts) — prefetch: 3, max retries: 3 - Dead letters: Failed emails go to the DLQ after 3 retries
Templates
| Action | Category | Trigger | Description |
|---|---|---|---|
welcome | Transactional | POST /auth/register | Email verification on signup |
email-change | Transactional | Change email | Verify new email address |
reset-password | Transactional | Forgot password | Time-limited reset link |
password-changed | Transactional | After password change | Security notification |
account-deactivated | Transactional | Account deletion | 30-day recovery window |
subscription-alerts | Alerts | POST /saved-searches | Alert subscription confirmation |
new-listing-match | Alerts | Daily 8am job | New listings matching user's alert |
inquiry-sent | Messaging | POST /inquiries | Confirmation to sender |
inquiry-received | Messaging | POST /inquiries | Notification to landlord |
All templates support EN and FR locales.
Package Exports
typescript
// Main: types, renderEmail, registry
import { renderEmail, templateRegistry, type EmailMessage } from "@mtl-rent/email";
// Publisher: RabbitMQ email queue publisher
import { initEmailPublisher, publishEmail, closeEmailPublisher } from "@mtl-rent/email/publish";
// Send: Direct Resend sender (used by consumer)
import { sendEmail } from "@mtl-rent/email/send";Adding a New Template
Define the data type in
packages/email/src/templates/_lib/types.ts:typescriptexport interface MyNewEmailData { name: string; link: string; }Add to
EmailActionunion andEmailDataMap.Add translations in
packages/email/src/templates/_i18n/en.tsandfr.ts:typescript'my-new-email': { subject: '...', heading: '...', body: '...', buttonText: '...' }Create the template in
packages/email/src/templates/<category>/my-new-email.tsx:tsximport { Layout } from '../components/layout'; export default function MyNewEmail({ locale = 'en', data }) { ... }Register metadata in
packages/email/src/templates/_lib/registry.ts.Add to render map in
packages/email/src/templates/_lib/render.ts.Build:
pnpm --filter @mtl-rent/email build
Preview Server
bash
pnpm email:preview
# → http://localhost:3100Interactive UI with:
- Sidebar listing all 9 templates by category
- EN/FR locale toggle
- Live HTML preview
- Send Test button (requires
RESEND_API_KEY+TEST_EMAIL_TOin.env)
Testing
bash
# Send a test email via CLI
pnpm email:send -- --action reset-password --locale en --to user@example.comConfiguration
Environment variables (in .env):
RESEND_API_KEY=re_your_api_key_here
RESEND_FROM_EMAIL=MTL Rent <alerts@mtl-rent.fesenko.net>
AMQP_URL=amqp://mtl:mtl@localhost:5672
TEST_EMAIL_TO=dev@example.com # For preview server Send TestKey Files
| File | Purpose |
|---|---|
packages/email/src/index.ts | Main exports (types, render, registry) |
packages/email/src/publish.ts | RabbitMQ publisher |
packages/email/src/send.ts | Resend sender |
packages/email/src/templates/_lib/types.ts | Email action types + data interfaces |
packages/email/src/templates/_lib/render.ts | React → HTML + plain text |
packages/email/src/templates/_lib/registry.ts | Template metadata |
packages/email/src/templates/_i18n/ | EN/FR translations |
packages/email/src/templates/components/layout.tsx | Shared email layout |
packages/email/src/preview/server.ts | Dev preview server |
services/api/src/plugins/email.ts | Fastify email publisher plugin |
services/scraper/src/consumers/email-consumer.ts | RabbitMQ email consumer |
services/scraper/src/mq/topology.ts | Email exchange/queue declarations |