Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ PGADMIN_DEFAULT_PASSWORD=admin
PGADMIN_PORT=5050

# API
NODE_ENV=development # development | production
API_PORT=3000
BETTER_AUTH_SECRET=1G9ueZXVQSRINjcRri14LqXWDYkV3fOd
TRUSTED_ORIGINS=http://localhost:3000,http://localhost:5173
Expand All @@ -26,7 +27,12 @@ SEED_DB=false #Use in dockerfile in production
# CLIENT
VITE_API_URL=http://localhost:3000

# BREVO API KEY
# EMAIL - BREVO (Production)
BREVO_API_KEY=
BREVO_FROM_EMAIL=
BREVO_FROM_NAME=
BREVO_FROM_NAME=

# EMAIL - MAILDEV (Development)
MAILDEV_HOST=localhost
MAILDEV_SMTP_PORT=1025
MAILDEV_WEB_PORT=1080
11 changes: 9 additions & 2 deletions apps/api/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@ PGADMIN_DEFAULT_PASSWORD=admin
PGADMIN_PORT=5050

# API
NODE_ENV=development # development | production
API_PORT=3000
BETTER_AUTH_SECRET=1G9ueZXVQSRINjcRri14LqXWDYkV3fOd
TRUSTED_ORIGINS=http://localhost:3000,http://localhost:5173
SEED_DB=false #Use in dockerfile in production

# CLIENT
VITE_API_URL=http://localhost:3000

# BREVO API KEY
# EMAIL - BREVO (Production)
BREVO_API_KEY=
BREVO_FROM_EMAIL=
BREVO_FROM_NAME=
BREVO_FROM_NAME=

# EMAIL - MAILDEV (Development)
MAILDEV_HOST=localhost
MAILDEV_SMTP_PORT=1025
MAILDEV_WEB_PORT=1080
3 changes: 2 additions & 1 deletion apps/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
"better-auth-mikro-orm": "^0.3.0",
"dotenv": "^16.5.0",
"express": "^4.21.1",
"nodemailer": "^7.0.3",
"nodemailer": "^7.0.6",
"pg": "^8.15.6",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1",
Expand All @@ -73,6 +73,7 @@
"@types/express": "^5.0.0",
"@types/jest": "^29.5.14",
"@types/node": "^20.3.1",
"@types/nodemailer": "^7.0.4",
"@types/pg": "^8.11.14",
"@types/supertest": "^6.0.0",
"jest": "^29.7.0",
Expand Down
266 changes: 77 additions & 189 deletions apps/api/src/modules/core/email/README.md
Original file line number Diff line number Diff line change
@@ -1,206 +1,94 @@
# Email Module
# Module Email

This module handles email sending in the application.
Service d'envoi d'emails avec switch automatique entre développement (Maildev) et production (Brevo).

## Technologies Used
## Architecture

- [nodemailer](https://nodemailer.com/about/) - Email sending service

## Features

- Email sending service
- Support for transactional emails
- Email logging

## Configuration

Currently, the module is configured to log emails rather than actually sending them. This is useful for development and testing.

## Sending Interface

```typescript
interface EmailOptions {
to: string
subject: string
content: string
}
```
┌──────────────────────────────────────────────────────────────┐
│ AuthService │
│ (better-auth callbacks: sendResetPassword, etc.) │
└───────────────────────┬──────────────────────────────────────┘
│ appelle
┌──────────────────────────────────────────────────────────────┐
│ EmailService │
│ • sendEmail(data) ← Méthode générique │
│ • sendInvitationEmail(data) ← Logique métier + template │
└───────────────────────┬──────────────────────────────────────┘
│ utilise (via injection)
┌──────────────────────────────────────────────────────────────┐
│ IEmailService │
│ Interface: sendEmail(to, subject, htmlContent) │
└───────────────────────┬──────────────────────────────────────┘
┌──────────────┴──────────────┐
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ DevMailService │ │ BrevoService │
│ (Maildev SMTP) │ │ (Brevo API) │
│ Development │ │ Production │
└──────────────────┘ └──────────────────┘
```

## Intégration avec Nodemailer (pour la production)

Lorsque je serai prêt à envoyer des emails réels, je pourrai intégrer Nodemailer dans l'EmailService:

### 1. Configuration du service d'emails

Mettre à jour mon EmailService pour utiliser Nodemailer:

```typescript
import { Injectable } from '@nestjs/common';
import * as nodemailer from 'nodemailer';
import { ConfigService } from '@nestjs/config';

@Injectable()
export class EmailService {
private transporter: nodemailer.Transporter;

constructor(private configService: ConfigService) {
// Initialiser le transporteur Nodemailer
this.transporter = nodemailer.createTransport({
host: this.configService.get('EMAIL_HOST'),
port: this.configService.get('EMAIL_PORT'),
secure: this.configService.get('EMAIL_SECURE') === 'true',
auth: {
user: this.configService.get('EMAIL_USER'),
pass: this.configService.get('EMAIL_PASSWORD'),
},
});
}

async sendEmail({
to,
subject,
content,
}: {
to: string;
subject: string;
content: string;
}): Promise<void> {
// En environnement de développement, simplement logger les emails
if (this.configService.get('NODE_ENV') === 'development') {
console.log('Email sent (dev mode):', { to, subject, content });
return;
}

// En production, envoyer l'email via Nodemailer
await this.transporter.sendMail({
from: this.configService.get('EMAIL_FROM'),
to,
subject,
html: content,
});
}

// Méthodes spécifiques pour les emails transactionnels
async sendVerificationEmail(user: any, url: string, token: string): Promise<void> {
const verificationUrl = `${url}?token=${token}`;

await this.sendEmail({
to: user.email,
subject: 'Vérification de votre adresse email',
content: `
<h1>Vérification d'email</h1>
<p>Bonjour,</p>
<p>Merci de vous être inscrit. Veuillez vérifier votre adresse email.</p>
<p><a href="${verificationUrl}">Cliquez ici pour vérifier votre email</a></p>
`,
});
}
## Flux d'appel - Exemple d'invitation

async sendResetPasswordEmail(user: any, url: string, token: string): Promise<void> {
const resetUrl = `${url}?token=${token}`;

await this.sendEmail({
to: user.email,
subject: 'Réinitialisation de votre mot de passe',
content: `
<h1>Réinitialisation de mot de passe</h1>
<p>Bonjour,</p>
<p>Vous avez demandé une réinitialisation de mot de passe.</p>
<p><a href="${resetUrl}">Cliquez ici pour définir un nouveau mot de passe</a></p>
<p>Si vous n'avez pas fait cette demande, vous pouvez ignorer cet email.</p>
`,
});
}
}
```
1. AuthService.sendInvitationEmail(callback)
└──> 2. EmailService.sendInvitationEmail(data)
├──> Génère le HTML (template)
└──> 3. EmailService.sendEmail({ to, subject, content })
└──> 4. emailTransport.sendEmail(to, subject, content)
├──> [DEV] DevMailService → Maildev SMTP
└──> [PROD] BrevoService → Brevo API
```

### 3. Variables d'environnement nécessaires

Je dois ajouter ces variables à mon fichier `.env`:
## Configuration

```env
# Configuration email
EMAIL_HOST=smtp.example.com
EMAIL_PORT=587
EMAIL_SECURE=false
[email protected]
EMAIL_PASSWORD=password
[email protected]
### Développement (Maildev)
```bash
# .env
NODE_ENV=development
MAILDEV_HOST=localhost
MAILDEV_SMTP_PORT=1025
MAILDEV_WEB_PORT=1080
```

## Intégration avec better-auth
Démarrer Maildev : `docker-compose up maildev`
Interface web : http://localhost:1080

Pour intégrer ce module avec better-auth, je dois mettre à jour mon fichier `better-auth.config.ts`:
### Production (Brevo)
```bash
# .env
NODE_ENV=production
BREVO_API_KEY=votre_clé_api
[email protected]
BREVO_FROM_NAME=DropIt
```

```typescript
import { betterAuth, User } from 'better-auth';
import { createAuthMiddleware } from 'better-auth/api';
import { openAPI } from 'better-auth/plugins';
import { Pool } from 'pg';
import { config } from './env.config';
## Fichiers

// Interface pour les options dynamiques
interface BetterAuthOptionsDynamic {
emailService: {
sendResetPasswordEmail: (user: User, url: string, token: string) => Promise<void>;
sendVerificationEmail: (user: User, url: string, token: string) => Promise<void>;
};
}
| Fichier | Rôle |
|---------|------|
| `email.port.ts` | Interface `IEmailService` + token d'injection |
| `email.service.ts` | Service métier avec logique et templates |
| `brevo.service.ts` | Implémentation Brevo (production) |
| `dev-mail.service.ts` | Implémentation Maildev (développement) |
| `email.module.ts` | Factory qui choisit le bon service selon `NODE_ENV` |

export function createAuthConfig(options: BetterAuthOptionsDynamic) {
return betterAuth({
secret: config.betterAuth.secret,
trustedOrigins: config.betterAuth.trustedOrigins,
emailAndPassword: {
enabled: true,
sendResetPassword: async (data, request) => {
await options.emailService.sendResetPasswordEmail(
data.user,
data.url,
data.token
);
},
},
emailVerification: {
sendOnSignUp: true,
expiresIn: 60 * 60 * 24 * 10, // 10 jours
sendVerificationEmail: async (data, request) => {
await options.emailService.sendVerificationEmail(
data.user,
data.url,
data.token
);
},
},
database: new Pool({
connectionString: config.database.connectionString,
}),
advanced: {
generateId: false,
},
rateLimit: {
window: 50,
max: 100,
},
plugins: [openAPI()],
});
}
```
## Switch automatique

Et dans mon module d'authentification, je dois injecter le service d'email:
Le module switch automatiquement selon `NODE_ENV` :
- **development** → `DevMailService` (Maildev)
- **production** → `BrevoService` (Brevo)

```typescript
@Module({
imports: [EmailModule],
providers: [
{
provide: 'AUTH',
useFactory: (emailService: EmailService) => {
return createAuthConfig({ emailService });
},
inject: [EmailService],
},
],
exports: ['AUTH'],
})
export class AuthModule {}
Voir `email.module.ts:11-21` pour la logique de switch.
Loading