feat: block inactive accounts and send deactivation email
- 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 <noreply@anthropic.com>
This commit is contained in:
parent
d4a8ff261d
commit
69c410a4c7
|
|
@ -101,7 +101,7 @@ export const login = asyncHandler(async (req, res, next) => {
|
||||||
|
|
||||||
// Récupérer l'utilisateur
|
// Récupérer l'utilisateur
|
||||||
const result = await pool.query(
|
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]
|
[email]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -118,10 +118,10 @@ export const login = asyncHandler(async (req, res, next) => {
|
||||||
return next(new AppError('Email ou mot de passe incorrect', 401));
|
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é
|
// Vérifier si le compte est actif
|
||||||
// if (!user.is_verified) {
|
if (user.is_active === false) {
|
||||||
// return next(new AppError('Veuillez vérifier votre email avant de vous connecter', 403));
|
return next(new AppError('Ce compte a été désactivé. Contactez le support pour le réactiver.', 403));
|
||||||
// }
|
}
|
||||||
|
|
||||||
// Générer le JWT
|
// Générer le JWT
|
||||||
const token = generateJWT(user.id, user.email, user.role);
|
const token = generateJWT(user.id, user.email, user.role);
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ export const googleLogin = asyncHandler(async (req, res) => {
|
||||||
const insertResult = await pool.query(
|
const insertResult = await pool.query(
|
||||||
`INSERT INTO users (email, first_name, last_name, password, role, is_verified)
|
`INSERT INTO users (email, first_name, last_name, password, role, is_verified)
|
||||||
VALUES ($1, $2, $3, $4, 'CLIENT', TRUE)
|
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]
|
[email, given_name || 'Utilisateur', family_name || '', hashedPassword]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -70,6 +70,14 @@ export const googleLogin = asyncHandler(async (req, res) => {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
user = userResult.rows[0];
|
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
|
// Générer un JWT
|
||||||
|
|
@ -153,7 +161,7 @@ export const facebookLogin = asyncHandler(async (req, res) => {
|
||||||
const insertResult = await pool.query(
|
const insertResult = await pool.query(
|
||||||
`INSERT INTO users (email, first_name, last_name, password, role, is_verified)
|
`INSERT INTO users (email, first_name, last_name, password, role, is_verified)
|
||||||
VALUES ($1, $2, $3, $4, 'CLIENT', TRUE)
|
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]
|
[email, first_name || 'Utilisateur', last_name || '', hashedPassword]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -168,6 +176,14 @@ export const facebookLogin = asyncHandler(async (req, res) => {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
user = userResult.rows[0];
|
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
|
// Générer un JWT
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from 'bcrypt';
|
||||||
import { pool } from '../../db.js';
|
import { pool } from '../../db.js';
|
||||||
import { AppError, asyncHandler } from '../middleware/errorHandler.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é
|
* 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++}`);
|
updates.push(`postal_code = $${paramCount++}`);
|
||||||
values.push(postalCode);
|
values.push(postalCode);
|
||||||
}
|
}
|
||||||
|
// Vérifier si on désactive le compte
|
||||||
|
const isDeactivating = isActive === false;
|
||||||
|
|
||||||
if (isActive !== undefined) {
|
if (isActive !== undefined) {
|
||||||
updates.push(`is_active = $${paramCount++}`);
|
updates.push(`is_active = $${paramCount++}`);
|
||||||
values.push(isActive);
|
values.push(isActive);
|
||||||
|
|
@ -104,9 +107,19 @@ export const updateProfile = asyncHandler(async (req, res) => {
|
||||||
const result = await pool.query(query, values);
|
const result = await pool.query(query, values);
|
||||||
const user = result.rows[0];
|
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({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Profil mis à jour avec succès',
|
message: isDeactivating ? 'Compte désactivé avec succès' : 'Profil mis à jour avec succès',
|
||||||
data: {
|
data: {
|
||||||
id: user.id,
|
id: user.id,
|
||||||
email: user.email,
|
email: user.email,
|
||||||
|
|
|
||||||
|
|
@ -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 = `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
|
||||||
|
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
||||||
|
.header { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); color: white; padding: 30px; text-align: center; border-radius: 10px 10px 0 0; }
|
||||||
|
.content { background: #f9f9f9; padding: 30px; border-radius: 0 0 10px 10px; }
|
||||||
|
.footer { text-align: center; margin-top: 20px; color: #666; font-size: 12px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>⏸️ Compte désactivé</h1>
|
||||||
|
</div>
|
||||||
|
<div class="content">
|
||||||
|
<h2>Bonjour ${firstName},</h2>
|
||||||
|
<p>Nous vous confirmons que votre compte Thé Tip Top a bien été <strong>désactivé</strong>.</p>
|
||||||
|
|
||||||
|
<p>Vos données sont conservées et vous pouvez réactiver votre compte à tout moment en contactant notre support.</p>
|
||||||
|
|
||||||
|
<div style="background: #fff3cd; border: 1px solid #ffc107; border-radius: 8px; padding: 15px; margin: 20px 0;">
|
||||||
|
<p style="margin: 0; color: #856404;">
|
||||||
|
<strong>💡 Besoin de réactiver votre compte ?</strong><br>
|
||||||
|
Contactez-nous à <a href="mailto:thetiptopgr3@gmail.com">thetiptopgr3@gmail.com</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p>Merci d'avoir participé au jeu-concours Thé Tip Top!</p>
|
||||||
|
|
||||||
|
<p style="margin-top: 30px; color: #666; font-size: 14px;">
|
||||||
|
Si vous n'êtes pas à l'origine de cette demande, veuillez nous contacter immédiatement.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="footer">
|
||||||
|
<p>© 2025 Thé Tip Top - Tous droits réservés</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
|
||||||
|
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
|
* Envoie un email au gagnant du tirage au sort
|
||||||
*/
|
*/
|
||||||
|
|
@ -565,5 +638,6 @@ export default {
|
||||||
sendContactEmail,
|
sendContactEmail,
|
||||||
sendWelcomeEmail,
|
sendWelcomeEmail,
|
||||||
sendAccountDeletedEmail,
|
sendAccountDeletedEmail,
|
||||||
|
sendAccountDeactivatedEmail,
|
||||||
sendDrawWinnerEmail,
|
sendDrawWinnerEmail,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user