Skip to content

Commit

Permalink
fix: make the mailer work again here in this project with hcaptcha (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
rlperez authored Sep 19, 2024
1 parent c007295 commit a5f1193
Show file tree
Hide file tree
Showing 8 changed files with 208 additions and 79 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ node_modules
dist
src/_includes/css
src/_includes/scripts
src/posts/**/*.html

# cache
.cache
Expand Down
93 changes: 93 additions & 0 deletions api/contact.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {verify} from 'hcaptcha';
import {MailerSend, EmailParams, Sender, Recipient} from 'mailersend';
import {getClientIp} from 'request-ip';

async function verifyCaptcha(localAddress, hcaptcha_response) {
const hcaptcha_site_key = process.env.HCAPTCHA_SITE_KEY;
const hcaptcha_secret = process.env.HCAPTCHA_SECRET;

if (!hcaptcha_secret) {
console.error('hcaptcha secret missing!');
return {status: 500, message: 'hcaptcha secret key missing!'};
}

if (!hcaptcha_site_key) {
console.error('hCaptcha site key missing!');
return {status: 500, message: 'hcaptcha site key missing!'};
}

if (hcaptcha_response) {
const verify_response = await verify(hcaptcha_secret, hcaptcha_response, localAddress, hcaptcha_site_key);

if (verify_response?.success) {
return {
status: 200,
message: 'hcaptcha verified user'
};
} else {
console.warn({verify_response});
return {
status: 429,
message: 'hcaptcha verification failed',
error_codes: verify_response['error-codes'],
hostname: verify_response.hostname
};
}
} else {
return {status: 400, message: 'Missing hcaptchaResponse'};
}
}

async function sendMail(request) {
const message = {
from: request['contactEmail'],
to: process.env.CONTACT_EMAIL,
name: request['contactName'],
subject: `[KABLAMO.ME] CONTACT PAGE FROM ${request['contactName']}`,
body:
`NAME: ${request['contactName']}\n` +
`PHONE: ${request['contactPhone']}\n\n` +
`${request['contactMessage']}`
};
console.trace('Sending email...', {message});
const mailer = new MailerSend({
apiKey: process.env.MAILER_SEND_API_KEY
});

const sentFrom = new Sender(message.from, message.name);
const recipients = [new Recipient(process.env.CONTACT_EMAIL, 'Kablamo.me Admin')];

const emailParams = new EmailParams()
.setFrom(sentFrom)
.setTo(recipients)
.setSubject(message.subject)
.setText(message.body);

return mailer.email.send(emailParams);
}

export default async function handler(request, response) {
const hcaptcha_response = request?.body['hcaptchaResponse'];
console.trace('Contact Request', {body: request.body});
const ipAddress = getClientIp(request);
const verify_response = await verifyCaptcha(ipAddress, hcaptcha_response);

if (verify_response.status === 200) {
const result = await sendMail(request.body);
if (result.statusCode >= 200 || result.statusCode <= 299) {
console.log({info});
return response.status(200).json({body: {message: 'Email successful sent'}});
} else {
console.error({request: request.body, result});
return response.status(500).json({
body: {
status: response.statusCode,
message: response.body
}
});
}
} else {
console.warn('hcaptcha verification failed', {verify_response});
response.status(verify_response.status).json({body: verify_response});
}
}
Binary file modified bun.lockb
Binary file not shown.
3 changes: 2 additions & 1 deletion eleventy.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ export default async function (eleventyConfig) {
eleventyConfig.addPassthroughCopy({
// -- to root
'src/assets/images/favicon/*': '/',
'node_modules/@hcaptcha/vanilla-hcaptcha/dist/*': 'assets/scripts/bundle/hcaptcha/',
'node_modules/@hcaptcha/vanilla-hcaptcha/dist/*': 'assets/scripts/components/hcaptcha/',
'src/assets/scripts/components/contact.js': 'assets/scripts/components/contact.js',
// -- node_modules
'node_modules/lite-youtube-embed/src/lite-yt-embed.{css,js}': `assets/components/`
});
Expand Down
18 changes: 12 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "eleventy-excellent",
"version": "0.1.1",
"version": "0.1.2",
"description": "Personal developer blog based on the eleventy-excellent theme.",
"author": "Rigoberto L. Perez",
"license": "ISC",
Expand All @@ -18,7 +18,10 @@
"start": "run-p dev:*",
"build": "run-s clean build:*"
},
"keywords": ["blog", "11ty"],
"keywords": [
"blog",
"11ty"
],
"repository": {
"type": "git",
"url": "https://github.com/rlperez/rlperez.github.io.git"
Expand All @@ -33,11 +36,13 @@
"@11ty/is-land": "^4.0.0",
"@hcaptcha/vanilla-hcaptcha": "^1.1.0-alpha2",
"@vercel/analytics": "^1.3.1",
"@vercel/node": "^3.2.14",
"@vercel/speed-insights": "^1.0.12",
"lite-youtube-embed": "^0.3.3"
"lite-youtube-embed": "^0.3.3",
"mailersend": "^2.3.0",
"request-ip": "^3.3.0"
},
"devDependencies": {
"tailwindcss": "^3.4.11",
"@tailwindcss/forms": "^0.5.9",
"@toycode/markdown-it-class": "^1.2.4",
"autoprefixer": "^10.4.20",
Expand All @@ -59,7 +64,7 @@
"markdown-it-prism": "^2.3.0",
"netlify-plugin-cache": "^1.0.3",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.45",
"postcss": "^8.4.47",
"postcss-cli": "^11.0.0",
"postcss-import": "^16.1.0",
"postcss-import-ext-glob": "^2.1.1",
Expand All @@ -70,6 +75,7 @@
"sharp": "^0.33.5",
"sharp-ico": "^0.1.5",
"slugify": "^1.6.6",
"svgo": "^3.3.2"
"svgo": "^3.3.2",
"tailwindcss": "^3.4.11"
}
}
26 changes: 16 additions & 10 deletions src/_layouts/contact.njk
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,25 @@ permalink: 'contact/index.html'
</div>
<div>
<div id="contactFlash is-hidden"></div>
<form method="POST" class="" id="contactForm" action="#">
<div class="contactFlash"></div>
<form method="POST" class="" id="contactForm" action="/api/contact">
<div id="contactFlash"></div>
<div class="">
<label for="messageName" class="block text-sm font-medium">Name</label>
<input type="text" class="input form-input" placeholder="Your name..." required />
<label for="contactName" class="block text-sm font-medium">Name</label>
<input id="contactName" type="text" class="input form-input" placeholder="Your name..." required />
</div>
<div class="mt-m">
<label for="messageEmail" class="block text-sm font-medium">Email</label>
<input type="email" class="input form-input" placeholder="[email protected]" required />
<label for="contactEmail" class="block text-sm font-medium">Email</label>
<input
id="contactEmail"
type="email"
class="input form-input"
placeholder="[email protected]"
required
/>
</div>
<div class="mt-m">
<label for="messageEmail" class="block text-sm font-medium">Phone</label>
<input type="tel" class="input form-input" placeholder="+1 (555) 555-5555" />
<label for="contactPhone" class="block text-sm font-medium">Phone (optional)</label>
<input id="contactPhone" type="tel" class="input form-input" placeholder="+1 (555) 555-5555" />
</div>
<div class="mt-m">
<label for="contactMessage" class="block text-sm font-medium">Message</label>
Expand Down Expand Up @@ -53,5 +59,5 @@ permalink: 'contact/index.html'
</div>
</custom-masonry>

<script src="/assets/scripts/bundle/contact.js" async defer></script>
<script src="/assets/scripts/bundle/hcaptcha/index.min.js"></script>
<script src="/assets/scripts/components/contact.js" defer></script>
<script src="/assets/scripts/components/hcaptcha/index.min.js"></script>
62 changes: 0 additions & 62 deletions src/assets/scripts/bundle/contact.js

This file was deleted.

84 changes: 84 additions & 0 deletions src/assets/scripts/components/contact.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
const showFlash = (flash, message, cssClass) => {
flash.classList.add(cssClass);
flash.innerHtml = message;
flash.classList.remove('is-hidden');
setTimeout(() => {
flash.classList.add('is-hidden');
flash.classList.remove(cssClass);
}, 10000);
};

document.addEventListener('DOMContentLoaded', () => {
const hcaptcha = document.getElementById('h-captcha');

const submitContactForm = async () => {
const form = document.getElementById('contactForm');
const flash = document.getElementById('contactFlash');
const btn = document.getElementById('contactSubmitBtn');
const hcaptchaToken = hcaptcha.getAttribute('data-token');

if (!hcaptchaToken) {
flash.classList.add('is-danger');
flash.innerHTML = 'An error occurred sending message! Please try again later.';
}

btn.setAttribute('disabled', '');

if (!flash) console.log('contactFlash div not found', flash);

const values = new URLSearchParams();
document
.querySelectorAll('#contactForm .input')
.forEach(element => values.append(element.id, element.value));
values.append('hcaptchaResponse', hcaptchaToken);

console.error(values);

const response = await fetch(form.action, {
method: form.method,
body: values,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
.then(response => {
console.log('Form submission response', response.body);
console.log('Form json', JSON.stringify(response.body ?? {}));
return response.json();
})
.catch(error => {
console.error('Error:', error);
});

if (response && response.status === 200) {
showFlash(flash, 'Message successfully sent!', 'is-primary');
} else {
setTimeout(() => {
btn.removeAttribute('disabled');
}, 5000);
showFlash(flash, 'An error occurred sending message! Please try again later.', 'is-danger');
}
};

hcaptcha.addEventListener('verified', e => {
const btn = document.getElementById('contactSubmitBtn');
hcaptcha.dataset.token = e.token;
btn.removeAttribute('disabled');
});

hcaptcha.addEventListener('error', e => {
console.log('error event', {error: e.error});
const btn = document.getElementById('contactSubmitBtn');
btn.setAttribute('disabled', '');

const flash = document.getElementById('contactFlash');
flash.classList.add('is-danger');
flash.innerHTML = 'An error occurred verifying your captcha! Please reload and try again later.';
});

const form = document.getElementById('contactForm');
form.addEventListener('submit', e => {
e.preventDefault();
submitContactForm();
});
});

0 comments on commit a5f1193

Please sign in to comment.