portfolio-page/backend/routes/contact.js

94 lines
2.5 KiB
JavaScript

import express from 'express';
import rateLimit from 'express-rate-limit';
import { body, validationResult } from 'express-validator';
import { sendContactEmail } from '../services/emailService.js';
const router = express.Router();
// GDPR compliant rate limiting
const contactLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 3, // Limit each IP to 3 requests per windowMs
message: {
error: 'Too many contact requests from this IP, please try again later.',
retryAfter: 15 * 60,
},
standardHeaders: true,
legacyHeaders: false,
});
// GDPR compliant validation
const validateContactForm = [
body('firstName').trim().isLength({ min: 1, max: 50 }).escape(),
body('lastName').trim().isLength({ min: 1, max: 50 }).escape(),
body('email').trim().isEmail().normalizeEmail().isLength({ max: 254 }),
body('subject').trim().isLength({ min: 1, max: 100 }).escape(),
body('message').trim().isLength({ min: 10, max: 2000 }).escape(),
body('gdprConsent').equals('true').withMessage('GDPR consent required'),
body('website').optional().isEmpty(), // Honeypot
];
router.post('/', contactLimiter, validateContactForm, async (req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
message: 'Invalid form data.',
errors: errors.array(),
});
}
const {
firstName,
lastName,
email,
subject,
message,
gdprConsent,
website,
} = req.body;
// Honeypot check
if (website) {
console.warn('🚨 Spam detected:', req.ip);
return res.status(400).json({
success: false,
message: 'Invalid submission detected.',
});
}
// GDPR consent check
if (gdprConsent !== 'true') {
return res.status(400).json({
success: false,
message: 'GDPR consent required.',
});
}
// Send email with minimal logging for GDPR
await sendContactEmail({
firstName,
lastName,
email,
subject,
message,
senderIP: req.ip,
userAgent: req.get('User-Agent'),
});
res.json({
success: true,
message: "Thank you for your message! I'll get back to you soon.",
});
} catch (error) {
console.error('❌ Contact error:', error.message); // No personal data in logs
res.status(500).json({
success: false,
message: 'Failed to send message. Please try again later.',
});
}
});
export { router as contactRouter };