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:
parent
de9e4cd337
commit
2eddd7aa1a
3
.env
3
.env
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user