Skip to content

Commit

Permalink
Add hCaptcha support
Browse files Browse the repository at this point in the history
  • Loading branch information
robin4002 committed Sep 21, 2022
1 parent 04fa422 commit 117f318
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 32 deletions.
78 changes: 51 additions & 27 deletions library.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
const { verify } = require('hcaptcha');
const simpleRecaptcha = require('simple-recaptcha-new');

const meta = require.main.require('./src/meta');
const winston = require.main.require('winston');
const emailer = require.main.require('./src/emailer');
Expand All @@ -8,22 +10,23 @@ const routeHelpers = require.main.require('./src/routes/helpers');
const ContactPage = {
reCaptchaPubKey: null,
reCaptchaPrivKey: null,
hcaptchaPubKey: null,
hcaptchaSecretKey: null,
contactEmail: null,
messageFooter: null,
// init the plugin
async init(params) {
const { router, middleware } = params;
const { router } = params;

router.get('/contact', middleware.buildHeader, renderContact);
router.get('/api/contact', renderContact);
routeHelpers.setupPageRoute(router, '/contact', renderContact);
router.post('/contact', postContact);

// admin panel
routeHelpers.setupAdminPageRoute(router, '/admin/plugins/contact-page', [], renderAdmin);

try {
const options = await meta.settings.get('contactpage');
for(let settingName of ["reCaptchaPubKey", "reCaptchaPrivKey", "contactEmail", "messageFooter"]) {
for(let settingName of ['reCaptchaPubKey', 'reCaptchaPrivKey', 'hcaptchaPubKey', 'hcaptchaSecretKey', 'contactEmail', 'messageFooter']) {
if (options.hasOwnProperty(settingName)) {
ContactPage[settingName] = options[settingName];
}
Expand All @@ -36,7 +39,8 @@ const ContactPage = {
// add public token to api
async getConfig(config) {
config.contactpage = {
reCaptchaPubKey: ContactPage.reCaptchaPubKey
reCaptchaPubKey: ContactPage.reCaptchaPubKey,
hcaptchaPubKey: ContactPage.hcaptchaPubKey
};
return config;
},
Expand All @@ -49,7 +53,7 @@ const ContactPage = {
return header;
},
async modifyEmail(mailData) {
if(mailData && mailData.template == "contact-page") {
if(mailData && mailData.template == 'contact-page') {
mailData = modifyFrom(mailData);
}
return mailData;
Expand All @@ -59,8 +63,9 @@ const ContactPage = {
function renderContact(req, res) {
return res.render('contact', {
recaptcha: ContactPage.reCaptchaPubKey,
hcaptcha: ContactPage.hcaptchaPubKey,
breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[contactpage:contact]]' }]),
title: "Contact"
title: '[[contactpage:contact]]'
});
}

Expand All @@ -69,43 +74,62 @@ async function postContact(req, res) {
return res.status(400).json({ success: false, msg: '[[contactpage:error.incomplete]]' });
}

if (ContactPage.reCaptchaPubKey) {
if(!req.body['g-recaptcha-response']) {
return res.status(400).json({ success: false, msg: '[[contactpage:error.incomplete.recaptcha]]' });
try {
if (ContactPage.reCaptchaPubKey && ContactPage.reCaptchaPrivKey) {
if(!req.body['g-recaptcha-response']) {
return res.status(400).json({ success: false, msg: '[[contactpage:error.incomplete.recaptcha]]' });
}
await checkReCaptcha(ContactPage.reCaptchaPrivKey, req.ip, req.body['g-recaptcha-response']);
}
simpleRecaptcha(ContactPage.reCaptchaPrivKey, req.ip, req.body['g-recaptcha-response'], async (err) => {

if (ContactPage.hcaptchaPubKey && ContactPage.hcaptchaSecretKey) {
if(!req.body['h-captcha-response']) {
return res.status(400).json({ success: false, msg: '[[contactpage:error.incomplete.recaptcha]]' });
}
await verify(ContactPage.hcaptchaSecretKey, req.body['h-captcha-response']);
}
}
catch (err) {
return res.status(400).json({ success: false, msg: '[[contactpage:error.invalid.recaptcha]]' });
}

try {
await sendMail(req.body.email, req.body.name, req.body.subject, req.body.message);
res.json({ success: true });
}
catch (error) {
winston.error("[plugin/contactpage] Failed to send mail:" + error);
res.status(500).json({ success: false, message: '[[contactpage:error.mail]]' });
}
}

function checkReCaptcha(privateKey, remoteIP, response) {
return new Promise((resolve, reject) => {
simpleRecaptcha(privateKey, remoteIP, response, (err, resp) => {
if (err) {
return res.status(400).json({ success: false, msg: '[[contactpage:error.invalid.recaptcha]]' });
} else {
await sendMail(req.body.email, req.body.name, req.body.subject, req.body.message, res);
reject(err);
}
else {
resolve(resp);
}
});
} else {
await sendMail(req.body.email, req.body.name, req.body.subject, req.body.message, res);
}
});
}

async function sendMail(replyTo, name, subject, message, res) {
function sendMail(replyTo, name, subject, message) {
let mailParams = {
content_text: message.replace(/(?:\r\n|\r|\n)/g, '<br>'),
footer_text: ContactPage.messageFooter,
from_name: name,
subject: subject,
template: 'contact-page',
uid: 0,
replyTo,
replyTo
}

mailParams = Object.assign({}, emailer._defaultPayload, mailParams);

try {
await emailer.sendToEmail('contact-page', ContactPage.contactEmail, undefined, mailParams);
res.json({ success: true });
}
catch (error) {
winston.error("[plugin/contactpage] Failed to send mail:" + error);
res.status(500).json({ success: false, message: '[[contactpage:error.mail]]' });
}
return emailer.sendToEmail('contact-page', ContactPage.contactEmail, undefined, mailParams);
}

function modifyFrom(mailData) {
Expand Down
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
},
"homepage": "https://github.com/MinecraftForgeFrance/nodebb-plugin-contact-page#readme",
"dependencies": {
"hcaptcha": "^0.1.1",
"simple-recaptcha-new": "^1.1.1"
},
"nbbpm": {
Expand Down
19 changes: 18 additions & 1 deletion static/js/contact.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,31 @@ define('forum/contact', ['translator', 'jquery-form'], function(translator) {
}

// Load Google recaptcha if configured
if (config.contactpage.reCaptchaPubKey !== undefined) {
if (config.contactpage.reCaptchaPubKey) {
if (!$('script[src*="www.recaptcha.net/recaptcha/api.js"]').length) {
injectScript('//www.recaptcha.net/recaptcha/api.js?onload=__contactPageRenderReCaptcha__&render=explicit');
} else if (grecaptcha !== undefined) {
window.__contactPageRenderReCaptcha__();
}
}

// Load hcaptcha if configured
if (config.contactpage.hcaptchaPubKey) {
if (!$('script[src*="js.hcaptcha.com/1/api.js"]').length) {
injectScript('//js.hcaptcha.com/1/api.js?render=explicit');
}
}
};

// For a unknown reason, h-captcha won't load with onload query arg, so the plugin use ajaxify.end event to render it.
$(window).on('action:ajaxify.end', function (evt, data) {
if (data.url == 'contact' && config.contactpage.hcaptchaPubKey && hcaptcha) {
hcaptcha.render('contact-page-h-captcha', {
sitekey: config.contactpage.hcaptchaPubKey,
});
}
});

return Contact;
});

Expand Down
25 changes: 23 additions & 2 deletions static/templates/admin/plugins/contact-page.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<div class="col-xs-12">
<h2>Contact page settings</h2>
<div class="row">
<div class="col-lg-6 col-xs-12">
<div class="col-md-6 col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">Main settings</div>
<div class="panel-body">
Expand All @@ -22,7 +22,7 @@
</div>
</div>
</div>
<div class="col-lg-6 col-xs-12">
<div class="col-md-6 col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">Google Re-Captcha (optional)</div>
<div class="panel-body">
Expand All @@ -42,6 +42,27 @@
</div>
</div>
</div>

<div class="col-md-6 col-xs-12">
<div class="panel panel-default">
<div class="panel-heading">hCaptcha (optional)</div>
<div class="panel-body">
<div class="form-group">
<label for="hcaptchaPubKey">
API key
</label>
<input class="form-control" type="text" name="hcaptchaPubKey" id="hcaptchaPubKey" />
</div>

<div class="form-group">
<label for="hcaptchaSecretKey">
Secret key (keep it secret)
</label>
<input class="form-control" type="text" name="hcaptchaSecretKey" id="hcaptchaSecretKey" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
Expand Down
12 changes: 10 additions & 2 deletions static/templates/contact.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,22 @@
</div>
</div>
<input type="hidden" name="_csrf" value="{config.csrf_token}" />
<!-- IF recaptcha -->
{{{if recaptcha}}}
<div class="form-group">
<label class="control-label col-sm-2">[[contactpage:form.captcha]]</label>
<div class="col-sm-10">
<div id="contact-page-google-recaptcha"></div>
</div>
</div>
<!-- ENDIF recaptcha -->
{{{end}}}
{{{if hcaptcha}}}
<div class="form-group">
<label class="control-label col-sm-2">[[contactpage:form.captcha]]</label>
<div class="col-sm-10">
<div id="contact-page-h-captcha"></div>
</div>
</div>
{{{end}}}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button id="send" class="btn btn-primary">[[contactpage:btn.send]]</button>
Expand Down

0 comments on commit 117f318

Please sign in to comment.