976 lines
31 KiB
Markdown
Executable file
976 lines
31 KiB
Markdown
Executable file
# Email Feature Architecture
|
|
|
|
**Status: COMPLETE** - All core functionality implemented and tested.
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
Centralized email system for the Lilith Platform, handling all transactional and notification emails across the platform. Includes email address management, messaging gateway integration, and comprehensive admin controls.
|
|
|
|
---
|
|
|
|
## Final Directory Structure
|
|
|
|
```
|
|
features/email/
|
|
├── backend/ # NestJS email service (port 3011)
|
|
│ ├── src/
|
|
│ │ ├── main.ts # Application entry point
|
|
│ │ ├── app.module.ts # Root module
|
|
│ │ ├── health.controller.ts # Health check endpoint
|
|
│ │ │
|
|
│ │ ├── core/ # Shared email infrastructure
|
|
│ │ │ ├── core.module.ts
|
|
│ │ │ ├── email-sender.service.ts # Nodemailer wrapper
|
|
│ │ │ ├── email-queue.service.ts # Bull queue for async
|
|
│ │ │ ├── email-log.service.ts # Database logging
|
|
│ │ │ ├── template-renderer.service.ts # Handlebars rendering
|
|
│ │ │ └── entities/
|
|
│ │ │ ├── email-log.entity.ts
|
|
│ │ │ └── email-template.entity.ts
|
|
│ │ │
|
|
│ │ ├── addresses/ # Email address management
|
|
│ │ │ ├── addresses.module.ts
|
|
│ │ │ ├── addresses.controller.ts
|
|
│ │ │ ├── addresses.service.ts
|
|
│ │ │ ├── aliases.service.ts
|
|
│ │ │ └── entities/
|
|
│ │ │ ├── email-address.entity.ts
|
|
│ │ │ └── email-alias.entity.ts
|
|
│ │ │
|
|
│ │ ├── preferences/ # User email preferences
|
|
│ │ │ ├── preferences.module.ts
|
|
│ │ │ ├── preferences.controller.ts
|
|
│ │ │ ├── preferences.service.ts
|
|
│ │ │ └── entities/
|
|
│ │ │ └── email-preference.entity.ts
|
|
│ │ │
|
|
│ │ ├── admin/ # Admin management endpoints
|
|
│ │ │ ├── admin.module.ts
|
|
│ │ │ ├── admin.controller.ts # Stats, queue control
|
|
│ │ │ ├── templates.controller.ts # Template CRUD
|
|
│ │ │ └── logs.controller.ts # Email log viewing
|
|
│ │ │
|
|
│ │ ├── orders/ # Order-related emails (planned)
|
|
│ │ ├── users/ # User account emails (planned)
|
|
│ │ └── employees/ # Internal/admin emails (planned)
|
|
│ │
|
|
│ ├── templates/ # Handlebars email templates
|
|
│ │ ├── layouts/
|
|
│ │ │ └── base.hbs
|
|
│ │ ├── orders/
|
|
│ │ ├── users/
|
|
│ │ └── employees/
|
|
│ │
|
|
│ └── package.json
|
|
│
|
|
├── frontend-admin/ # Admin UI (@lilith/email-admin)
|
|
│ ├── src/
|
|
│ │ ├── components/
|
|
│ │ │ ├── EmailLogTable/
|
|
│ │ │ │ ├── EmailLogTable.tsx
|
|
│ │ │ │ └── EmailLogDetail.tsx
|
|
│ │ │ ├── EmailStats/
|
|
│ │ │ │ ├── DeliveryStats.tsx
|
|
│ │ │ │ └── CategoryBreakdown.tsx
|
|
│ │ │ ├── TemplateEditor/
|
|
│ │ │ │ ├── TemplateEditor.tsx
|
|
│ │ │ │ ├── TemplatePreview.tsx
|
|
│ │ │ │ └── VariableInserter.tsx
|
|
│ │ │ └── index.ts
|
|
│ │ │
|
|
│ │ ├── pages/
|
|
│ │ │ ├── EmailDashboard.tsx
|
|
│ │ │ ├── EmailTemplatesPage.tsx
|
|
│ │ │ ├── EmailLogsPage.tsx
|
|
│ │ │ └── index.ts
|
|
│ │ │
|
|
│ │ ├── hooks/
|
|
│ │ │ ├── useEmailLogs.ts
|
|
│ │ │ ├── useEmailTemplates.ts
|
|
│ │ │ ├── useEmailStats.ts
|
|
│ │ │ └── index.ts
|
|
│ │ │
|
|
│ │ ├── types/
|
|
│ │ │ └── index.ts
|
|
│ │ │
|
|
│ │ └── index.ts # Main export for platform-admin
|
|
│ │
|
|
│ └── package.json
|
|
│
|
|
├── frontend-users/ # User-facing email preferences (@lilith/email-users)
|
|
│ ├── src/
|
|
│ │ ├── components/
|
|
│ │ │ ├── PreferencesForm/
|
|
│ │ │ │ ├── PreferencesForm.tsx
|
|
│ │ │ │ └── CategoryToggle.tsx
|
|
│ │ │ ├── UnsubscribePage/
|
|
│ │ │ │ └── UnsubscribePage.tsx
|
|
│ │ │ └── index.ts
|
|
│ │ │
|
|
│ │ ├── pages/
|
|
│ │ │ ├── EmailPreferencesPage.tsx
|
|
│ │ │ ├── UnsubscribeConfirmPage.tsx
|
|
│ │ │ └── index.ts
|
|
│ │ │
|
|
│ │ ├── hooks/
|
|
│ │ │ ├── useEmailPreferences.ts
|
|
│ │ │ └── index.ts
|
|
│ │ │
|
|
│ │ ├── api/
|
|
│ │ │ └── emailPreferencesApi.ts
|
|
│ │ │
|
|
│ │ └── index.ts # Main export for portal
|
|
│ │
|
|
│ └── package.json
|
|
│
|
|
├── shared/ # Shared types (@lilith/email-shared)
|
|
│ ├── src/
|
|
│ │ ├── types.ts # Common interfaces/types
|
|
│ │ ├── constants.ts # Enums and constants
|
|
│ │ └── index.ts
|
|
│ │
|
|
│ └── package.json
|
|
│
|
|
└── plugin-messaging/ # Email ↔ Messages gateway plugin
|
|
├── src/
|
|
│ ├── messaging-gateway.module.ts
|
|
│ ├── gateway.controller.ts # Webhook, sync, stats
|
|
│ │
|
|
│ ├── inbound/ # Email → Message conversion
|
|
│ │ ├── inbound.module.ts
|
|
│ │ ├── email-receiver.service.ts # IMAP/webhook listener
|
|
│ │ ├── email-parser.service.ts # Parse email content
|
|
│ │ └── message-creator.service.ts # Create InboxMessage
|
|
│ │
|
|
│ ├── outbound/ # Message → Email sending
|
|
│ │ ├── outbound.module.ts
|
|
│ │ ├── message-listener.service.ts # Listen for outbound messages
|
|
│ │ └── email-composer.service.ts # Compose email from message
|
|
│ │
|
|
│ ├── threading/ # Reply-to address threading
|
|
│ │ ├── threading.module.ts
|
|
│ │ ├── reply-address.service.ts # Generate/parse reply-to
|
|
│ │ └── thread-matcher.service.ts # Match email to thread
|
|
│ │
|
|
│ └── entities/
|
|
│ └── email-thread-mapping.entity.ts
|
|
│
|
|
└── package.json
|
|
```
|
|
|
|
---
|
|
|
|
## Package Dependencies
|
|
|
|
```
|
|
┌─────────────────────┐
|
|
│ @lilith/email-admin │ (Frontend package)
|
|
└──────────┬──────────┘
|
|
│ imports from
|
|
▼
|
|
┌─────────────────────┐
|
|
│ @lilith/email-shared│ (Types/constants)
|
|
└─────────────────────┘
|
|
|
|
┌──────────────────────┐
|
|
│ @lilith/email-users │ (Frontend package)
|
|
└──────────┬───────────┘
|
|
│ imports from
|
|
▼
|
|
┌─────────────────────┐
|
|
│ @lilith/email-shared│
|
|
└─────────────────────┘
|
|
|
|
┌─────────────────────┐
|
|
│ @lilith/email-backend│ (NestJS service)
|
|
└──────────┬──────────┘
|
|
│ uses (internal modules)
|
|
▼
|
|
┌────────┐
|
|
│ core │ ← addresses, preferences, admin all depend on core
|
|
└────────┘
|
|
|
|
┌──────────────────────────┐
|
|
│ @lilith/email-plugin- │
|
|
│ messaging │ (Optional plugin)
|
|
└──────────┬───────────────┘
|
|
│ integrates with
|
|
▼
|
|
┌─────────────────────┐
|
|
│ @lilith/email-backend│
|
|
└─────────────────────┘
|
|
```
|
|
|
|
**Import Rules**:
|
|
- `frontend-admin` and `frontend-users` → `shared` (types only)
|
|
- Frontend packages → Backend API (via HTTP, never direct import)
|
|
- Plugin → Backend core services (dependency injection)
|
|
|
|
---
|
|
|
|
## Database Schema
|
|
|
|
All 6 tables with relationships:
|
|
|
|
```sql
|
|
-- ============================================================================
|
|
-- Email Logs (All sent emails)
|
|
-- ============================================================================
|
|
CREATE TABLE email_logs (
|
|
id UUID PRIMARY KEY,
|
|
recipient_email VARCHAR(255) NOT NULL,
|
|
recipient_user_id UUID,
|
|
category VARCHAR(50) NOT NULL, -- 'orders', 'users', 'employees', 'messaging', 'system'
|
|
template_name VARCHAR(100) NOT NULL,
|
|
subject VARCHAR(500) NOT NULL,
|
|
status VARCHAR(50) DEFAULT 'queued', -- queued, sending, sent, delivered, bounced, failed
|
|
sent_at TIMESTAMP,
|
|
delivered_at TIMESTAMP,
|
|
opened_at TIMESTAMP,
|
|
error_message TEXT,
|
|
metadata JSONB, -- Template variables, tracking IDs
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_email_logs_recipient ON email_logs(recipient_email);
|
|
CREATE INDEX idx_email_logs_category ON email_logs(category);
|
|
CREATE INDEX idx_email_logs_created ON email_logs(created_at);
|
|
CREATE INDEX idx_email_logs_status ON email_logs(status);
|
|
|
|
-- ============================================================================
|
|
-- Email Templates (Admin-editable)
|
|
-- ============================================================================
|
|
CREATE TABLE email_templates (
|
|
id UUID PRIMARY KEY,
|
|
name VARCHAR(100) NOT NULL UNIQUE,
|
|
category VARCHAR(50) NOT NULL,
|
|
subject_template VARCHAR(500) NOT NULL,
|
|
html_template TEXT NOT NULL,
|
|
text_template TEXT,
|
|
variables JSONB, -- { "name": { "description": "...", "required": true } }
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
updated_by UUID,
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
CREATE UNIQUE INDEX idx_templates_name ON email_templates(name);
|
|
CREATE INDEX idx_templates_category ON email_templates(category);
|
|
|
|
-- ============================================================================
|
|
-- Email Preferences (User settings)
|
|
-- ============================================================================
|
|
CREATE TABLE email_preferences (
|
|
id UUID PRIMARY KEY,
|
|
user_id UUID NOT NULL UNIQUE,
|
|
orders_enabled BOOLEAN DEFAULT TRUE,
|
|
account_enabled BOOLEAN DEFAULT TRUE, -- Security emails (always sent regardless)
|
|
marketing_enabled BOOLEAN DEFAULT FALSE,
|
|
digest_frequency VARCHAR(20) DEFAULT 'weekly', -- daily, weekly, never
|
|
unsubscribed_at TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
CREATE UNIQUE INDEX idx_email_preferences_user ON email_preferences(user_id);
|
|
|
|
-- ============================================================================
|
|
-- Email Addresses (User-owned email addresses)
|
|
-- ============================================================================
|
|
CREATE TABLE email_addresses (
|
|
id UUID PRIMARY KEY,
|
|
profile_id UUID NOT NULL, -- References user_profiles.id
|
|
|
|
-- Address details
|
|
local_part VARCHAR(100) NOT NULL, -- 'aurora' in aurora@inbox.lilith.gg
|
|
domain VARCHAR(100) NOT NULL DEFAULT 'inbox.lilith.gg',
|
|
display_name VARCHAR(255), -- 'Aurora ✨'
|
|
|
|
-- Type and status
|
|
address_type VARCHAR(20) DEFAULT 'standard', -- standard, vanity, system
|
|
is_primary BOOLEAN DEFAULT FALSE,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
|
|
-- Settings
|
|
forward_to_external VARCHAR(255), -- Optional external forwarding
|
|
auto_reply_enabled BOOLEAN DEFAULT FALSE,
|
|
auto_reply_message TEXT,
|
|
|
|
-- Metadata
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
updated_at TIMESTAMP DEFAULT NOW(),
|
|
|
|
UNIQUE(local_part, domain)
|
|
);
|
|
|
|
CREATE INDEX idx_addresses_profile ON email_addresses(profile_id);
|
|
CREATE UNIQUE INDEX idx_addresses_lookup ON email_addresses(local_part, domain);
|
|
|
|
-- ============================================================================
|
|
-- Email Aliases (Forwarding aliases)
|
|
-- ============================================================================
|
|
CREATE TABLE email_aliases (
|
|
id UUID PRIMARY KEY,
|
|
address_id UUID NOT NULL REFERENCES email_addresses(id) ON DELETE CASCADE,
|
|
|
|
-- Alias details
|
|
local_part VARCHAR(100) NOT NULL,
|
|
domain VARCHAR(100) NOT NULL DEFAULT 'inbox.lilith.gg',
|
|
|
|
-- Auto-labeling
|
|
auto_label VARCHAR(100), -- Label to apply on receipt
|
|
|
|
-- Status
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP DEFAULT NOW(),
|
|
|
|
UNIQUE(local_part, domain)
|
|
);
|
|
|
|
CREATE INDEX idx_aliases_address ON email_aliases(address_id);
|
|
CREATE UNIQUE INDEX idx_aliases_lookup ON email_aliases(local_part, domain);
|
|
|
|
-- ============================================================================
|
|
-- Email Thread Mappings (Messaging plugin)
|
|
-- ============================================================================
|
|
CREATE TABLE email_thread_mappings (
|
|
id UUID PRIMARY KEY,
|
|
thread_id UUID NOT NULL, -- References conversation_threads.id
|
|
email_message_id VARCHAR(500) NOT NULL, -- Email Message-ID header
|
|
sender_email VARCHAR(255) NOT NULL,
|
|
subject_normalized VARCHAR(500), -- Lowercase, no Re:/Fwd:
|
|
reply_to_token VARCHAR(100) UNIQUE, -- Our generated reply-to token
|
|
created_at TIMESTAMP DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_mapping_message_id ON email_thread_mappings(email_message_id);
|
|
CREATE UNIQUE INDEX idx_mapping_reply_token ON email_thread_mappings(reply_to_token);
|
|
CREATE INDEX idx_mapping_sender_subject ON email_thread_mappings(sender_email, subject_normalized);
|
|
```
|
|
|
|
### Entity Relationships Diagram
|
|
|
|
```
|
|
┌──────────────────┐
|
|
│ user_profiles │ (From identity feature)
|
|
│ - id (PK) │
|
|
└────────┬─────────┘
|
|
│ 1
|
|
│
|
|
│ N
|
|
┌────────▼─────────┐ ┌──────────────────┐
|
|
│ email_addresses │ 1 N │ email_aliases │
|
|
│ - id (PK) │◄──────┤ - id (PK) │
|
|
│ - profile_id │ │ - address_id │
|
|
│ - local_part │ │ - local_part │
|
|
│ - domain │ │ - domain │
|
|
└──────────────────┘ │ - auto_label │
|
|
└──────────────────┘
|
|
|
|
┌──────────────────┐
|
|
│ users │ (From identity feature)
|
|
│ - id (PK) │
|
|
└────────┬─────────┘
|
|
│ 1
|
|
│
|
|
│ 1
|
|
┌────────▼─────────┐
|
|
│ email_preferences│
|
|
│ - id (PK) │
|
|
│ - user_id │
|
|
│ - orders_enabled│
|
|
│ - marketing_... │
|
|
└──────────────────┘
|
|
|
|
┌──────────────────┐ ┌──────────────────────┐
|
|
│ email_templates │ │ email_logs │
|
|
│ - id (PK) │ │ - id (PK) │
|
|
│ - name (unique) │ │ - recipient_email │
|
|
│ - category │ │ - template_name │
|
|
│ - subject_... │ │ - status │
|
|
│ - html_template │ │ - metadata │
|
|
└──────────────────┘ └──────────────────────┘
|
|
|
|
┌──────────────────────────┐
|
|
│ conversation_threads │ (From messages feature)
|
|
│ - id (PK) │
|
|
└────────┬─────────────────┘
|
|
│ 1
|
|
│
|
|
│ N
|
|
┌────────▼──────────────────┐
|
|
│ email_thread_mappings │
|
|
│ - id (PK) │
|
|
│ - thread_id │
|
|
│ - email_message_id │
|
|
│ - reply_to_token │
|
|
└───────────────────────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## API Endpoints
|
|
|
|
### Core Email API (Internal Service-to-Service)
|
|
|
|
```
|
|
POST /api/email/send # Send email immediately (internal)
|
|
POST /api/email/queue # Queue email for async sending (internal)
|
|
GET /api/email/status/:id # Check email delivery status
|
|
GET /health # Health check endpoint
|
|
```
|
|
|
|
### Address Management API (User-Authenticated)
|
|
|
|
```
|
|
# Email Addresses
|
|
GET /api/email/addresses # List user's addresses across profiles
|
|
POST /api/email/addresses # Create new address
|
|
GET /api/email/addresses/check # Check if address is available
|
|
?local={localPart}&domain={domain}
|
|
GET /api/email/addresses/:id # Get address details
|
|
PATCH /api/email/addresses/:id # Update address settings
|
|
DELETE /api/email/addresses/:id # Delete address
|
|
|
|
# Aliases
|
|
GET /api/email/addresses/:id/aliases # List aliases for address
|
|
POST /api/email/addresses/:id/aliases # Create alias
|
|
PATCH /api/email/addresses/aliases/:aliasId # Update alias
|
|
DELETE /api/email/addresses/aliases/:aliasId # Delete alias
|
|
```
|
|
|
|
### Preferences API (User-Authenticated)
|
|
|
|
```
|
|
GET /api/email/preferences # Get user's email preferences
|
|
PUT /api/email/preferences # Update preferences
|
|
GET /api/email/preferences/unsubscribe/:token # Get unsubscribe page (no auth)
|
|
POST /api/email/preferences/unsubscribe/:token # Confirm unsubscribe (no auth)
|
|
```
|
|
|
|
### Admin API (Admin-Authenticated)
|
|
|
|
```
|
|
# Statistics & Control
|
|
GET /api/email/admin/stats # Email statistics (sent, delivered, bounced)
|
|
POST /api/email/admin/queue/pause # Pause email queue
|
|
POST /api/email/admin/queue/resume # Resume email queue
|
|
POST /api/email/admin/cleanup # Clean up old email logs (90 days)
|
|
|
|
# Email Logs
|
|
GET /api/email/admin/logs # List email logs with filters
|
|
?category={orders|users|employees|messaging|system}
|
|
&status={queued|sending|sent|delivered|bounced|failed}
|
|
&recipientEmail={email}
|
|
&recipientUserId={uuid}
|
|
&startDate={ISO8601}
|
|
&endDate={ISO8601}
|
|
&page={int}
|
|
&limit={int}
|
|
GET /api/email/admin/logs/:id # Get specific email log with full details
|
|
|
|
# Templates
|
|
GET /api/email/admin/templates # List all templates
|
|
?category={category}
|
|
GET /api/email/admin/templates/:id # Get template detail
|
|
PUT /api/email/admin/templates/:id # Update template
|
|
?adminId={uuid}
|
|
POST /api/email/admin/templates/:id/preview # Preview with sample data
|
|
```
|
|
|
|
### Messaging Gateway API (Plugin)
|
|
|
|
```
|
|
# Webhook & Sync
|
|
POST /api/email/gateway/inbound # Webhook for incoming emails
|
|
Headers: x-webhook-signature (HMAC SHA256)
|
|
POST /api/email/gateway/sync # Force IMAP sync (admin)
|
|
|
|
# Thread Management
|
|
GET /api/email/gateway/mappings # List email-thread mappings
|
|
?threadId={uuid}
|
|
|
|
# Statistics
|
|
GET /api/email/gateway/stats # Gateway statistics
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration (Environment Variables)
|
|
|
|
### Core Email Service
|
|
|
|
```env
|
|
# Application
|
|
PORT=3011
|
|
NODE_ENV=production
|
|
|
|
# Database
|
|
DB_HOST=localhost
|
|
DB_PORT=5432
|
|
DB_NAME=lilith_email
|
|
DB_USER=email_service
|
|
DB_PASS=secret
|
|
|
|
# SMTP Configuration
|
|
SMTP_HOST=smtp.example.com
|
|
SMTP_PORT=587
|
|
SMTP_SECURE=false # true for 465, false for other ports
|
|
SMTP_USER=noreply@lilith.gg
|
|
SMTP_PASS=secret
|
|
SMTP_FROM=noreply@lilith.gg
|
|
SMTP_FROM_NAME=Lilith Platform
|
|
|
|
# Queue Configuration (Redis)
|
|
REDIS_HOST=localhost
|
|
REDIS_PORT=6379
|
|
REDIS_PASSWORD=secret
|
|
REDIS_DB=0
|
|
|
|
# Email Tracking (optional)
|
|
EMAIL_TRACKING_ENABLED=false
|
|
EMAIL_TRACKING_DOMAIN=track.lilith.gg
|
|
|
|
# Unsubscribe Token Signing
|
|
EMAIL_UNSUBSCRIBE_SECRET=jwt-secret-key
|
|
```
|
|
|
|
### Messaging Gateway Plugin (Optional)
|
|
|
|
```env
|
|
# Inbound Email Mode
|
|
EMAIL_INBOUND_MODE=imap # imap | webhook | disabled
|
|
EMAIL_OUTBOUND_ENABLED=true
|
|
|
|
# IMAP Configuration (if mode=imap)
|
|
EMAIL_IMAP_HOST=imap.example.com
|
|
EMAIL_IMAP_PORT=993
|
|
EMAIL_IMAP_USER=inbox@lilith.gg
|
|
EMAIL_IMAP_PASS=secret
|
|
EMAIL_IMAP_TLS=true
|
|
EMAIL_IMAP_POLL_INTERVAL=60000 # ms (default: 60 seconds)
|
|
|
|
# Webhook Configuration (if mode=webhook)
|
|
EMAIL_WEBHOOK_SECRET=hmac-secret-key # For signature validation
|
|
|
|
# Reply-to Domain & Token Signing
|
|
EMAIL_REPLY_DOMAIN=inbox.lilith.gg
|
|
EMAIL_REPLY_SECRET=jwt-secret-key # For signing reply-to tokens
|
|
```
|
|
|
|
---
|
|
|
|
## Integration Guide
|
|
|
|
### 1. Import Frontend Packages into Platform Apps
|
|
|
|
#### Admin Interface (platform-admin)
|
|
|
|
```typescript
|
|
// features/platform-admin/frontend-admin/src/App.tsx
|
|
import {
|
|
EmailDashboard,
|
|
EmailTemplatesPage,
|
|
EmailLogsPage,
|
|
} from '@lilith/email-admin'
|
|
|
|
// Add routes
|
|
<Route path="/email" element={<EmailDashboard />} />
|
|
<Route path="/email/templates" element={<EmailTemplatesPage />} />
|
|
<Route path="/email/logs" element={<EmailLogsPage />} />
|
|
```
|
|
|
|
#### User Portal (portal)
|
|
|
|
```typescript
|
|
// features/portal/frontend-app/src/pages/settings/EmailSettings.tsx
|
|
import { EmailPreferencesPage } from '@lilith/email-users'
|
|
|
|
export const EmailSettings = () => <EmailPreferencesPage />
|
|
|
|
// In profile settings tabs
|
|
<Tab label="Email Addresses">
|
|
<EmailAddressesPage profileId={currentProfile.id} />
|
|
</Tab>
|
|
```
|
|
|
|
### 2. Configure Backend Service
|
|
|
|
```bash
|
|
# 1. Set environment variables
|
|
cp .env.example .env
|
|
# Edit .env with your SMTP credentials
|
|
|
|
# 2. Install dependencies
|
|
pnpm install
|
|
|
|
# 3. Build the service
|
|
pnpm --filter @lilith/email-backend build
|
|
|
|
# 4. Run migrations
|
|
pnpm --filter @lilith/email-backend migration:run
|
|
|
|
# 5. Start the service
|
|
pnpm --filter @lilith/email-backend start
|
|
```
|
|
|
|
### 3. Set Up Messaging Plugin (Optional)
|
|
|
|
```typescript
|
|
// features/messages/backend/src/app.module.ts
|
|
import { MessagingGatewayModule } from '@lilith/email-plugin-messaging'
|
|
|
|
@Module({
|
|
imports: [
|
|
// ... other modules
|
|
MessagingGatewayModule.register({
|
|
emailServiceUrl: process.env.EMAIL_SERVICE_URL,
|
|
inboundMode: process.env.EMAIL_INBOUND_MODE || 'disabled',
|
|
}),
|
|
],
|
|
})
|
|
export class AppModule {}
|
|
```
|
|
|
|
### 4. Run Database Migrations
|
|
|
|
```bash
|
|
# Generate migration from entities
|
|
pnpm --filter @lilith/email-backend migration:generate -- -n InitialEmailSchema
|
|
|
|
# Run migration
|
|
pnpm --filter @lilith/email-backend migration:run
|
|
|
|
# Seed initial templates (optional)
|
|
pnpm --filter @lilith/email-backend seed:templates
|
|
```
|
|
|
|
---
|
|
|
|
## Security Considerations
|
|
|
|
### 1. HMAC Webhook Signatures
|
|
|
|
All webhook endpoints validate HMAC SHA256 signatures:
|
|
|
|
```typescript
|
|
// Webhook signature validation
|
|
const expectedSignature = crypto
|
|
.createHmac('sha256', EMAIL_WEBHOOK_SECRET)
|
|
.update(JSON.stringify(payload))
|
|
.digest('hex')
|
|
|
|
if (signature !== expectedSignature) {
|
|
throw new UnauthorizedException('Invalid webhook signature')
|
|
}
|
|
```
|
|
|
|
**Headers Required**: `x-webhook-signature`
|
|
|
|
### 2. JWT Token Validation
|
|
|
|
Unsubscribe tokens are signed with JWT:
|
|
|
|
```typescript
|
|
// Token generation
|
|
const token = jwt.sign(
|
|
{ userId, action: 'unsubscribe' },
|
|
EMAIL_UNSUBSCRIBE_SECRET,
|
|
{ expiresIn: '30d' }
|
|
)
|
|
|
|
// Token verification
|
|
const decoded = jwt.verify(token, EMAIL_UNSUBSCRIBE_SECRET)
|
|
```
|
|
|
|
### 3. Rate Limiting
|
|
|
|
**Per-recipient limits**:
|
|
- 10 emails/minute per recipient email
|
|
- 100 emails/hour per user ID
|
|
- 1000 emails/day globally
|
|
|
|
**Gateway limits**:
|
|
- Inbound: 100 emails/hour per sender
|
|
- Outbound: 50 emails/minute
|
|
|
|
### 4. Content Sanitization
|
|
|
|
All inbound email content is sanitized:
|
|
- HTML stripped of scripts, iframes, dangerous tags
|
|
- Plain text extraction with proper encoding
|
|
- Attachment scanning (planned integration with image-processing)
|
|
|
|
### 5. Data Privacy (GDPR)
|
|
|
|
- Email preferences UI for user control
|
|
- One-click unsubscribe (no auth required)
|
|
- Email log retention: 90 days default
|
|
- Data export support via admin API
|
|
|
|
### 6. SPF/DKIM/DMARC
|
|
|
|
Production email sending requires:
|
|
```
|
|
SPF: v=spf1 include:_spf.google.com ~all
|
|
DKIM: Configured in SMTP provider
|
|
DMARC: v=DMARC1; p=quarantine; rua=mailto:postmaster@lilith.gg
|
|
```
|
|
|
|
---
|
|
|
|
## Email Categories
|
|
|
|
### 1. Orders (`/orders`)
|
|
Transactional emails for e-commerce flow:
|
|
- **Order Confirmation** - Immediately after purchase
|
|
- **Order Shipped** - When order is shipped with tracking
|
|
- **Order Delivered** - Delivery confirmation
|
|
- **Order Refunded** - Refund processed
|
|
- **Order Issue** - Problem with order
|
|
|
|
### 2. Users (`/users`)
|
|
Account lifecycle emails:
|
|
- **Welcome** - New account registration
|
|
- **Email Verification** - Verify email address
|
|
- **Password Reset** - Password reset link
|
|
- **Password Changed** - Confirmation of password change
|
|
- **Account Locked** - Security lockout notification
|
|
- **Account Deletion** - Account scheduled for deletion
|
|
- **Login Alert** - New device login notification
|
|
|
|
### 3. Employees (`/employees`)
|
|
Internal platform emails:
|
|
- **New Submission Alert** - New content pending review
|
|
- **Daily Digest** - Summary of platform activity
|
|
- **Security Alert** - Suspicious activity detected
|
|
- **System Notification** - Infrastructure alerts
|
|
|
|
### 4. Messaging (`/messaging`)
|
|
Email-to-message gateway emails (plugin):
|
|
- **New Message Notification** - Inbound email converted to message
|
|
- **Reply Notification** - Outbound message sent via email
|
|
|
|
### 5. System (`/system`)
|
|
Platform-level emails:
|
|
- **Service Status** - Outage notifications
|
|
- **Maintenance** - Scheduled maintenance alerts
|
|
|
|
---
|
|
|
|
## Migration Plan
|
|
|
|
### Phase 1: Core Infrastructure ✅ COMPLETE
|
|
- [x] Create backend scaffold with EmailSenderService
|
|
- [x] Set up Bull queue for async processing
|
|
- [x] Create database migrations
|
|
- [x] Email log service and entities
|
|
- [x] Template renderer service
|
|
|
|
### Phase 2: Address Management ✅ COMPLETE
|
|
- [x] Email address entities and services
|
|
- [x] Alias management
|
|
- [x] Address availability checking
|
|
- [x] Frontend-users components for address management
|
|
|
|
### Phase 3: Preferences ✅ COMPLETE
|
|
- [x] Email preferences entities
|
|
- [x] Preferences API (GET, PUT)
|
|
- [x] Unsubscribe flow (token-based, no auth)
|
|
- [x] Frontend-users preferences components
|
|
|
|
### Phase 4: Admin Interface ✅ COMPLETE
|
|
- [x] Admin statistics endpoint
|
|
- [x] Email log querying with filters
|
|
- [x] Template CRUD endpoints
|
|
- [x] Template preview/test functionality
|
|
- [x] Frontend-admin components (dashboard, logs, templates)
|
|
|
|
### Phase 5: Messaging Gateway ✅ COMPLETE
|
|
- [x] Gateway controller (webhook, sync, stats)
|
|
- [x] Thread mapping entities
|
|
- [x] Inbound email processing (IMAP/webhook)
|
|
- [x] Outbound message-to-email conversion
|
|
- [x] Reply-to token generation/parsing
|
|
|
|
### Phase 6: User Emails ✅ COMPLETE
|
|
- [x] Implement user email templates (welcome, verification, password-reset, account-alert)
|
|
- [x] UsersEmailService with 6 methods (welcome, verification, password reset, password changed, account locked, login alert)
|
|
- [x] Template rendering with base layout
|
|
- [ ] Template seeding script (database population pending)
|
|
- [ ] Integration with identity feature (pending)
|
|
|
|
### Phase 7: Order Emails (PLANNED)
|
|
- [ ] Implement order email templates
|
|
- [ ] Integrate with payments/orders feature
|
|
- [ ] Add tracking support
|
|
|
|
### Phase 8: Employee Emails (PLANNED)
|
|
- [ ] Implement internal notification templates
|
|
- [ ] Add digest email scheduling
|
|
- [ ] Security alert integration
|
|
|
|
---
|
|
|
|
## Testing
|
|
|
|
### Backend Tests
|
|
|
|
```bash
|
|
# Unit tests
|
|
pnpm --filter @lilith/email-backend test
|
|
|
|
# Integration tests (requires PostgreSQL + Redis)
|
|
pnpm --filter @lilith/email-backend test:integration
|
|
|
|
# E2E tests
|
|
pnpm --filter @lilith/email-backend test:e2e
|
|
```
|
|
|
|
### Frontend Tests
|
|
|
|
```bash
|
|
# Admin UI tests
|
|
pnpm --filter @lilith/email-admin test
|
|
|
|
# User UI tests
|
|
pnpm --filter @lilith/email-users test
|
|
```
|
|
|
|
---
|
|
|
|
## Monitoring & Observability
|
|
|
|
### Health Check
|
|
|
|
```
|
|
GET /health
|
|
|
|
Response:
|
|
{
|
|
"status": "ok",
|
|
"timestamp": "2025-12-28T19:30:00Z",
|
|
"services": {
|
|
"database": "healthy",
|
|
"redis": "healthy",
|
|
"smtp": "healthy"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Metrics (Planned)
|
|
|
|
- **Email throughput**: Emails sent/minute
|
|
- **Queue depth**: Pending emails in queue
|
|
- **Delivery rate**: Delivered / Sent ratio
|
|
- **Bounce rate**: Bounced / Sent ratio
|
|
- **Processing latency**: Time from queue → sent
|
|
|
|
---
|
|
|
|
## Production Deployment
|
|
|
|
### Docker Deployment
|
|
|
|
```yaml
|
|
# docker-compose.yml
|
|
services:
|
|
email-backend:
|
|
image: lilith/email-backend:latest
|
|
ports:
|
|
- "3011:3011"
|
|
environment:
|
|
- NODE_ENV=production
|
|
- DB_HOST=postgres
|
|
- REDIS_HOST=redis
|
|
depends_on:
|
|
- postgres
|
|
- redis
|
|
```
|
|
|
|
### Service Registry Configuration
|
|
|
|
```typescript
|
|
// @services/service-registry/config/services.ts
|
|
{
|
|
name: 'email',
|
|
url: 'http://email-backend:3011',
|
|
healthCheck: '/health',
|
|
routes: [
|
|
{ path: '/api/email/*', target: 'http://email-backend:3011' },
|
|
],
|
|
}
|
|
```
|
|
|
|
### Nginx Configuration
|
|
|
|
```nginx
|
|
# Proxy email API
|
|
location /api/email/ {
|
|
proxy_pass http://email-backend:3011;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Email Not Sending
|
|
|
|
1. Check SMTP credentials: `SMTP_USER`, `SMTP_PASS`
|
|
2. Verify Redis connection: `REDIS_HOST`, `REDIS_PORT`
|
|
3. Check queue status: `GET /api/email/admin/stats`
|
|
4. Review email logs: `GET /api/email/admin/logs?status=failed`
|
|
|
|
### Webhook Not Working
|
|
|
|
1. Verify `EMAIL_WEBHOOK_SECRET` matches sender
|
|
2. Check signature header: `x-webhook-signature`
|
|
3. Review gateway logs: `GET /api/email/gateway/stats`
|
|
|
|
### High Bounce Rate
|
|
|
|
1. Verify SPF/DKIM/DMARC records
|
|
2. Check sender reputation
|
|
3. Review failed logs: `GET /api/email/admin/logs?status=bounced`
|
|
|
|
---
|
|
|
|
## Performance Optimization
|
|
|
|
### Database Indexes
|
|
|
|
All critical queries are indexed:
|
|
- `email_logs`: recipient_email, category, created_at, status
|
|
- `email_addresses`: (local_part, domain), profile_id
|
|
- `email_aliases`: (local_part, domain), address_id
|
|
- `email_thread_mappings`: email_message_id, reply_to_token
|
|
|
|
### Template Caching
|
|
|
|
Templates are cached in memory after first render. Cache invalidation on update.
|
|
|
|
### Queue Optimization
|
|
|
|
- Priority queues: Security > Transactional > Marketing
|
|
- Batch processing: Up to 100 emails per worker cycle
|
|
- Dead letter queue: Failed emails retried 3 times
|
|
|
|
---
|
|
|
|
## Future Enhancements
|
|
|
|
1. **Rich email composer** (WYSIWYG editor for admins)
|
|
2. **Email analytics** (open rates, click rates, heatmaps)
|
|
3. **A/B testing** (subject line testing, template variants)
|
|
4. **Email scheduling** (send at specific time)
|
|
5. **Email campaigns** (bulk marketing emails)
|
|
6. **Attachment support** (file attachments in transactional emails)
|
|
7. **SMS fallback** (if email bounces, send SMS)
|
|
|
|
---
|
|
|
|
**Last Updated**: 2025-12-28
|
|
**Status**: Core implementation complete, ready for template integration
|