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 { try { // Récupération de la configuration SMTP const smtpHost = this.configService.get('smtp_host'); const smtpPort = this.configService.get('smtp_port'); const smtpSecure = this.configService.get('smtp_secure'); const smtpAuthRequired = this.configService.get('smtp_auth_required'); const smtpUser = this.configService.get('smtp_user'); const smtpPassword = this.configService.get('smtp_password'); const emailFromName = this.configService.get('email_from_name'); const emailFromAddress = this.configService.get('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 { const appName = this.configService.get('app_name', 'P\'titsPas'); const appUrl = this.configService.get('app_url', 'https://app.ptits-pas.fr'); const subject = `Bienvenue sur ${appName}`; const html = `

Bienvenue ${prenom} ${nom} !

Votre compte gestionnaire sur ${appName} a été créé avec succès.

Vous pouvez dès à présent vous connecter avec l'adresse email ${to} et le mot de passe qui vous a été communiqué.

Lors de votre première connexion, il vous sera demandé de modifier votre mot de passe pour des raisons de sécurité.


Cet email a été envoyé automatiquement. Merci de ne pas y répondre.

`; 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 { const appName = this.configService.get('app_name', "P'titsPas"); const appUrl = this.configService.get('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 ? `

Message du gestionnaire :

${comment.replace(//g, '>')}

` : ''; const html = `

Bonjour ${prenom} ${nom},

Votre dossier d'inscription sur ${appName} n'a pas pu être validé en l'état.

${commentBlock}

Vous pouvez corriger les éléments indiqués et soumettre à nouveau votre dossier en cliquant sur le lien ci-dessous.

Ce lien est valable 7 jours. Si vous n'avez pas demandé cette reprise, vous pouvez ignorer cet email.


Cet email a été envoyé automatiquement. Merci de ne pas y répondre.

`; await this.sendEmail(to, subject, html); } }