From cae3c892beb20aa88bf3a6405f2d5ffdc9120635 Mon Sep 17 00:00:00 2001 From: dereklseitz Date: Sat, 23 Aug 2025 00:27:01 -0500 Subject: [PATCH] chore: add example env file, improve documentation and code comments --- README.md | 1 + controllers/contactController.js | 3 --- example.env | 27 +++++++++++++++++++++++++++ middleware/securityMw.js | 5 ++--- routes/contactRoutes.js | 11 +++-------- server.js | 13 ++----------- 6 files changed, 35 insertions(+), 25 deletions(-) create mode 100644 example.env diff --git a/README.md b/README.md index 8d44c73..def2a1a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # dlseitz.dev: A Backend Demonstration +To learn about the front end of this two-part project, check out [**dlseitz.dev – A Frontend Demonstration**](https://git.dsnet.pro/dereklseitz/dlseitz.dev-frontend/src/branch/main/README.md). --- ## Table of Contents diff --git a/controllers/contactController.js b/controllers/contactController.js index 863c4c2..e5dacca 100644 --- a/controllers/contactController.js +++ b/controllers/contactController.js @@ -1,12 +1,10 @@ // contactController.js module.exports = (pool, transporter) => { - // The main function that handles the form submission const submitForm = async (req, res) => { const { firstName, lastName, organization, email, phone, contactMethod, message } = req.body; try { - // 1. Save submission to the database const result = await pool.query( `INSERT INTO submissions(first_name, last_name, organization, email, phone, contact_method, message) VALUES($1, $2, $3, $4, $5, $6, $7) RETURNING *`, @@ -15,7 +13,6 @@ module.exports = (pool, transporter) => { console.log('Successfully saved submission to the database:', result.rows[0]); - // 2. Send the email notification const mailOptions = { from: `"Contact Form" `, to: process.env.EMAIL_RCPT, diff --git a/example.env b/example.env new file mode 100644 index 0000000..a889774 --- /dev/null +++ b/example.env @@ -0,0 +1,27 @@ +# Example environment variables for dlseitz.dev backend +# DO NOT PUT REAL CREDENTIALS IN HERE +# DO NOT COMMIT ACTUAL .env FILES TO VERSION CONTROL + +# Database-related +#----------------- +DB_HOST=your_database_host +DB_PORT=5432 +DB_USER=your_database_user +DB_PASSWORD=your_database_password +DB_NAME=your_database_name + +# Email-related +#-------------- +EMAIL_HOST=smtp.yourmail.com +EMAIL_PORT=587 +EMAIL_USER=your_email_username +EMAIL_PASS=your_email_password +EMAIL_RCPT=recipient@example.com + +# Security-related +#----------------- +HCAPTCHA_SECRET=your_hcaptcha_secret_key + +# Other +#------ +NODE_ENV=production \ No newline at end of file diff --git a/middleware/securityMw.js b/middleware/securityMw.js index ee611ab..8378472 100644 --- a/middleware/securityMw.js +++ b/middleware/securityMw.js @@ -4,13 +4,13 @@ const fetch = require('node-fetch'); module.exports = { formSecurityCheck: async (req, res, next) => { - // 1. Honeypot check (first line of defense) + // 1. Honeypot check if (req.body.url) { console.warn('Bot detected! Honeypot field was filled.'); return res.status(200).json({ success: true, message: 'Thank you for your submission.' }); } - // 2. hCaptcha verification (second line of defense) + // 2. hCaptcha verification const hCaptchaResponse = req.body.hCaptchaResponse; if (!hCaptchaResponse) { return res.status(400).json({ success: false, message: 'CAPTCHA token missing.' }); @@ -36,7 +36,6 @@ module.exports = { return res.status(400).json({ success: false, message: 'CAPTCHA verification failed. Please try again.' }); } - // If all checks pass, move to the next middleware or controller next(); } catch (error) { diff --git a/routes/contactRoutes.js b/routes/contactRoutes.js index 62f24c4..fe0f7be 100644 --- a/routes/contactRoutes.js +++ b/routes/contactRoutes.js @@ -5,21 +5,18 @@ module.exports = (contactController, securityMw) => { const rateLimit = require('express-rate-limit'); const { body, validationResult } = require('express-validator'); - // 🛡️ Configure rate limiting to prevent DDoS and spamming + // Configure rate limiting to prevent spam const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 5, message: "Too many requests from this IP, please try again after 15 minutes." }); - // Define the route for form submissions with all middleware router.post('/submit-form', apiLimiter, - // The security middleware is now a separate step, - // containing both the honeypot check and hCaptcha verification. securityMw.formSecurityCheck, [ - // express-validator: sanitation and validation + // Sanitize and validate form data body('firstName').trim().escape(), body('lastName').trim().escape(), body('email').isEmail().normalizeEmail(), @@ -27,7 +24,7 @@ module.exports = (contactController, securityMw) => { body('phone').trim(), body('message').trim().escape(), ], - // Middleware to handle the express-validator results + // Handle validation results (req, res, next) => { const errors = validationResult(req); if (!errors.isEmpty()) { @@ -36,10 +33,8 @@ module.exports = (contactController, securityMw) => { } next(); }, - // The controller, which is the final step contactController.submitForm ); - // Return the configured router return router; }; diff --git a/server.js b/server.js index c057e8b..4a7e989 100644 --- a/server.js +++ b/server.js @@ -8,20 +8,17 @@ require('dotenv').config(); const app = express(); const port = process.env.SERVER_PORT || 3000; -// add logging for troubleshooting app.use((req, res, next) => { console.log(`Incoming request: ${req.method} ${req.url}`); next(); }); -// Middleware to parse incoming JSON data from the frontend app.use(express.json()); -// Middleware to serve static files (like index.html, styles.css, script.js) const STATIC_DIR = process.env.STATIC_DIR || 'public' app.use(express.static(path.join(__dirname, STATIC_DIR))); -// Database connection pool setup using environment variables for security +// Setup database connection pool using environment variables const pool = new Pool({ user: process.env.DB_USER, host: process.env.DB_HOST, @@ -30,7 +27,7 @@ const pool = new Pool({ port: process.env.DB_PORT, }); -// Nodemailer transporter setup for sending emails +// Configure Nodemailer for sending emails via Brevo relay const transporter = nodemailer.createTransport({ host: process.env.EMAIL_HOST, port: 2525, @@ -43,17 +40,11 @@ const transporter = nodemailer.createTransport({ }); const contactController = require('./controllers/contactController')(pool, transporter); - -// Import the security middleware const securityMw = require('./middleware/securityMw'); - -// Import contactRoutes and contactController, and pass in securityMw const contactRoutes = require('./routes/contactRoutes')(contactController, securityMw); -// Use contactRoutes to connect the modular router to the main app app.use('/api', contactRoutes); -// Start the server app.listen(port, () => { console.log(`Server listening at http://localhost:${port}`); }); \ No newline at end of file