Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | /** * Route Contact — Formulaire de contact public. * * POST /contact — Envoie un email a l'equipe IliaCloud. * * Pas d'authentification requise (formulaire public). * Rate limite pour eviter le spam. * L'adresse de destination est en variable d'environnement (pas dans le code). * * @module routes/contact */ const express = require('express'); const rateLimit = require('express-rate-limit'); const { sendEmail } = require('../services/email'); const router = express.Router(); // Rate limit : 3 messages par IP par heure const contactLimiter = rateLimit({ windowMs: 60 * 60 * 1000, max: 3, message: { error: 'Trop de messages envoyes. Reessayez dans une heure.' }, }); // Validation email — quantificateurs bornes pour eviter tout backtracking excessif (ReDoS-safe) const EMAIL_REGEX = /^[^\s@]{1,64}@[^\s@]{1,253}\.[^\s@]{1,63}$/; // ─── POST /contact ────────────────────────────────────────────────────────── router.post('/', contactLimiter, async (req, res, next) => { try { const { name, email, subject, message } = req.body; // Validation if (!name || typeof name !== 'string' || name.trim().length === 0) { return res.status(400).json({ error: 'Nom requis.' }); } if (name.length > 100) { return res.status(400).json({ error: 'Nom trop long (max 100 caracteres).' }); } if (!email || !EMAIL_REGEX.test(email)) { return res.status(400).json({ error: 'Email invalide.' }); } if (email.length > 254) { return res.status(400).json({ error: 'Email trop long.' }); } if (!message || typeof message !== 'string' || message.trim().length === 0) { return res.status(400).json({ error: 'Message requis.' }); } if (message.length > 5000) { return res.status(400).json({ error: 'Message trop long (max 5000 caracteres).' }); } if (subject && subject.length > 200) { return res.status(400).json({ error: 'Sujet trop long (max 200 caracteres).' }); } // Anti-spam basique : honeypot (champ invisible dans le formulaire) if (req.body.website) { // Bot qui remplit le champ invisible — on simule un succes sans envoyer return res.json({ message: 'Message envoye.' }); } const contactEmail = process.env.CONTACT_EMAIL || process.env.SMTP_USER; const subjectLine = subject ? `[Contact IliaCloud] ${subject.trim()}` : `[Contact IliaCloud] Message de ${name.trim()}`; const html = ` <div style="font-family: -apple-system, sans-serif; max-width: 600px;"> <h2 style="color: #6366f1;">Nouveau message de contact</h2> <table style="width: 100%; border-collapse: collapse; margin: 16px 0;"> <tr><td style="padding: 8px 0; color: #64748b; width: 80px;">Nom</td><td style="padding: 8px 0; font-weight: 600;">${escapeHtml(name.trim())}</td></tr> <tr><td style="padding: 8px 0; color: #64748b;">Email</td><td style="padding: 8px 0;"><a href="mailto:${escapeHtml(email)}">${escapeHtml(email)}</a></td></tr> ${subject ? `<tr><td style="padding: 8px 0; color: #64748b;">Sujet</td><td style="padding: 8px 0;">${escapeHtml(subject.trim())}</td></tr>` : ''} </table> <div style="background: #f8fafc; border-radius: 8px; padding: 16px; margin-top: 16px; white-space: pre-wrap; line-height: 1.6;"> ${escapeHtml(message.trim())} </div> <p style="color: #94a3b8; font-size: 12px; margin-top: 24px;"> Envoye depuis le formulaire de contact IliaCloud — IP: ${req.ip} </p> </div> `; const sent = await sendEmail({ to: contactEmail, subject: subjectLine, html, text: `Nom: ${name.trim()}\nEmail: ${email}\nSujet: ${subject || '-'}\n\n${message.trim()}`, }); if (!sent) { return res.status(503).json({ error: 'Service email temporairement indisponible.' }); } res.json({ message: 'Message envoye. Nous vous repondrons rapidement.' }); } catch (err) { next(err); } }); /** * Echappe le HTML pour eviter les injections XSS dans le template email. */ function escapeHtml(str) { return str.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"'); } module.exports = router; |