platform-codebase/@packages/@infrastructure/email-client/README.md

172 lines
4.2 KiB
Markdown
Raw Permalink Normal View History

# @lilith/email-client
Shared NestJS client for sending emails through the centralized email service. Any backend feature that needs to send email should use this package rather than implementing direct SMTP/nodemailer.
## Installation
Add to your feature's `package.json`:
```json
{
"dependencies": {
"@lilith/email-client": "*"
}
}
```
## Usage
### 1. Import the module
```typescript
// app.module.ts
import { EmailClientModule } from '@lilith/email-client'
@Module({
imports: [
EmailClientModule.forRoot(),
// ...
],
})
export class AppModule {}
```
The module is `@Global()` — once imported in AppModule, `EmailClientService` is injectable everywhere.
### 2. Inject the service
```typescript
import { EmailClientService } from '@lilith/email-client'
@Injectable()
export class MyService {
constructor(private readonly emailClient: EmailClientService) {}
async notifyUser() {
await this.emailClient.sendTemplate({
to: 'user@example.com',
templateName: 'my-feature/notification',
variables: { name: 'Alice', action: 'completed' },
category: 'my-feature',
priority: 'normal',
})
}
}
```
## API
### Typed methods (SSO/auth emails)
| Method | Purpose |
|--------|---------|
| `sendWelcome(data)` | Welcome email to new user |
| `sendVerification(data)` | Email verification link |
| `sendPasswordReset(data)` | Password reset (accepts `resetToken`, NOT `resetUrl`) |
| `sendPasswordChanged(data)` | Password changed confirmation |
| `sendAccountLocked(data)` | Account locked notification |
| `sendLoginAlert(data)` | New login alert |
| `sendOtp(data)` | MFA OTP code |
### Generic methods (any feature)
| Method | Purpose |
|--------|---------|
| `sendTemplate(options)` | Send via named Handlebars template |
| `sendCustom(options)` | Send with inline HTML content |
### SendTemplateEmailOptions
```typescript
interface SendTemplateEmailOptions {
to: string | string[]
templateName: string // e.g. 'qa/report-alert', 'merch/approval'
variables: Record<string, unknown>
category?: string
userId?: string
priority?: 'high' | 'normal' | 'low'
}
```
### SendCustomEmailOptions
```typescript
interface SendCustomEmailOptions {
to: string | string[]
subject: string
html: string
text?: string
category?: string
userId?: string
priority?: 'high' | 'normal' | 'low'
}
```
## Configuration
### Defaults (zero-config)
With `EmailClientModule.forRoot()` and no arguments, the client:
1. Resolves the email service URL from `@lilith/service-registry`
2. Reads the API key from `EMAIL_INTERNAL_API_KEY` env var
3. If the API key is missing, email sending is **disabled** (logs a warning, never throws)
### Custom options
```typescript
EmailClientModule.forRoot({
serviceUrl: 'http://custom-email:3011', // Override URL
apiKeyEnvVar: 'MY_API_KEY_VAR', // Override env var name
})
```
### Async options
```typescript
EmailClientModule.forRootAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
serviceUrl: config.get('EMAIL_URL'),
}),
})
```
## Environment Variables
| Variable | Required | Default | Purpose |
|----------|----------|---------|---------|
| `EMAIL_INTERNAL_API_KEY` | Yes | — | API key for email service internal endpoints |
| `EMAIL_SERVICE_URL` | No | service-registry | Override email service URL |
## Error Handling
All methods are **graceful** — email failures log errors but never throw. This ensures email delivery issues never break calling service flows. Methods return `string | null` (the job ID, or null on failure).
## Current Consumers
| Feature | Methods Used |
|---------|-------------|
| SSO | All 7 typed methods (welcome, verification, reset, etc.) |
| Platform Admin | `sendTemplate()` for QA report alerts |
| Landing | `sendTemplate()` for merch approval/rejection |
| QA Backend | Domain events (indirect via email service QA processor) |
## Internal Architecture
```
Your Service
│ emailClient.sendTemplate({...})
EmailClientService (this package)
│ POST /internal/send/template
│ Header: X-Internal-Api-Key
Email Service (codebase/features/email/backend-api)
│ BullMQ queue → Handlebars render → Nodemailer
SMTP → Recipient
```