Complete CRM foundation with contact modal in footer Backend: - Contact.model.js: Full CRUD model with statistics tracking - contact.controller.js: Submit, list, assign, respond, update, delete - contact.routes.js: Public submission + admin management endpoints - routes/index.js: Mount contact routes at /api/contact Frontend: - footer.js: Replace mailto link with Contact Us modal button - Contact modal: Form with type, name, email, org, subject, message - CSRF protection: Extracts token from cookie (like newsletter) - Rate limiting: formRateLimiter (5/min) - Validation: Input sanitization + required fields - UX: Success/error messages, auto-close on success Admin UI: - navbar-admin.js: New 'CRM & Communications' section - Links: Contact Management, Case Submissions, Media Inquiries Foundation for multi-project CRM across tractatus, family-history, sydigital Next: Build /admin/contact-management.html page
95 lines
2.9 KiB
JavaScript
95 lines
2.9 KiB
JavaScript
/**
|
|
* Contact Routes
|
|
* Public contact form and admin management
|
|
*/
|
|
|
|
const express = require('express');
|
|
const router = express.Router();
|
|
|
|
const contactController = require('../controllers/contact.controller');
|
|
const { authenticateToken, requireRole } = require('../middleware/auth.middleware');
|
|
const { validateRequired } = require('../middleware/validation.middleware');
|
|
const { asyncHandler } = require('../middleware/error.middleware');
|
|
const { createInputValidationMiddleware } = require('../middleware/input-validation.middleware');
|
|
const { formRateLimiter } = require('../middleware/rate-limit.middleware');
|
|
const { csrfProtection } = require('../middleware/csrf-protection.middleware');
|
|
|
|
/**
|
|
* Public Routes
|
|
*/
|
|
|
|
// Validation schema for contact submission
|
|
const contactSubmitSchema = {
|
|
'type': { required: false, type: 'string', maxLength: 50 },
|
|
'name': { required: true, type: 'name', maxLength: 100 },
|
|
'email': { required: true, type: 'email', maxLength: 254 },
|
|
'organization': { required: false, type: 'string', maxLength: 200 },
|
|
'phone': { required: false, type: 'phone', maxLength: 50 },
|
|
'subject': { required: false, type: 'string', maxLength: 200 },
|
|
'message': { required: true, type: 'string', maxLength: 5000 }
|
|
};
|
|
|
|
// POST /api/contact/submit - Submit contact form
|
|
router.post('/submit',
|
|
formRateLimiter, // 5 requests per minute
|
|
csrfProtection, // CSRF validation
|
|
createInputValidationMiddleware(contactSubmitSchema),
|
|
validateRequired(['name', 'email', 'message']),
|
|
asyncHandler(contactController.submit)
|
|
);
|
|
|
|
/**
|
|
* Admin Routes (require authentication)
|
|
*/
|
|
|
|
// GET /api/contact/admin/stats - Get contact statistics
|
|
router.get('/admin/stats',
|
|
authenticateToken,
|
|
requireRole('admin', 'moderator'),
|
|
asyncHandler(contactController.getStats)
|
|
);
|
|
|
|
// GET /api/contact/admin/list - List contacts with filtering
|
|
router.get('/admin/list',
|
|
authenticateToken,
|
|
requireRole('admin', 'moderator'),
|
|
asyncHandler(contactController.list)
|
|
);
|
|
|
|
// GET /api/contact/admin/:id - Get single contact
|
|
router.get('/admin/:id',
|
|
authenticateToken,
|
|
requireRole('admin', 'moderator'),
|
|
asyncHandler(contactController.getById)
|
|
);
|
|
|
|
// POST /api/contact/admin/:id/assign - Assign contact to user
|
|
router.post('/admin/:id/assign',
|
|
authenticateToken,
|
|
requireRole('admin', 'moderator'),
|
|
asyncHandler(contactController.assign)
|
|
);
|
|
|
|
// POST /api/contact/admin/:id/respond - Mark as responded
|
|
router.post('/admin/:id/respond',
|
|
authenticateToken,
|
|
requireRole('admin', 'moderator'),
|
|
validateRequired(['content']),
|
|
asyncHandler(contactController.respond)
|
|
);
|
|
|
|
// PUT /api/contact/admin/:id - Update contact
|
|
router.put('/admin/:id',
|
|
authenticateToken,
|
|
requireRole('admin', 'moderator'),
|
|
asyncHandler(contactController.update)
|
|
);
|
|
|
|
// DELETE /api/contact/admin/:id - Delete contact
|
|
router.delete('/admin/:id',
|
|
authenticateToken,
|
|
requireRole('admin'),
|
|
asyncHandler(contactController.deleteContact)
|
|
);
|
|
|
|
module.exports = router;
|