# Marcus Kida's Blog

Hey! I'm Marcus, a Freelance Senior iOS Developer and CTO-as-a-Service @Bearologics. I'm solving problems through code. Want to hire me? Let's talk!

Rationale

My company Bearologics' Website is hosted using Cloudflare Pages. Previously I've used Netlify and their (amazing) hosted contact forms solution, but as I'm already using (and paying for) Cloudflare as a CDN, I thought it might make sense to also host the site there.

I wouldn't have guessed it upfront, but the contact form on my website makes up a pretty large amount (roughly 1/3) of no. of messages when comparing it to direct messages via e-mail and calls via phone.

What

I want my clients to be able to send me messages, also the forms needs to have some basic form of spam protection and should not send emails but funnel messages into my company Slack.

For this to happen you'll obviously need to be able to create and link apps to your Slack, please head over to their API site and create a new App and store it's App token in a safe place.

How

I'm going to use this neat workers Slack module and set up a Cloudflare worker to receive HTML form data input and proxy it. Also after doing some research I'm coming to the conclusion that, as a very basic measure of conterfeiting spam, I'm going to try out a simple honeypot form field. If it won't be sufficient I'll get back to the problem again, but for now I'm definitely not going to implement a captcha etc.

Let's code

Client side HTML

As you'll see we're going to create a simple contact form which posts to /contact/submit/ and we're going to add a form element subject which we'll hide via CSS. As you will see in the Worker code, whenever there is a message that actually contains a subject, we'll simply discard it. The reason for this being spam protection, as the field is hidden via CSS a regular user wouldn't fill it, and if it contains data you can be pretty sure that it'll be automatically filled by some bot.

Disclaimer: I've removed all CSS classes to only focus on the functional code and no style.

<style type="text/css">
    #subject {
        display: none;
    }
</style>

<form method="POST" action="/contact/submit">       

    <label for="name">Name</label>
    <input id="name" type="text" placeholder="Your name" name="name" required>

    <label for="email">E-Mail</label>
    <input id="email" type="email" placeholder="Your e-mail address" name="email" required>

    <label for="subject">Subject</label>
    <input id="subject" name="subject" type="text" placeholder="Subject">

    <label for="message">Message</label>
    <textarea id="message" name="message" placeholder="What can we do for you or your business?" required></textarea>

    <input type="checkbox" id="accept" name="privacy" value="accepted" required>
    <label for="accept">
        I accept the <a href="/legal" target="_blank">terms, privacy and data protection declaration</a>.
    </label>

    <button type="submit">Send message</button>

</form>

Worker JavaScript

First let's bootstrap a new Cloudflare Worker script using Wrangler CLI.

$ wrangler generate worker-form-slack && cd worker-form-slack

No edit the wrangler.toml and update the route, account_id , zone_id and some environment variables. SLACK_CHANNEL must be the name of the channel the Slack app can post into. REDIRECT_SUCCESS is going to be the destination page which the user will be redirected to, after successfully submitting the form.

name = "worker-form-slack"
type = "webpack"

account_id = "your-cf-account-id"
workers_dev = false
route = "www.my-website.tld/contact/submit"
zone_id = "your-cf-domain-zone-id"

vars = { SLACK_CHANNEL = "website-contact", REDIRECT_SUCCESS = "https://www.my-website.tld/contact/success" }

Now open up index.js and let's create the actual Worker code, you'll need to add SLACK_BOT_TOKEN to the Cloudflare Worker secrets. You can find the secrets by navigating to the Settings Tab of your Worker entry. The following code is everything you need to get the worker up and running:

const SlackREST = require('@sagi.io/workers-slack')
const SlackAPI = new SlackREST({ botAccessToken: SLACK_BOT_TOKEN })

addEventListener('fetch', event => {
  event.respondWith(sendMessage(event.request))
})

async function sendMessage(request) {
  const body = await request.formData()
  const { name, email, subject, message } = Object.fromEntries(body)

  if (subject) {
    return Response.redirect(REDIRECT_SUCCESS, 301)
  }

  const result = await SlackAPI.chat.postMessage({
    channel: SLACK_CHANNEL,
    text: `*New Website Contact Message:*\n\nName: ${name}\nEmail: ${email}\nMessage: ${message}`,
  })

  if (result.ok) {
    return Response.redirect(REDIRECT_SUCCESS, 301)
  }

  return new Response(JSON.stringify({ success: result.ok }), {
    headers: { 'content-type': 'application/json' },
  })
}

That's about it, you can now deploy the Worker. With the form and the Cloudflare Worker in place, you're ready to receive contact form messages from your website, right into your Slack inbox, without any further server deployments.

You can find the full source code here: https://github.com/kimar/Cloudflare-Workers-Website-Slack-Contact-Form

If you've liked this post, (or not) feel free to drop me a line via Twitter or visit my company website in case you need a freelancer for your existing or next iOS App.

Tagged with: