138 lines
6.2 KiB
TypeScript
138 lines
6.2 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
||
import { AppConfigService } from '../config/config.service';
|
||
|
||
@Injectable()
|
||
export class MailService {
|
||
private readonly logger = new Logger(MailService.name);
|
||
|
||
constructor(private readonly configService: AppConfigService) {}
|
||
|
||
/**
|
||
* Envoi d'un email générique
|
||
* @param to Destinataire
|
||
* @param subject Sujet
|
||
* @param html Contenu HTML
|
||
* @param text Contenu texte (optionnel)
|
||
*/
|
||
async sendEmail(to: string, subject: string, html: string, text?: string): Promise<void> {
|
||
try {
|
||
// Récupération de la configuration SMTP
|
||
const smtpHost = this.configService.get<string>('smtp_host');
|
||
const smtpPort = this.configService.get<number>('smtp_port');
|
||
const smtpSecure = this.configService.get<boolean>('smtp_secure');
|
||
const smtpAuthRequired = this.configService.get<boolean>('smtp_auth_required');
|
||
const smtpUser = this.configService.get<string>('smtp_user');
|
||
const smtpPassword = this.configService.get<string>('smtp_password');
|
||
const emailFromName = this.configService.get<string>('email_from_name');
|
||
const emailFromAddress = this.configService.get<string>('email_from_address');
|
||
|
||
// Import dynamique de nodemailer
|
||
const nodemailer = await import('nodemailer');
|
||
|
||
// Configuration du transporteur
|
||
const transportConfig: any = {
|
||
host: smtpHost,
|
||
port: smtpPort,
|
||
secure: smtpSecure,
|
||
};
|
||
|
||
if (smtpAuthRequired && smtpUser && smtpPassword) {
|
||
transportConfig.auth = {
|
||
user: smtpUser,
|
||
pass: smtpPassword,
|
||
};
|
||
}
|
||
|
||
const transporter = nodemailer.createTransport(transportConfig);
|
||
|
||
// Envoi de l'email
|
||
await transporter.sendMail({
|
||
from: `"${emailFromName}" <${emailFromAddress}>`,
|
||
to,
|
||
subject,
|
||
text: text || html.replace(/<[^>]*>?/gm, ''), // Fallback texte simple
|
||
html,
|
||
});
|
||
|
||
this.logger.log(`📧 Email envoyé à ${to} : ${subject}`);
|
||
} catch (error) {
|
||
this.logger.error(`❌ Erreur lors de l'envoi de l'email à ${to}`, error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Envoi de l'email de bienvenue pour un gestionnaire
|
||
* @param to Email du gestionnaire
|
||
* @param prenom Prénom
|
||
* @param nom Nom
|
||
* @param token Token de création de mot de passe (si applicable) ou mot de passe temporaire (si applicable)
|
||
* @note Pour l'instant, on suppose que le gestionnaire doit définir son mot de passe via "Mot de passe oublié" ou un lien d'activation
|
||
* Mais le ticket #17 parle de "Flag changement_mdp_obligatoire = TRUE", ce qui implique qu'on lui donne un mot de passe temporaire ou qu'on lui envoie un lien.
|
||
* Le ticket #24 parle de "API Création mot de passe" via token.
|
||
* Pour le ticket #17, on crée le gestionnaire avec un mot de passe (hashé).
|
||
* Si on suit le ticket #35 (Frontend), on saisit un mot de passe.
|
||
* Donc on envoie juste un email de confirmation de création de compte.
|
||
*/
|
||
async sendGestionnaireWelcomeEmail(to: string, prenom: string, nom: string): Promise<void> {
|
||
const appName = this.configService.get<string>('app_name', 'P\'titsPas');
|
||
const appUrl = this.configService.get<string>('app_url', 'https://app.ptits-pas.fr');
|
||
|
||
const subject = `Bienvenue sur ${appName}`;
|
||
const html = `
|
||
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
|
||
<h2 style="color: #4CAF50;">Bienvenue ${prenom} ${nom} !</h2>
|
||
<p>Votre compte gestionnaire sur <strong>${appName}</strong> a été créé avec succès.</p>
|
||
<p>Vous pouvez dès à présent vous connecter avec l'adresse email <strong>${to}</strong> et le mot de passe qui vous a été communiqué.</p>
|
||
<p>Lors de votre première connexion, il vous sera demandé de modifier votre mot de passe pour des raisons de sécurité.</p>
|
||
<div style="text-align: center; margin: 30px 0;">
|
||
<a href="${appUrl}" style="background-color: #4CAF50; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;">Accéder à l'application</a>
|
||
</div>
|
||
<hr style="border: 1px solid #eee; margin: 20px 0;">
|
||
<p style="color: #666; font-size: 12px;">
|
||
Cet email a été envoyé automatiquement. Merci de ne pas y répondre.
|
||
</p>
|
||
</div>
|
||
`;
|
||
|
||
await this.sendEmail(to, subject, html);
|
||
}
|
||
|
||
/**
|
||
* Email de refus de dossier avec lien reprise (token).
|
||
* Ticket #110 – Refus sans suppression
|
||
*/
|
||
async sendRefusEmail(
|
||
to: string,
|
||
prenom: string,
|
||
nom: string,
|
||
comment: string | undefined,
|
||
token: string,
|
||
): Promise<void> {
|
||
const appName = this.configService.get<string>('app_name', "P'titsPas");
|
||
const appUrl = this.configService.get<string>('app_url', 'https://app.ptits-pas.fr');
|
||
const repriseLink = `${appUrl}/reprise?token=${encodeURIComponent(token)}`;
|
||
|
||
const subject = `Votre dossier – compléments demandés`;
|
||
const commentBlock = comment
|
||
? `<p><strong>Message du gestionnaire :</strong></p><p>${comment.replace(/</g, '<').replace(/>/g, '>')}</p>`
|
||
: '';
|
||
const html = `
|
||
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
|
||
<h2 style="color: #333;">Bonjour ${prenom} ${nom},</h2>
|
||
<p>Votre dossier d'inscription sur <strong>${appName}</strong> n'a pas pu être validé en l'état.</p>
|
||
${commentBlock}
|
||
<p>Vous pouvez corriger les éléments indiqués et soumettre à nouveau votre dossier en cliquant sur le lien ci-dessous.</p>
|
||
<div style="text-align: center; margin: 30px 0;">
|
||
<a href="${repriseLink}" style="background-color: #2196F3; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; font-weight: bold;">Reprendre mon dossier</a>
|
||
</div>
|
||
<p style="color: #666; font-size: 12px;">Ce lien est valable 7 jours. Si vous n'avez pas demandé cette reprise, vous pouvez ignorer cet email.</p>
|
||
<hr style="border: 1px solid #eee; margin: 20px 0;">
|
||
<p style="color: #666; font-size: 12px;">Cet email a été envoyé automatiquement. Merci de ne pas y répondre.</p>
|
||
</div>
|
||
`;
|
||
|
||
await this.sendEmail(to, subject, html);
|
||
}
|
||
}
|