From 69c410a4c787b160d4647116fe8b5b95e478b333 Mon Sep 17 00:00:00 2001 From: soufiane Date: Tue, 2 Dec 2025 19:55:19 +0100 Subject: [PATCH] feat: block inactive accounts and send deactivation email MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Block login for inactive accounts (isActive=false) - Block Google OAuth login for inactive accounts - Block Facebook OAuth login for inactive accounts - Send deactivation email when account is archived - Add sendAccountDeactivatedEmail function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/controllers/auth.controller.js | 10 ++-- src/controllers/oauth.controller.js | 20 +++++++- src/controllers/user.controller.js | 17 ++++++- src/services/email.service.js | 74 +++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 9 deletions(-) diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index 5ccad508..04e216b0 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -101,7 +101,7 @@ export const login = asyncHandler(async (req, res, next) => { // Récupérer l'utilisateur const result = await pool.query( - 'SELECT id, email, password, first_name, last_name, role, is_verified FROM users WHERE email = $1', + 'SELECT id, email, password, first_name, last_name, role, is_verified, is_active FROM users WHERE email = $1', [email] ); @@ -118,10 +118,10 @@ export const login = asyncHandler(async (req, res, next) => { return next(new AppError('Email ou mot de passe incorrect', 401)); } - // Note: Vous pouvez choisir de bloquer la connexion si l'email n'est pas vérifié - // if (!user.is_verified) { - // return next(new AppError('Veuillez vérifier votre email avant de vous connecter', 403)); - // } + // Vérifier si le compte est actif + if (user.is_active === false) { + return next(new AppError('Ce compte a été désactivé. Contactez le support pour le réactiver.', 403)); + } // Générer le JWT const token = generateJWT(user.id, user.email, user.role); diff --git a/src/controllers/oauth.controller.js b/src/controllers/oauth.controller.js index 3bdcb50a..54d7f268 100644 --- a/src/controllers/oauth.controller.js +++ b/src/controllers/oauth.controller.js @@ -55,7 +55,7 @@ export const googleLogin = asyncHandler(async (req, res) => { const insertResult = await pool.query( `INSERT INTO users (email, first_name, last_name, password, role, is_verified) VALUES ($1, $2, $3, $4, 'CLIENT', TRUE) - RETURNING id, email, first_name, last_name, role, is_verified, created_at`, + RETURNING id, email, first_name, last_name, role, is_verified, is_active, created_at`, [email, given_name || 'Utilisateur', family_name || '', hashedPassword] ); @@ -70,6 +70,14 @@ export const googleLogin = asyncHandler(async (req, res) => { } } else { user = userResult.rows[0]; + + // Vérifier si le compte est actif + if (user.is_active === false) { + return res.status(403).json({ + success: false, + message: 'Ce compte a été désactivé. Contactez le support pour le réactiver.', + }); + } } // Générer un JWT @@ -153,7 +161,7 @@ export const facebookLogin = asyncHandler(async (req, res) => { const insertResult = await pool.query( `INSERT INTO users (email, first_name, last_name, password, role, is_verified) VALUES ($1, $2, $3, $4, 'CLIENT', TRUE) - RETURNING id, email, first_name, last_name, role, is_verified, created_at`, + RETURNING id, email, first_name, last_name, role, is_verified, is_active, created_at`, [email, first_name || 'Utilisateur', last_name || '', hashedPassword] ); @@ -168,6 +176,14 @@ export const facebookLogin = asyncHandler(async (req, res) => { } } else { user = userResult.rows[0]; + + // Vérifier si le compte est actif + if (user.is_active === false) { + return res.status(403).json({ + success: false, + message: 'Ce compte a été désactivé. Contactez le support pour le réactiver.', + }); + } } // Générer un JWT diff --git a/src/controllers/user.controller.js b/src/controllers/user.controller.js index d50416a2..5bba843a 100644 --- a/src/controllers/user.controller.js +++ b/src/controllers/user.controller.js @@ -4,7 +4,7 @@ import bcrypt from 'bcrypt'; import { pool } from '../../db.js'; import { AppError, asyncHandler } from '../middleware/errorHandler.js'; -import { sendAccountDeletedEmail } from '../services/email.service.js'; +import { sendAccountDeletedEmail, sendAccountDeactivatedEmail } from '../services/email.service.js'; /** * Récupérer le profil de l'utilisateur connecté @@ -78,6 +78,9 @@ export const updateProfile = asyncHandler(async (req, res) => { updates.push(`postal_code = $${paramCount++}`); values.push(postalCode); } + // Vérifier si on désactive le compte + const isDeactivating = isActive === false; + if (isActive !== undefined) { updates.push(`is_active = $${paramCount++}`); values.push(isActive); @@ -104,9 +107,19 @@ export const updateProfile = asyncHandler(async (req, res) => { const result = await pool.query(query, values); const user = result.rows[0]; + // Envoyer l'email si le compte vient d'être désactivé + if (isDeactivating) { + try { + await sendAccountDeactivatedEmail(user.email, user.first_name); + console.log('📧 Email de désactivation envoyé à', user.email); + } catch (error) { + console.error('Erreur envoi email de désactivation:', error); + } + } + res.json({ success: true, - message: 'Profil mis à jour avec succès', + message: isDeactivating ? 'Compte désactivé avec succès' : 'Profil mis à jour avec succès', data: { id: user.id, email: user.email, diff --git a/src/services/email.service.js b/src/services/email.service.js index bb71f379..ac25a6e7 100644 --- a/src/services/email.service.js +++ b/src/services/email.service.js @@ -476,6 +476,79 @@ export const sendAccountDeletedEmail = async (email, firstName) => { }); }; +/** + * Envoie un email de confirmation de désactivation de compte + */ +export const sendAccountDeactivatedEmail = async (email, firstName) => { + const html = ` + + + + + + +
+
+

⏸️ Compte désactivé

+
+
+

Bonjour ${firstName},

+

Nous vous confirmons que votre compte Thé Tip Top a bien été désactivé.

+ +

Vos données sont conservées et vous pouvez réactiver votre compte à tout moment en contactant notre support.

+ +
+

+ 💡 Besoin de réactiver votre compte ?
+ Contactez-nous Ă  thetiptopgr3@gmail.com +

+
+ +

Merci d'avoir participé au jeu-concours Thé Tip Top!

+ +

+ Si vous n'êtes pas à l'origine de cette demande, veuillez nous contacter immédiatement. +

+
+ +
+ + + `; + + const text = ` + Compte désactivé + + Bonjour ${firstName}, + + Nous vous confirmons que votre compte Thé Tip Top a bien été désactivé. + + Vos données sont conservées et vous pouvez réactiver votre compte à tout moment en contactant notre support. + + Besoin de réactiver votre compte ? + Contactez-nous à thetiptopgr3@gmail.com + + Merci d'avoir participé au jeu-concours Thé Tip Top! + + Si vous n'êtes pas à l'origine de cette demande, veuillez nous contacter immédiatement. + `; + + return sendEmail({ + to: email, + subject: '⏸️ Votre compte Thé Tip Top a été désactivé', + html, + text, + }); +}; + /** * Envoie un email au gagnant du tirage au sort */ @@ -565,5 +638,6 @@ export default { sendContactEmail, sendWelcomeEmail, sendAccountDeletedEmail, + sendAccountDeactivatedEmail, sendDrawWinnerEmail, };