5: Setting up a working contact form.

This should be straight forward enough… Famous last words. Well. It was straight forward enough but as with anything where you are using examples found on the web it didn’t quite work as expected first time.

The source post

I found this AWS post: https://aws.amazon.com/blogs/architecture/create-dynamic-contact-forms-for-s3-static-websites-using-aws-lambda-amazon-api-gateway-and-amazon-ses/ which looked like it was just what I needed.

The bones of the structure is:

so I need to do the following:

  • Setup a contact form in jekyll
  • Setup lambda function
  • Setup API gateway in front of the lambda function.
  • Setup jquery script and add it to jekyll site
  • Publish and test.

I’ve done this already so the following sections are more about what I did to get it working rather than a direct walk through.

Set up a contact form in jekyll

I had a contact page set up in the last article however I grabbed the form text from the AWS article and pasted this in.

This has the phone number set to hidden and the recaptcha removed.

Setup the lambda function

I logged in to the AWS console then selected lambda. I followed the basic steps covered in the article however I had to make a couple of changes.

The lambda function itself didn’t quite work. Well… it worked via a test method but didn’t via the API gateway. When I was debugging I was seeing the text I wanted in the body entry.

2021-01-03T14:44:01.080Z	b7b06af3-0793-49ef-bcf9-ceed6ad687a2	INFO	Received event: {
  version: '2.0',
  routeKey: 'POST /contact',
  rawPath: '/contact',
  rawQueryString: '',
  headers: {
    accept: 'application/json, text/javascript, */*; q=0.01',
    'accept-encoding': 'gzip, deflate, br',
    'accept-language': 'en-GB,en-US;q=0.9,en;q=0.8',
    'content-length': '86',
    'content-type': 'application/json; charset=UTF-8',
    host: 'qenydlwe11.execute-api.eu-west-3.amazonaws.com',
    origin: 'http://pubgolf.co.uk',
    referer: 'http://pubgolf.co.uk/contact.htm',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'cross-site',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.107 Safari/537.36',
    'x-amzn-trace-id': 'Root=1-5ff1d830-1ca42efa3256c5e6020f7b82',
    'x-forwarded-for': '217.155.45.217',
    'x-forwarded-port': '443',
    'x-forwarded-proto': 'https'
  },
  requestContext: {
    accountId: '434376514118',
    apiId: 'qenydlwe11',
    domainName: 'qenydlwe11.execute-api.eu-west-3.amazonaws.com',
    domainPrefix: 'qenydlwe11',
    http: {
      method: 'POST',
      path: '/contact',
      protocol: 'HTTP/1.1',
      sourceIp: '217.155.45.217',
      userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.107 Safari/537.36'
    },
    requestId: 'Yk63nis0iGYEMcg=',
    routeKey: 'POST /contact',
    stage: '$default',
    time: '03/Jan/2021:14:44:00 +0000',
    timeEpoch: 1609685040628
  },
  body: '{"name":"Jamie Glendinning","email":"jamie_glendinning@work2fly.co.uk","desc":"jjjjj"}',
  isBase64Encoded: false
}

however the methd was attempting to use event.name when that info was inside “body”.

So an extra statement was added to parse event.body.

var json = JSON.parse(event.body);

then I replaced event.name –> json.name event.email –> json.email event.email –> json.email

I also added in a few extra debug log statements to help me work out where things were going wrong initially. Got this funtction out the other end.

var AWS = require('aws-sdk');
var ses = new AWS.SES();
 
var RECEIVER = 'jamie_glendinning  work2fly.co.uk';
var SENDER = 'jamie_glendinning  work2fly.co.uk';


var response = {
 "isBase64Encoded": false,
 "headers": { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': 'pubgolf.co.uk'},
 "statusCode": 200,
 "body": "{\"result\": \"Success.\"}"
 };

exports.handler = function (event, context) {
    console.log('Received event:', event);
    console.log('body text :', event.body);
    var json = JSON.parse(event.body);
    console.log('senders name:', json.name);
    sendEmail(event, function (err, data) {
        context.done(err, null);
    });
};


 
function sendEmail (event, done) {
    var json = JSON.parse(event.body);
    console.log(' name: ',  json.name);
    console.log(' email: ',  json.email);
    console.log(' message: ',  json.desc);
    
    var params = {
        Destination: {
            ToAddresses: [
                RECEIVER
            ]
        },
        Message: {
            Body: {
                Text: {
                    Data: 'name: ' + json.name + '\nemail: ' + json.email + '\ndesc: ' + json.desc,
                    Charset: 'UTF-8'
                }
            },
            Subject: {
                Data: 'pubgolf contact form ' + json.name,
                Charset: 'UTF-8'
            }
        },
        Source: SENDER
    };
    ses.sendEmail(params, done);
    console.log(' Email sent: ', params);
}

I’m sure there is a better way or different way to do this however it’s working and the api gateway is not set up as a proxy so maybe a parameter mapping tweak. It works. So I’ll leave it at that.

Setup API gateway in front of the lambda function.

The API gateway has changed a fair bit from the article. So I span through setting up an api gateway with integration to the lambda function. This appeared to be set up and running however I couldn’t find a test button as per the old api gateway structure.

testing the api gateway in postman.

Logged into postman then set up the api with a couple of body form data entries:

Clicking send gave me an error. CORS related.

Update CORS settings.

After a bit of trial and error this configuration appeared to work. I suspect I may refine it further.

Enable api gateway cloudwatch logs.

I enabled the logging option and set up a log group. I wanted see what the API gateway was thinking as nothing was getting through to the lambda function.

API gateway config screenshots

contact.js

I took the script sample on the article and added it to the jekyll site under /assets/js/contact.js. The file was updated to reflect the api gateway path

function submitToAPI(e) {
       e.preventDefault();
       var URL = "https://qenydlwe11.execute-api.eu-west-3.amazonaws.com/contact";

            var Namere = /[A-Za-z]{1}[A-Za-z]/;
            if (!Namere.test($("#name-input").val())) {
                         alert ("Name can not less than 2 char");
                return;
            }

            if ($("#email-input").val()=="") {
                alert ("Please enter your email address");
                return;
            }
            var mobilere = /[0-9]{10}/;
            if (mobilere.test($("#phone-input").val())) {
                alert ("Please enter valid phone number");
                return;
            }
            var reeamil = /^([\w-\.]+@([\w-]+\.)+[\w-]{2,6})?$/;
            if (!reeamil.test($("#email-input").val())) {
                alert ("Please enter valid email address");
                return;
            }

       var name = $("#name-input").val();
       var email = $("#email-input").val();
       var desc = $("#description-input").val();
       var data = {
          name : name,
          email : email,
          desc : desc
        };

       $.ajax({
         type: "POST",
         url : "https://qenydlwe11.execute-api.eu-west-3.amazonaws.com/contact",
         dataType: "json",
         crossDomain: "true",
         contentType: "application/json; charset=utf-8",
         data: JSON.stringify(data),

         
         success: function () {
           // clear form and show a success message
           alert("Successfull");
           document.getElementById("contact-form").reset();
       location.reload();
         },
         error: function () {
           // show an error message
           alert("UnSuccessfull");
         }});
     }

The other change I’ve made is to validate that the phone number field is blank. This field is hidden so will only be populated (I hope) by bots or scripts so this may cut down on the number of random posts sent from the page. The original form had a v2 recaptcha line so may look at that option in the future.

Adding the script into jekyll

It’s easier to tweak the layout and include files in a jekyll site than edit the head tags on a specific page. Or that’s how I read it.

So lets make a copy of the “Single” page and call it “contact” I copied the Single.html to _layouts/contact.html

Then checked the file. It was based on the default.html file. So I copied default.html to _layouts/default-contact.html

I then edited the head tag here to add in:

Then edited the contact.html file to reference default-contact.html

---
layout: default-contact
---

and updated my contact.md file that generated the contactus.html file to refer to the contact layout.

---
layout: contact
title: Contact
permalink: /contact.htm
---

So the files are all now in place.

Run jeckyll serve to create _site files

jekyll serve

The files are recreated locally and can be reviewed. All looked ok.

sync to the bucket and checked

The files look ok so we can ru the sync comand

aws s3 sync _site s3://pubgolf.co.uk

The website appears to be working. The contact form appears to be working.

Email received

Job done :-)

Any questions about this set up - get in touch.

Jamie

Updated: