Skip to content

Commit

Permalink
Implement NodeJS code to enable contact form functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
alexbates committed Apr 5, 2024
1 parent 3536d19 commit 2d7ddb3
Show file tree
Hide file tree
Showing 39 changed files with 66,058 additions and 1 deletion.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# alexbates.github.io
My personal portfolio.

Browse by going to https://alexbates.github.io
Browse by going to https://alexbates.github.io

All client side files are located at both / and /public. The files at / are used for browsing at https://alexbates.github.io, while the files at /public are used for running with NodeJS.

NodeJS is required for contact form functionality.
73 changes: 73 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const express = require('express');
const app = express();
// Import path module into script,
const path = require('path');
// Import the nodemailer module into script, install with npm install nodemailer
const nodemailer = require('nodemailer');
// The port the node app runs on
const port = 3000;

// For parsing application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: true }));
// The public directory includes files the client has access to
app.use(express.static(path.join(__dirname, 'public')));

app.post('/submit-form', (req, res) => {
// Access form data from HTML form, including name, email, subject, and message
const name = req.body.name;
const email = req.body.email;
const subject = req.body.subject;
const message = req.body.text;
// A regular expression is used to validate email address
const regex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
// Form has already been validated client side
// Validate form again server side. If it validates, proceeed with sending of email with contact message
if(name !== '' && email !== '' && subject !== '' && message !== '' && regex.test(email)) {
// Create a transporter object using SMTP transport
let transporter = nodemailer.createTransport({
// Outgoing server for email account
host: 'mail.REDACTED.com',
// Custom port
port: 2525,
// Do not use SSL
secure: false,
auth: {
// Email account username
user: '[email protected]',
// Email account password
pass: 'REDACTEDPASSWORD'
}
});

// Define email options
let options = {
// Sender for email
from: '[email protected]',
// Recipient for email
to: '[email protected]',
// Subject for email
subject: subject,
// Body of email
text: `Name: ${name}\nEmail: ${email}\nMessage: ${message}`
};

// Send email using nodemailer
transporter.sendMail(options, (error, info) => {
if (error) {
// Send the contact form back to client, but with an error message at top of the form
res.sendFile(__dirname + '/public/contact/contact-error-email.html');
} else {
// Send the contact form back to client, but with a success message at top of the form
res.sendFile(__dirname + '/public/contact/contact-success.html');
}
});
}
else {
// Send the contact form back to client, but with an error message at top of the form
res.sendFile(__dirname + '/public/contact/contact-error-validate.html');
}
});

app.listen(port, () => {
console.log(`Server running on http://localhost:${port}`);
});
121 changes: 121 additions & 0 deletions contact/contact-error-email.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- Control size of viewport, prevent zooming -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Alex Bates - Contact</title>
<!-- Link a stylesheet that applies sitewide as well as one that is page specific -->
<link href="../stylesheets/global-ab.css" rel='stylesheet' type='text/css'>
<link href="../stylesheets/contact.css" rel='stylesheet' type='text/css'>
<!-- Import externally hosted Merriweather font family -->
<link href="https://fonts.googleapis.com/css2?family=Annapurna+SIL:wght@400;700&family=Fredoka:[email protected]&family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900&display=swap" rel="stylesheet">
<!-- Wait until DOM is loaded until loading scripts -->
<script defer src="../scripts/global.js"></script>
<script defer src="../scripts/contact.js"></script>
</head>
<body>

<header class="navbar">
<!-- Create a container that is centered on screens wider than 1200px -->
<div class="navbarinner">
<!-- Hamburger nav menu that only displays on mobile devices -->
<nav class="mobilemenu">
<!-- Create hidden checkbox that is used to open and close menu -->
<input id="hiddencheckbox" type="checkbox">
<label class="mobilemenubtn" for="hiddencheckbox">
<span></span>
</label>
<ul class="mobilemenubox">
<li><a class="mobilemenuitem" href="/">Home</a></li>
<li><a class="mobilemenuitem" href="/about/">About</a></li>
<li><a class="mobilemenuitem" href="/contact/">Contact</a></li>
<!-- Github link that opens in new tab when clicked -->
<li><a class="mobilemenuitem mobilegithub" href="https://github.com/alexbates/" target="_blank">
Github
<!-- Credits to Flowbite for the Link icon https://flowbite.com/icons/ -->
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.2 9.8a3.4 3.4 0 0 0-4.8 0L5 13.2A3.4 3.4 0 0 0 9.8 18l.3-.3m-.3-4.5a3.4 3.4 0 0 0 4.8 0L18 9.8A3.4 3.4 0 0 0 13.2 5l-1 1"/>
</svg>
</a></li>
</ul>
</nav>
<!-- Logo image that displays at left of navbar on desktop and center on mobile -->
<!-- Division is used instead of img since img requires an src attribute -->
<div class="logo"></div>
<div class="themeselector">
<!-- Create hidden checkbox that is used to switch between themes -->
<input type="checkbox" class="checkbox" id="checkbox">
<!-- Use a label that acts a button to toggle the checkbox -->
<label for="checkbox" class="checkbox-label">
<span class="ts-sun"></span>
<span class="ts-moon"></span>
<span class="ball"></span>
</label>
</div>
<!-- Site navigation that only appears on screens wider than 600px -->
<nav class="desktopnav">
<div class="github">
<a href="https://github.com/alexbates/" target="_blank">
<!-- Credits to Flowbite for the Github icon https://flowbite.com/icons/ -->
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M12 2c-2.4 0-4.7.9-6.5 2.4a10.5 10.5 0 0 0-2 13.1A10 10 0 0 0 8.7 22c.5 0 .7-.2.7-.5v-2c-2.8.7-3.4-1.1-3.4-1.1-.1-.6-.5-1.2-1-1.5-1-.7 0-.7 0-.7a2 2 0 0 1 1.5 1.1 2.2 2.2 0 0 0 1.3 1 2 2 0 0 0 1.6-.1c0-.6.3-1 .7-1.4-2.2-.3-4.6-1.2-4.6-5 0-1.1.4-2 1-2.8a4 4 0 0 1 .2-2.7s.8-.3 2.7 1c1.6-.5 3.4-.5 5 0 2-1.3 2.8-1 2.8-1 .3.8.4 1.8 0 2.7a4 4 0 0 1 1 2.7c0 4-2.3 4.8-4.5 5a2.5 2.5 0 0 1 .7 2v2.8c0 .3.2.6.7.5a10 10 0 0 0 5.4-4.4 10.5 10.5 0 0 0-2.1-13.2A9.8 9.8 0 0 0 12 2Z" clip-rule="evenodd"/>
</svg>
</a>
</div>
<!-- An unordered list contains links to other pages -->
<ul class="navlist">
<li><span><a href="/">Home</a></span></li><li><span><a href="/about/">About</a></span></li><li class="active"><span>Contact</span></li>
</ul>
</nav>
</div>
</header>
<main>
<!-- A fixed height division allows content to overflow, while still being contained within main -->
<div class="block1">
<!-- division has fixed width and is centered on screens wider than 1200px -->
<div class="maininner">
<!-- Appears on left side on desktop and top on mobile -->
<div class="mainleft">
<!-- Credits to Flowbite for the Mailbox icon https://flowbite.com/icons/ -->
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16v-5.5C11 8.5 9.4 7 7.5 7m3.5 9H4v-5.5C4 8.5 5.6 7 7.5 7m3.5 9v4M7.5 7H14m0 0V4h2.5M14 7v3m-3.5 6H20v-6a3 3 0 0 0-3-3m-2 9v4m-8-6.5h1"/>
</svg>
<h1>Reach Out</h1>
<h3>Use this form to send me an email.</h3>
</div>
<!-- Appears on right side on desktop and bottom on mobile -->
<div class="mainright">
<h2>Send Me a Message</h2>
<!-- Display error message if email fails to send server side -->
<p id="formError">Error: the email failed to send.</p>
<!-- Contact form labels describe the content of the inputs and text area -->
<!-- Form label spans are used for error messages and are empty except when validation fails -->
<!-- Form inputs, textarea, and spans use unique ids to enable manipulation with javascript -->
<form id="form" class="contactform" action="/submit-form" method="post">
<label for="name">Name <span id="errorname" class="errorspan"></span></label>
<input id="name" name="name" type="text">
<label for="email">Email <span id="erroremail" class="errorspan"></span></label>
<input id="email" name="email" type="text">
<label for="subject">Subject <span id="errorsubject" class="errorspan"></span></label>
<input id="subject" name="subject" type="text">
<label for="message"><span class="your">Your </span>Message <span id="errormessage" class="errorspan"></span></label>
<textarea rows="3" id="message" name="text"></textarea>
<!-- Form submit button consists of 'Send' text and icon contained within spans -->
<button type="submit">
<span class="btn-text">Send</span>
<span class="btn-icon">
<!-- Credits to Flowbite for the Paper Plane (send) icon https://flowbite.com/icons/ -->
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M12 2c.4 0 .8.3 1 .6l7 18a1 1 0 0 1-1.4 1.3L13 19.5V13a1 1 0 1 0-2 0v6.5L5.4 22A1 1 0 0 1 4 20.6l7-18a1 1 0 0 1 1-.6Z" clip-rule="evenodd"/>
</svg>
</span>
</button>
</form>
</div>
</div>
</div>
</main>

</body>
</html>
121 changes: 121 additions & 0 deletions contact/contact-error-validate.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- Control size of viewport, prevent zooming -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Alex Bates - Contact</title>
<!-- Link a stylesheet that applies sitewide as well as one that is page specific -->
<link href="../stylesheets/global-ab.css" rel='stylesheet' type='text/css'>
<link href="../stylesheets/contact.css" rel='stylesheet' type='text/css'>
<!-- Import externally hosted Merriweather font family -->
<link href="https://fonts.googleapis.com/css2?family=Annapurna+SIL:wght@400;700&family=Fredoka:[email protected]&family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900&display=swap" rel="stylesheet">
<!-- Wait until DOM is loaded until loading scripts -->
<script defer src="../scripts/global.js"></script>
<script defer src="../scripts/contact.js"></script>
</head>
<body>

<header class="navbar">
<!-- Create a container that is centered on screens wider than 1200px -->
<div class="navbarinner">
<!-- Hamburger nav menu that only displays on mobile devices -->
<nav class="mobilemenu">
<!-- Create hidden checkbox that is used to open and close menu -->
<input id="hiddencheckbox" type="checkbox">
<label class="mobilemenubtn" for="hiddencheckbox">
<span></span>
</label>
<ul class="mobilemenubox">
<li><a class="mobilemenuitem" href="/">Home</a></li>
<li><a class="mobilemenuitem" href="/about/">About</a></li>
<li><a class="mobilemenuitem" href="/contact/">Contact</a></li>
<!-- Github link that opens in new tab when clicked -->
<li><a class="mobilemenuitem mobilegithub" href="https://github.com/alexbates/" target="_blank">
Github
<!-- Credits to Flowbite for the Link icon https://flowbite.com/icons/ -->
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.2 9.8a3.4 3.4 0 0 0-4.8 0L5 13.2A3.4 3.4 0 0 0 9.8 18l.3-.3m-.3-4.5a3.4 3.4 0 0 0 4.8 0L18 9.8A3.4 3.4 0 0 0 13.2 5l-1 1"/>
</svg>
</a></li>
</ul>
</nav>
<!-- Logo image that displays at left of navbar on desktop and center on mobile -->
<!-- Division is used instead of img since img requires an src attribute -->
<div class="logo"></div>
<div class="themeselector">
<!-- Create hidden checkbox that is used to switch between themes -->
<input type="checkbox" class="checkbox" id="checkbox">
<!-- Use a label that acts a button to toggle the checkbox -->
<label for="checkbox" class="checkbox-label">
<span class="ts-sun"></span>
<span class="ts-moon"></span>
<span class="ball"></span>
</label>
</div>
<!-- Site navigation that only appears on screens wider than 600px -->
<nav class="desktopnav">
<div class="github">
<a href="https://github.com/alexbates/" target="_blank">
<!-- Credits to Flowbite for the Github icon https://flowbite.com/icons/ -->
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M12 2c-2.4 0-4.7.9-6.5 2.4a10.5 10.5 0 0 0-2 13.1A10 10 0 0 0 8.7 22c.5 0 .7-.2.7-.5v-2c-2.8.7-3.4-1.1-3.4-1.1-.1-.6-.5-1.2-1-1.5-1-.7 0-.7 0-.7a2 2 0 0 1 1.5 1.1 2.2 2.2 0 0 0 1.3 1 2 2 0 0 0 1.6-.1c0-.6.3-1 .7-1.4-2.2-.3-4.6-1.2-4.6-5 0-1.1.4-2 1-2.8a4 4 0 0 1 .2-2.7s.8-.3 2.7 1c1.6-.5 3.4-.5 5 0 2-1.3 2.8-1 2.8-1 .3.8.4 1.8 0 2.7a4 4 0 0 1 1 2.7c0 4-2.3 4.8-4.5 5a2.5 2.5 0 0 1 .7 2v2.8c0 .3.2.6.7.5a10 10 0 0 0 5.4-4.4 10.5 10.5 0 0 0-2.1-13.2A9.8 9.8 0 0 0 12 2Z" clip-rule="evenodd"/>
</svg>
</a>
</div>
<!-- An unordered list contains links to other pages -->
<ul class="navlist">
<li><span><a href="/">Home</a></span></li><li><span><a href="/about/">About</a></span></li><li class="active"><span>Contact</span></li>
</ul>
</nav>
</div>
</header>
<main>
<!-- A fixed height division allows content to overflow, while still being contained within main -->
<div class="block1">
<!-- division has fixed width and is centered on screens wider than 1200px -->
<div class="maininner">
<!-- Appears on left side on desktop and top on mobile -->
<div class="mainleft">
<!-- Credits to Flowbite for the Mailbox icon https://flowbite.com/icons/ -->
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 16v-5.5C11 8.5 9.4 7 7.5 7m3.5 9H4v-5.5C4 8.5 5.6 7 7.5 7m3.5 9v4M7.5 7H14m0 0V4h2.5M14 7v3m-3.5 6H20v-6a3 3 0 0 0-3-3m-2 9v4m-8-6.5h1"/>
</svg>
<h1>Reach Out</h1>
<h3>Use this form to send me an email.</h3>
</div>
<!-- Appears on right side on desktop and bottom on mobile -->
<div class="mainright">
<h2>Send Me a Message</h2>
<!-- Display error message if form does not validate server side -->
<p id="formError">Error: email is invalid or fields are missing.</p>
<!-- Contact form labels describe the content of the inputs and text area -->
<!-- Form label spans are used for error messages and are empty except when validation fails -->
<!-- Form inputs, textarea, and spans use unique ids to enable manipulation with javascript -->
<form id="form" class="contactform" action="/submit-form" method="post">
<label for="name">Name <span id="errorname" class="errorspan"></span></label>
<input id="name" name="name" type="text">
<label for="email">Email <span id="erroremail" class="errorspan"></span></label>
<input id="email" name="email" type="text">
<label for="subject">Subject <span id="errorsubject" class="errorspan"></span></label>
<input id="subject" name="subject" type="text">
<label for="message"><span class="your">Your </span>Message <span id="errormessage" class="errorspan"></span></label>
<textarea rows="3" id="message" name="text"></textarea>
<!-- Form submit button consists of 'Send' text and icon contained within spans -->
<button type="submit">
<span class="btn-text">Send</span>
<span class="btn-icon">
<!-- Credits to Flowbite for the Paper Plane (send) icon https://flowbite.com/icons/ -->
<svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24">
<path fill-rule="evenodd" d="M12 2c.4 0 .8.3 1 .6l7 18a1 1 0 0 1-1.4 1.3L13 19.5V13a1 1 0 1 0-2 0v6.5L5.4 22A1 1 0 0 1 4 20.6l7-18a1 1 0 0 1 1-.6Z" clip-rule="evenodd"/>
</svg>
</span>
</button>
</form>
</div>
</div>
</div>
</main>

</body>
</html>
Loading

0 comments on commit 2d7ddb3

Please sign in to comment.