feat(backend): implement gestionnaire creation with email notification (#17)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
MARTIN Julien 2026-02-23 22:46:30 +01:00
parent 135c7c2255
commit d0b730c8ab
4 changed files with 131 additions and 2 deletions

View File

@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { MailService } from './mail.service';
import { AppConfigModule } from '../config/config.module';
@Module({
imports: [AppConfigModule],
providers: [MailService],
exports: [MailService],
})
export class MailModule {}

View File

@ -0,0 +1,100 @@
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 é 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 é 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 é envoyé automatiquement. Merci de ne pas y répondre.
</p>
</div>
`;
await this.sendEmail(to, subject, html);
}
}

View File

@ -4,11 +4,13 @@ import { GestionnairesController } from './gestionnaires.controller';
import { Users } from 'src/entities/users.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthModule } from 'src/routes/auth/auth.module';
import { MailModule } from 'src/modules/mail/mail.module';
@Module({
imports: [
TypeOrmModule.forFeature([Users]),
AuthModule,
MailModule,
],
controllers: [GestionnairesController],
providers: [GestionnairesService],

View File

@ -9,12 +9,14 @@ import { RoleType, Users } from 'src/entities/users.entity';
import { CreateGestionnaireDto } from '../dto/create_gestionnaire.dto';
import { UpdateGestionnaireDto } from '../dto/update_gestionnaire.dto';
import * as bcrypt from 'bcrypt';
import { MailService } from 'src/modules/mail/mail.service';
@Injectable()
export class GestionnairesService {
constructor(
@InjectRepository(Users)
private readonly gestionnaireRepository: Repository<Users>,
private readonly mailService: MailService,
) { }
// Création dun gestionnaire
@ -39,11 +41,26 @@ export class GestionnairesService {
date_consentement_photo: dto.date_consentement_photo
? new Date(dto.date_consentement_photo)
: undefined,
changement_mdp_obligatoire: dto.changement_mdp_obligatoire ?? false,
changement_mdp_obligatoire: true, // Forcé à true pour les nouveaux gestionnaires
role: RoleType.GESTIONNAIRE,
relaisId: dto.relaisId,
});
return this.gestionnaireRepository.save(entity);
const savedUser = await this.gestionnaireRepository.save(entity);
// Envoi de l'email de bienvenue
try {
await this.mailService.sendGestionnaireWelcomeEmail(
savedUser.email,
savedUser.prenom || '',
savedUser.nom || '',
);
} catch (error) {
// On ne bloque pas la création si l'envoi d'email échoue, mais on log l'erreur
console.error('Erreur lors de l\'envoi de l\'email de bienvenue au gestionnaire', error);
}
return savedUser;
}
// Liste des gestionnaires