feat: add email check endpoint and fix email service

- Add POST /api/auth/check-email endpoint for email validation
- Check if email exists in database
- Validate email domain with MX DNS records
- Fix email service transporter lazy loading
- Add detailed logging for email sending
- Add FRONTEND_URL to .env for email links

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
soufiane 2025-12-02 16:16:09 +01:00
parent de9e4cd337
commit 2eddd7aa1a
4 changed files with 106 additions and 16 deletions

3
.env
View File

@ -14,6 +14,9 @@ FACEBOOK_APP_SECRET=e6889f4339d140c218f1df177149893f
JWT_SECRET=thetiptopsecret
SESSION_SECRET=thetiptopsessionsecret
# Frontend URL (pour les liens dans les emails)
FRONTEND_URL=http://localhost:3000
# Email Configuration (SMTP)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587

View File

@ -6,6 +6,10 @@ import { pool } from '../../db.js';
import { AppError, asyncHandler } from '../middleware/errorHandler.js';
import { generateToken, generateJWT, getTokenExpiry, isExpired } from '../utils/helpers.js';
import { sendResetPasswordEmail, sendWelcomeEmail } from '../services/email.service.js';
import dns from 'dns';
import { promisify } from 'util';
const resolveMx = promisify(dns.resolveMx);
/**
* Inscription d'un nouvel utilisateur
@ -280,6 +284,70 @@ export const getMe = asyncHandler(async (req, res, next) => {
});
});
/**
* Vérifier si un email existe et est valide
* POST /api/auth/check-email
*/
export const checkEmail = asyncHandler(async (req, res, next) => {
const { email } = req.body;
if (!email) {
return next(new AppError('Email requis', 400));
}
// Validation format email
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return res.json({
success: true,
isValid: false,
exists: false,
message: 'Format d\'email invalide',
details: {
formatValid: false,
domainValid: false,
mxRecords: false,
existsInDb: false
}
});
}
const domain = email.split('@')[1];
let mxValid = false;
let mxRecords = [];
// Vérification MX (enregistrements DNS du domaine)
try {
mxRecords = await resolveMx(domain);
mxValid = mxRecords && mxRecords.length > 0;
} catch (error) {
console.log(`❌ Pas d'enregistrements MX pour ${domain}:`, error.code);
mxValid = false;
}
// Vérifier si l'email existe déjà en BDD
const existingUser = await pool.query('SELECT id FROM users WHERE email = $1', [email.toLowerCase()]);
const existsInDb = existingUser.rows.length > 0;
// Résultat final
const isValid = mxValid && !existsInDb;
res.json({
success: true,
isValid,
exists: existsInDb,
message: existsInDb
? 'Cet email est déjà utilisé'
: (mxValid ? 'Email valide et disponible' : 'Le domaine de cet email n\'existe pas'),
details: {
formatValid: true,
domainValid: mxValid,
mxRecords: mxValid,
existsInDb
}
});
});
export default {
register,
login,
@ -288,4 +356,5 @@ export default {
resetPassword,
logout,
getMe,
checkEmail,
};

View File

@ -21,6 +21,7 @@ router.post('/login', validate(loginSchema), authController.login);
router.get('/verify-email/:token', authController.verifyEmail);
router.post('/forgot-password', validate(forgotPasswordSchema), authController.forgotPassword);
router.post('/reset-password', validate(resetPasswordSchema), authController.resetPassword);
router.post('/check-email', authController.checkEmail);
// Routes OAuth
router.post('/google', oauthController.googleLogin);

View File

@ -6,44 +6,59 @@ import dotenv from 'dotenv';
dotenv.config();
// Configuration du transporteur email
const createTransporter = () => {
// Configuration du transporteur email (créé à la demande pour éviter les problèmes de chargement)
let transporter = null;
const getTransporter = () => {
if (transporter) return transporter;
// Pour le développement, utilisez un service de test comme Ethereal
// Pour la production, utilisez un vrai service SMTP (Gmail, SendGrid, etc.)
if (process.env.NODE_ENV === 'development' && !process.env.SMTP_HOST) {
// Mode développement sans configuration SMTP
console.log('⚠️ Mode développement: Les emails seront affichés dans la console');
return null;
}
return nodemailer.createTransport({
if (!process.env.SMTP_HOST || !process.env.SMTP_USER || !process.env.SMTP_PASS) {
console.error('❌ Configuration SMTP manquante:', {
host: !!process.env.SMTP_HOST,
user: !!process.env.SMTP_USER,
pass: !!process.env.SMTP_PASS
});
return null;
}
console.log('📧 Création du transporteur SMTP:', process.env.SMTP_HOST);
transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: parseInt(process.env.SMTP_PORT) || 587,
secure: process.env.SMTP_PORT === '465', // true pour le port 465, false pour les autres
secure: process.env.SMTP_PORT === '465',
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASS,
},
tls: {
rejectUnauthorized: false // Ignore les erreurs de certificat SSL
rejectUnauthorized: false
}
});
};
const transporter = createTransporter();
return transporter;
};
/**
* Fonction générique pour envoyer un email
*/
const sendEmail = async ({ to, subject, html, text }) => {
try {
const emailTransporter = getTransporter();
// En mode dev sans SMTP, afficher l'email dans la console
if (!transporter) {
console.log('📧 Email qui aurait été envoyé:');
if (!emailTransporter) {
console.log('📧 Email qui aurait été envoyé (pas de transporteur):');
console.log('To:', to);
console.log('Subject:', subject);
console.log('Content:', text || html);
console.log('Content:', text ? text.substring(0, 200) : 'HTML content');
return { success: true, mode: 'dev' };
}
@ -55,12 +70,14 @@ const sendEmail = async ({ to, subject, html, text }) => {
html,
};
const info = await transporter.sendMail(mailOptions);
console.log('✅ Email envoyé:', info.messageId);
console.log('📤 Envoi email à:', to, '- Sujet:', subject);
const info = await emailTransporter.sendMail(mailOptions);
console.log('✅ Email envoyé avec succès:', info.messageId);
return { success: true, messageId: info.messageId };
} catch (error) {
console.error('❌ Erreur envoi email:', error);
throw new Error('Erreur lors de l\'envoi de l\'email');
console.error('❌ Erreur envoi email:', error.message);
console.error('❌ Stack:', error.stack);
throw new Error('Erreur lors de l\'envoi de l\'email: ' + error.message);
}
};