For this site, I write all demos and blog posts in Markdown format and then use Eleventy to convert them into web pages.
Since Eleventy generates static HTML, I needed to use a third-party service or serverless function to handle the form submission.
I recently completed my AWS Certified Cloud Practitioner training, so I used this as an opportunity to practice my AWS skills.
The basic steps were:
AWS provides a nice test interface for the lambda function itself. I also tested the end-to-end procuss using the HTML form I created (see below).
I also wrote 2 tests using curl
to help debug the lambda function and the API. One uses JSON
data, while the other used an encoded multi-part form.
curl -X POST "$ENDPOINT" \
-H 'Content-Type: application/json' \
-d '{"name": "John Doe", "email": "john.doe@example.com", "message": "Hello, this is a test message."}'
echo
curl -X POST "$ENDPOINT" \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'name=Alice' \
--data-urlencode 'email=alice@example.com' \
--data-urlencode 'message=Hello, this from Alice.'
Below is the final python script, which parses the incoming payload then sends
an email via the boto3
library.
import json
from base64 import b64decode
from urllib.parse import parse_qs, unquote_plus
import boto3
client = boto3.client('ses', region_name='us-east-1')
def lambda_handler(event, context):
# Parse the incoming payload
if event.get("isBase64Encoded"):
body = b64decode(event['body']).decode('utf-8') # decode base64 string
body = unquote_plus(body) # decode the percent-encoded characters and + signs
body = parse_qs(body) # to dict
elif isinstance(event["body"], dict):
body = event["body"]
else:
body = json.loads(event['body'])
name = body['name']
email = body['email']
message = body['message']
print(f"Name: {name}, Email: {email}, Message: {message}")
msg = f"name: {name}\n"
msg += f"email: {email}\n"
msg += f"message: {message}"
response = client.send_email(
Destination={
'ToAddresses': [MyPersonalEmail]
},
Message={
'Body': {
'Text': {
'Charset': 'UTF-8',
'Data': msg,
}
},
'Subject': {
'Charset': 'UTF-8',
'Data': 'Contact form received from jeremyschaub.us',
},
},
Source='MyPersonalEmail'
)
html_content = """
<html>
<head>
<title>Confirmation</title>
</head>
<body>
<p>Thank you for reaching out! I'll be in touch within a day or two.</p>
<p> Return to <a href="https://jeremyschaub.us">Jeremy's site</a>.
</body>
</html>
"""
return {
'statusCode': 200,
'headers': {
'Content-Type': 'text/html'
},
'body': html_content
}
The contact form is simple enough.
---
title: Contact Me
layout: layout.html
permalink: contact.html
---
<form id="contact-form" action="https://tseww1okmf.execute-api.us-east-1.amazonaws.com/submit" method="POST">
<label for="name">Name:</label><br>
<input type="text" id="name" name="name" class="jds-form" required><br>
<label for="email">Email:</label><br>
<input type="email" id="email" name="email" required><br>
<label for="message">Message:</label><br>
<textarea id="message" name="message" required></textarea><br>
<input type="submit" value="Submit">
</form>
Currently, this sends you to the AWS API endpoint, which displays a message that redirects you back to my site.
It would be cleaner if I implemented this via client-side javascript, so I may do that in the future.
I also need to implement a CAPTCHA or alternative so to reduce spam when I add my site to the popular search engines for indexing. I like the simple Cloudflare Turnstile, which we use on the ISSI site.