DTP Base provides a scalable and secure Node.js application development harness ready for production service.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

119 lines
3.5 KiB

// email.js
// Copyright (C) 2024 Digital Telepresence, LLC
// All Rights Reserved
'use strict';
import path from 'node:path';
import express from 'express';
import SvgCaptcha from 'svg-captcha';
import { SiteController, SiteError} from '../../lib/site-lib.js';
export default class EmailController extends SiteController {
static get name ( ) { return 'EmailController'; }
static get slug ( ) { return 'email'; }
constructor (dtp) {
super(dtp, EmailController);
}
async start ( ) {
const { jobQueue: jobQueueService, limiter: limiterService } = this.dtp.services;
SvgCaptcha.loadFont(path.join(this.dtp.config.root, 'client', 'fonts', 'green-nature.ttf'));
this.emailJobQueue = jobQueueService.getJobQueue('email', {
attempts: 3
});
const router = express.Router();
this.dtp.app.use('/email', router);
router.post(
'/verify',
limiterService.create(limiterService.config.email.postEmailVerify),
this.postEmailVerify.bind(this),
);
router.get(
'/verify',
limiterService.create(limiterService.config.email.getEmailVerify),
this.getEmailVerify.bind(this),
);
router.get(
'/opt-out',
limiterService.create(limiterService.config.email.getEmailOptOut),
this.getEmailOptOut.bind(this),
);
return router;
}
async postEmailVerify (req, res, next) {
const { email: emailService } = this.dtp.services;
try {
// if the session doesn't *have* an emailVerify captcha challenge, they
// didn't start by requesting the form (and are most likely automated)
if (!req.session.dtp || !req.session.dtp.captcha || !req.session.dtp.captcha.emailVerify) {
throw new SiteError(403, 'Invalid input');
}
// If the captcha text entered does not exactly match the text stored in
// the session, reject the request.
if (req.body.captcha.trim() !== req.session.dtp.captcha.emailVerify) {
throw new SiteError(403, 'The captcha text entered does not match');
}
await emailService.verifyToken(req.body.token);
res.render('email/verify-success');
} catch (error) {
this.log.error('failed to verify email', { error });
return next(error);
}
}
async getEmailOptOut (req, res, next) {
const { user: userService } = this.dtp.services;
try {
await userService.emailOptOut(req.query.u, req.query.c);
res.render('email/opt-out-success');
} catch (error) {
this.log.error('failed to opt-out from email', {
userId: req.query.t,
category: req.query.c,
error,
});
return next(error);
}
}
async getEmailVerify (req, res, next) {
const { email: emailService } = this.dtp.services;
try {
res.locals.token = await emailService.getVerificationToken(req.query.t);
if (!res.locals.token) {
throw new SiteError(404, 'Verification token not found');
}
res.locals.captcha = SvgCaptcha.create({
size: Math.round(Math.random() * 2) + 4,
width: 280,
height: 80,
noise: Math.floor(Math.random() * 2) + 1,
// background: '#d8d8d8',
color: false,
});
req.session.dtp = req.session.dtp || { };
req.session.dtp.captcha = req.session.dtp.captcha || { };
req.session.dtp.captcha.emailVerify = res.locals.captcha.text;
res.render('email/verify-form');
} catch (error) {
this.log.error('failed to verify email', { error });
return next(error);
}
}
}