We will create a contact form using Cloudflare Pages with Pages Functions to handle form submissions and send emails through Oracle’s Email Delivery API.
Prerequisites
A Cloudflare account with Pages enabled
Oracle Cloud Infrastructure (OCI) account with Email Delivery service configured
OCI API key and config details ready:
Tenancy OCID
User OCID
API key fingerprint
Private key in PEM format
Approved sender email address in OCI Email Delivery
Project Structure
We’ll create two main files:
public/index.html - Contains the contact form
functions/api/contact.js - Handles form submission and email sending
1. Creating the Contact Form
First, create the contact form in public/index.html by replacing <TURNSTILE_SITE_KEY> with the Cloudflare Turnstile site key:
The onRequestPost function exclusively handles POST requests and ignores other methods, since form submissions only use POST requests. It implements proper error handling and response redirects to ensure a smooth user experience.
The validateToken function validates the checks performed by the Turnstile widget in the frontend to confirm whether form submissions are from real people and block unwanted bots.
The forwardMessage function handles email creation and sending through Oracle’s Email Delivery API. While Oracle provides an SDK for email sending, Cloudflare Pages Functions currently doesn’t support it. Therefore, we must manually sign our API requests for authentication.
First, set up the email template and date formatting:
asyncfunctionforwardMessage(name,email,message,env){constdate=newDate();constemailDate=date.toLocaleString('en-GB',{timeZone:'Asia/Brunei',timeZoneName:'short',hour12:false,weekday:'short',year:'numeric',month:'short',day:'2-digit',hour:'2-digit',minute:'2-digit',second:'2-digit',});consthtml=`<!DOCTYPE html><html lang="en-gb" dir="ltr"><head><meta charset="utf8"><title>Contact</title><meta name="viewport" content="width=device-width,initial-scale=1"></head><body><p>Someone just submitted your form on <a rel="noopener noreferrer" href="https://halimdaud.com/">https://halimdaud.com/</a>.</p><p>Here's what they had to say:</p><table style="font-family:'Trebuchet MS',Arial,Helvetica,sans-serif;border-collapse:collapse;width:100%"><tbody><tr><th style="border:1px solid #ddd;padding:12px 8px;text-align:left;background-color:#354d91;color:#fff">Name</th><th style="border:1px solid #ddd;padding:12px 8px;text-align:left;background-color:#354d91;color:#fff">Value</th></tr><tr><td style="border:1px solid #ddd;padding:8px"><strong>name</strong></td><td style="border:1px solid #ddd;padding:8px"><pre style="margin:0;white-space:pre-wrap">${name}</pre></td></tr><tr><td style="border:1px solid #ddd;padding:8px"><strong>email</strong></td><td style="border:1px solid #ddd;padding:8px"><pre style="margin:0;white-space:pre-wrap"><a href="mailto:${email}">${email}</a></pre></td></tr><tr><td style="border:1px solid #ddd;padding:8px"><strong>message</strong></td><td style="border:1px solid #ddd;padding:8px"><pre style="margin:0;white-space:pre-wrap">${message}</pre></td></tr></tbody></table><br><p style="text-align:center">Submitted at ${emailDate}</p><br></body></html>`;consttext=`Someone just submitted your form on https://halimdaud.com/.\n\nHere's what they had to say:\n\nname: ${name}\nemail: ${email}\nmessage: ${message}\n\nSubmitted at ${emailDate}`;// ... (rest of the function implementation)
Next, set up the environment variables:
68
69
70
71
72
73
74
75
76
// ... (previous code for email template and date formatting)
consttenancy=env.OCI_TENANCY;constuser=env.OCI_USER;constfingerprint=env.OCI_FINGERPRINT;constprivateKey=env.OCI_PRIVATE_KEY;constcompartmentId=tenancy;// ... (rest of the function implementation)
// ... (previous code for environment variables)
constapiKeyId=`${tenancy}/${user}/${fingerprint}`;constregion="ap-singapore-1"constreqUrl=newURL(`https://cell0.submit.email.${region}.oci.oraclecloud.com/20220926/actions/submitEmail`);constreqHost=reqUrl.host;constreqPathname=reqUrl.pathname;constreqHeaders=newHeaders();constreqMethod="POST";constrequestTargetHeader=`(request-target): ${reqMethod.toLowerCase()}${reqPathname}`;constreqDate=date.toUTCString();reqHeaders.append("date",reqDate);constdateHeader=`date: ${reqDate}`;consthostHeader=`host: ${reqHost}`;constreqBody=JSON.stringify({sender:{senderAddress:{email:env.SENDER_EMAIL,name:env.SENDER_NAME},compartmentId:compartmentId},recipients:{to:[{email:env.RECEIVER_EMAIL}]},subject:"New submission from https://halimdaud.com/",bodyHtml:html,bodyText:text,replyTo:[{email:email}]});consthash=crypto.createHash('sha256');hash.update(reqBody);constbase64EncodedBodyHash=hash.digest("base64");reqHeaders.append("x-content-sha256",base64EncodedBodyHash);constcontentSha256Header=`x-content-sha256: ${base64EncodedBodyHash}`;constcontentLengthHeader=`content-length: ${reqBody.length}`;reqHeaders.append("content-type","application/json");reqHeaders.append("accept","application/json");constcontentTypeHeader="content-type: application/json";// ... (rest of the function implementation)
// ... (previous code for headers and body preparation)
constsigningStringArray=[requestTargetHeader,dateHeader,hostHeader,contentSha256Header,contentTypeHeader,contentLengthHeader];constheadersToSign=["(request-target)","date","host","x-content-sha256","content-type","content-length"];constheaders=headersToSign.join(" ");constsigningString=signingStringArray.join("\n");constsigningKey=awaitimportPrivateKey(privateKey);constbase64EncodedSignature=awaitsignMessage(signingKey,signingString);constauthorizationHeader=`Signature version="1",keyId="${apiKeyId}",algorithm="rsa-sha256",headers="${headers}",signature="${base64EncodedSignature}"`;reqHeaders.append("Authorization",authorizationHeader);// ... (rest of the function implementation)
Finally, send the POST request to Oracle’s HTTPS API:
// ... (previous code for combining headers)
constnewRequest=newRequest(reqUrl,{method:reqMethod,body:reqBody,headers:reqHeaders});letdata;constresponse=awaitfetch(newRequest);if(!response.ok){data=awaitresponse.text();}else{data=awaitresponse.json();}returndata;}
Utility Functions
These helper functions handle RSA key operations. The importPrivateKey function imports RSA private keys in PEM format, while signMessage signs API requests for authentication.
Set up the following environment variables in the Cloudflare Pages project:
TURNSTILE_SECRET_KEY - The Cloudflare Turnstile secret key
OCI_TENANCY - The Oracle Cloud Infrastructure tenancy OCID
OCI_USER - The OCI user OCID
OCI_FINGERPRINT - The API key fingerprint
OCI_PRIVATE_KEY - The private key in PEM format
SENDER_EMAIL - The approved sender email address from OCI Email Delivery
SENDER_NAME - The name to display as the sender
RECEIVER_EMAIL - The email address to receive the contact form submissions
Deployment
Deploy project to Cloudflare Pages using their Git integration or direct upload. The Pages Functions will automatically handle the form submissions and email sending.
Contact form is now ready to use! When users submit the form, it will validate the Turnstile token and send the message through Oracle’s Email Delivery service.