-
-
Save muiton/2cb210d4c7ba74a39f471cb4b9a88585 to your computer and use it in GitHub Desktop.
Revisions
-
maxkostinevich revised this gist
Jan 1, 2020 . 1 changed file with 11 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,3 +1,14 @@ <!-- /* * Serverless contact form handler for Cloudflare Workers. * Emails are sent via Mailgun. * * Learn more at https://maxkostinevich.com/blog/serverless-contact-form * Live demo: https://codesandbox.io/s/serverless-contact-form-example-x0neb * * (c) Max Kostinevich / https://maxkostinevich.com */ --> <!DOCTYPE html> <html lang="en"> <head> -
maxkostinevich revised this gist
Jan 1, 2020 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -3,6 +3,7 @@ * Emails are sent via Mailgun. * * Learn more at https://maxkostinevich.com/blog/serverless-contact-form * Live demo: https://codesandbox.io/s/serverless-contact-form-example-x0neb * * (c) Max Kostinevich / https://maxkostinevich.com */ -
maxkostinevich created this gist
Jan 1, 2020 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,185 @@ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Serverless Contact Form Example</title> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous" /> </head> <body> <div class="container"> <div class="row justify-content-center"> <div class="col-12 col-md-4"> <h1 class="text-center">Serverless Contact Form Example</h1> <p class="alert alert-primary"> Email sending is disabled on this demo.<br /> Learn more <a target="_blank" href="https://maxkostinevich.com/blog/serverless-contact-form" >here</a >. </p> <form action="https://contact-dev.frontier.workers.dev/" method="post" class="form-horizontal ajax-form" > <!-- Notifications --> <p class="msg-container text-center"></p> <div class="form-group"> <label for="name">Name</label> <input type="text" class="form-control" name="name" id="name" placeholder="Your name" /> </div> <div class="form-group"> <label for="eml">Email</label> <input type="email" class="form-control validate validate_userEmail" name="eml" id="eml" placeholder="Your email" /> </div> <div class="form-group"> <label for="message">Message</label> <textarea rows="5" class="form-control validate validate_msgText" name="message" id="message" placeholder="Your message" ></textarea> </div> <div class="form-group"> <button type="submit" class="btn btn-primary" data-btn-label="Submit" data-btn-label-processing="Processing.." > Submit </button> </div> <input type="text" class="input-honeypot" style="visibility:hidden;width:1px;height:1px;padding:0px;border:none;" name="eml2" value="" /> </form> <hr /> <p class="text-secondary text-center"> Created by <a href="https://maxkostinevich.com" target="_blank" >Max Kostinevich</a > </p> </div> <!-- /End Contact Form Col --> </div> </div> <script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous" ></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous" ></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js" integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6" crossorigin="anonymous" ></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> $(document).on("submit", "form.ajax-form", function(e) { e.preventDefault(); var currentForm = $(this); // Get current form object // disable submit button $("[type=submit]", currentForm).attr("disabled", "disabled"); // clean up the msg container $(".msg-container", currentForm) .html("") .attr("class", "msg-container text-center") .css("display", "hidden"); // remove fields error classes currentForm.find(".is-invalid").removeClass("is-invalid"); // add preloader $("[type=submit]", currentForm).html( $("[type=submit]", currentForm).data("btn-label-processing") ); let formData = $(this) .serializeArray() .map( function(x) { this[x.name] = x.value; return this; }.bind({}) )[0]; axios({ method: $(this).attr("method"), url: $(this).attr("action"), data: formData }) .then(function(response) { var hand = setTimeout(function() { // clear the form if form submitted successfully $(currentForm).trigger("reset"); // show returned message $(".msg-container", currentForm) .addClass("alert alert-success") .html(response.data["message"]) .css("display", "none"); // enable submit button again var btnLabel = $("[type=submit]", currentForm).data("btn-label"); $("[type=submit]", currentForm).removeAttr("disabled"); $("[type=submit]", currentForm).html(btnLabel); clearTimeout(hand); }, 1000); }) .catch(function(error) { // show returned message $(".msg-container", currentForm) .addClass("alert alert-danger") .html(error.response.data["message"]) .css("display", "block"); // enable submit button again var btnLabel = $("[type=submit]", currentForm).data("btn-label"); $("[type=submit]", currentForm).removeAttr("disabled"); $("[type=submit]", currentForm).html(btnLabel); console.log(error); }); return false; }); </script> </body> </html> This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,195 @@ /* * Serverless contact form handler for Cloudflare Workers. * Emails are sent via Mailgun. * * Learn more at https://maxkostinevich.com/blog/serverless-contact-form * * (c) Max Kostinevich / https://maxkostinevich.com */ // Script configuration const config = { mailgun_key: "YOUR_MAILGUN_API_KEY", mailgun_domain: "YOUR_MAILGUN_DOMAIN", from: "no-reply <no-reply@YOUR_DOMAIN>", admin_email: "xxxxx@YOUR_DOMAIN", email_field: "eml", // email field name form_fields: ["name", "message"], // list of required fields honeypot_field: "eml2" // honeypot field name }; // -------- // utility function to convert object to url string const urlfy = obj => Object.keys(obj) .map(k => encodeURIComponent(k) + "=" + encodeURIComponent(obj[k])) .join("&"); // Helper function to return JSON response const JSONResponse = (message, status = 200) => { let headers = { headers: { "content-type": "application/json;charset=UTF-8", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type" }, status: status }; let response = { message: message }; return new Response(JSON.stringify(response), headers); }; addEventListener("fetch", event => { const request = event.request; if (request.method === "OPTIONS") { event.respondWith(handleOptions(request)); } else { event.respondWith(handle(request)); } }); const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type" }; function handleOptions(request) { if ( request.headers.get("Origin") !== null && request.headers.get("Access-Control-Request-Method") !== null && request.headers.get("Access-Control-Request-Headers") !== null ) { // Handle CORS pre-flight request. return new Response(null, { headers: corsHeaders }); } else { // Handle standard OPTIONS request. return new Response(null, { headers: { Allow: "GET, HEAD, POST, OPTIONS" } }); } } async function handle(request) { try { const form = await request.json(); // Honeypot / anti-spam check // Honeypot field should be hidden on the frontend (via css), // and always have an empty value. If value is not empty, then (most likely) the form has been filled-in by spam-bot if (form[config.honeypot_field] !== "") { return JSONResponse("Invalid request", 400); } // Validate form inputs for (let i = 0; i < config.form_fields.length; i++) { let field = config.form_fields[i]; if (form[field] === "") { return JSONResponse(`${field} is required`, 400); } } // Validate email field let email_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,}))$/; if ( form[config.email_field] == "" || !email_regex.test(form[config.email_field]) ) { return JSONResponse("Please, enter valid email address", 400); } // assign email address to the form form["email"] = form[config.email_field]; const admin_template = ` <html> <head> <title>New message from ${form.name}</title> </head> <body> New message has been sent via website.<br><br> <b>Name:</b> ${form.name} <br> <b>Email:</b> ${form.email} <br> <br> <b>Message:</b><br> ${form.message.replace(/(?:\r\n|\r|\n)/g, "<br>")} </body> </html> `; const user_template = ` Hello ${form.name}, Thank you for contacting me! I have received your message and I will get back to you as soon as possible. `; let admin_data = { from: config.from, to: config.admin_email, subject: `New message from ${form.name}`, html: admin_template, "h:Reply-To": form.email // reply to user }; let admin_options = { method: "POST", headers: { Authorization: "Basic " + btoa("api:" + config.mailgun_key), "Content-Type": "application/x-www-form-urlencoded", "Content-Length": admin_data.length }, body: urlfy(admin_data) }; let user_data = { from: config.from, to: form.email, subject: "Thank you for contacting me!", html: user_template, "h:Reply-To": config.admin_email // reply to admin }; let user_options = { method: "POST", headers: { Authorization: "Basic " + btoa("api:" + config.mailgun_key), "Content-Type": "application/x-www-form-urlencoded", "Content-Length": user_data.length }, body: urlfy(user_data) }; try { /* let results = await Promise.all([ fetch(`https://api.mailgun.net/v3/${config.mailgun_domain}/messages`, admin_options), fetch(`https://api.mailgun.net/v3/${config.mailgun_domain}/messages`, user_options) ]); console.log('Got results'); console.log(results); */ return JSONResponse("Message has been sent"); } catch (err) { console.log("Error"); console.log(err); return JSONResponse("Oops! Something went wrong.", 400); } } catch (err) { return new Response(""); } }