22 changed files with 399 additions and 74 deletions
@ -0,0 +1,78 @@ |
|||||
|
// email.js
|
||||
|
// Copyright (C) 2021 Digital Telepresence, LLC
|
||||
|
// License: Apache-2.0
|
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
const DTP_COMPONENT_NAME = 'email'; |
||||
|
|
||||
|
const express = require('express'); |
||||
|
|
||||
|
const { SiteController/*, SiteError*/ } = require('../../lib/site-lib'); |
||||
|
|
||||
|
class EmailController extends SiteController { |
||||
|
|
||||
|
constructor (dtp) { |
||||
|
super(dtp, DTP_COMPONENT_NAME); |
||||
|
} |
||||
|
|
||||
|
async start ( ) { |
||||
|
const { jobQueue: jobQueueService, limiter: limiterService } = this.dtp.services; |
||||
|
|
||||
|
this.emailJobQueue = jobQueueService.getJobQueue('email', { |
||||
|
attempts: 3 |
||||
|
}); |
||||
|
|
||||
|
const router = express.Router(); |
||||
|
this.dtp.app.use('/email', router); |
||||
|
|
||||
|
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 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 { |
||||
|
await emailService.verifyToken(req.query.t); |
||||
|
res.render('email/verify-success'); |
||||
|
} catch (error) { |
||||
|
this.log.error('failed to verify email', { error }); |
||||
|
return next(error); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
module.exports = { |
||||
|
slug: 'email', |
||||
|
name: 'email', |
||||
|
create: async (dtp) => { |
||||
|
let controller = new EmailController(dtp); |
||||
|
return controller; |
||||
|
}, |
||||
|
}; |
@ -0,0 +1,19 @@ |
|||||
|
// email-log.js
|
||||
|
// Copyright (C) 2022 DTP Technologies, LLC
|
||||
|
// All Rights Reserved
|
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
const mongoose = require('mongoose'); |
||||
|
const Schema = mongoose.Schema; |
||||
|
|
||||
|
const EmailLogSchema = new Schema({ |
||||
|
created: { type: Date, default: Date.now, required: true, index: -1 }, |
||||
|
from: { type: String, required: true, }, |
||||
|
to: { type: String, required: true }, |
||||
|
to_lc: { type: String, required: true, lowercase: true, index: 1 }, |
||||
|
subject: { type: String }, |
||||
|
messageId: { type: String }, |
||||
|
}); |
||||
|
|
||||
|
module.exports = mongoose.model('EmailLog', EmailLogSchema); |
@ -0,0 +1,18 @@ |
|||||
|
// email-verify.js
|
||||
|
// Copyright (C) 2021 Digital Telepresence, LLC
|
||||
|
// License: Apache-2.0
|
||||
|
|
||||
|
'use strict'; |
||||
|
|
||||
|
const mongoose = require('mongoose'); |
||||
|
|
||||
|
const Schema = mongoose.Schema; |
||||
|
|
||||
|
const EmailVerifySchema = new Schema({ |
||||
|
created: { type: Date, default: Date.now, required: true, index: -1, expires: '30d' }, |
||||
|
verified: { type: Date }, |
||||
|
user: { type: Schema.ObjectId, required: true, index: 1, ref: 'User' }, |
||||
|
token: { type: String, required: true }, |
||||
|
}); |
||||
|
|
||||
|
module.exports = mongoose.model('EmailVerify', EmailVerifySchema); |
@ -1,10 +1,8 @@ |
|||||
.common-footer |
.common-footer |
||||
|
p This email was sent to #{recipient.email} because you selected to receive emails from #{site.name}. You can #[a(href=`https://${site.domain}/email/opt-out?u=${recipient._id}&c=marketing`) opt out] at any time to stop receiving these emails. |
||||
p This email was sent to #{user.email} because you selected to receive emails from LocalRedAction.com. You can #[a(href=`https://localredaction.com/opt-out/${voter._id}/email`) opt out] at any time to stop receiving these emails. |
//- p You can request to stop receiving these emails in writing at: |
||||
|
//- address |
||||
p You can request to stop receiving these emails in writing at: |
//- div Digital Telepresence, LLC |
||||
address |
//- div P.O. Box ######## |
||||
div Local Red Action |
//- div McKees Rocks, PA 15136 |
||||
div P.O. Box ######## |
//- div USA |
||||
div McKees Rocks, PA 15136 |
|
||||
div USA |
|
@ -1 +1,2 @@ |
|||||
.greeting Dear #{voter.name}, |
.common-title= emailTitle || `Greetings from ${site.name}!` |
||||
|
.common-slogan= site.description |
@ -1,9 +1,10 @@ |
|||||
|
| |
||||
| - - - |
| - - - |
||||
| This email was sent to #{user.email} because you selected to receive emails from LocalRedAction.com. Visit #{`https://localredaction.com/opt-out/${voter._id}/email`} to opt out and stop receiving these emails. |
| This email was sent to #{recipient.email} because you selected to receive emails from #{site.name}. Visit #{`https://${site.domain}/email/opt-out?u=${recipient._id}&c=marketing`} to opt out and stop receiving these emails. |
||||
| |
| |
||||
| You can request to stop receiving these emails in writing at: |
//- | You can request to stop receiving these emails in writing at: |
||||
| |
//- | |
||||
| Local Red Action |
//- | #{site.company} |
||||
| P.O. Box ######## |
//- | P.O. Box ######## |
||||
| McKees Rocks, PA 15136 |
//- | McKees Rocks, PA 15136 |
||||
| USA |
//- | USA |
@ -1 +1,2 @@ |
|||||
| Dear #{voter.name}, |
| Dear #{recipient.displayName || recipient.username}, |
||||
|
| |
@ -0,0 +1,3 @@ |
|||||
|
extends ../layouts/html/system-message |
||||
|
block message-body |
||||
|
.content-message!= htmlMessage |
@ -1,27 +1,4 @@ |
|||||
doctype html |
extends ../layouts/html/system-message |
||||
html(lang='en') |
block content |
||||
head |
p Welcome to #{site.name}! Please visit #[a(href=`https://${site.domain}/email/verify?t=${emailVerifyToken}`)= `https://${site.domain}/email/verify?t=${emailVerifyToken}`] to verify your email address and enable all features on your new account. |
||||
meta(charset='UTF-8') |
p If you did not sign up for a new account at #{site.name}, please disregard this message. |
||||
meta(name='viewport', content='width=device-width, initial-scale=1.0') |
|
||||
meta(name='description', content= pageDescription || siteDescription) |
|
||||
|
|
||||
title= pageTitle ? `${pageTitle} | ${site.name}` : site.name |
|
||||
|
|
||||
style(type="text/css"). |
|
||||
html, body { |
|
||||
margin: 0; |
|
||||
padding: 0; |
|
||||
} |
|
||||
.greeting { font-size: 1.5em; margin-bottom: 16px; } |
|
||||
.message {} |
|
||||
|
|
||||
body |
|
||||
|
|
||||
include ../common/html/header |
|
||||
|
|
||||
.message |
|
||||
p Welcome to #{service.name}! Please visit #[a(href=`https://localredaction.com/verify?t=${emailVerifyToken}`)= `https://localredaction.com/verify?t=${emailVerifyToken}`] to verify your email address. |
|
||||
|
|
||||
p Thank you for supporting your local Republican committee and candidates! |
|
||||
|
|
||||
include ../common/html/footer |
|
@ -0,0 +1,106 @@ |
|||||
|
doctype html |
||||
|
html(lang='en') |
||||
|
head |
||||
|
meta(charset='UTF-8') |
||||
|
meta(name='viewport', content='width=device-width, initial-scale=1.0') |
||||
|
meta(name='description', content= pageDescription || siteDescription) |
||||
|
|
||||
|
title= pageTitle ? `${pageTitle} | ${site.name}` : site.name |
||||
|
|
||||
|
style(type="text/css"). |
||||
|
html, body { |
||||
|
padding: 0; |
||||
|
margin: 0; |
||||
|
background-color: #ffffff; |
||||
|
color: #1a1a1a; |
||||
|
} |
||||
|
|
||||
|
section { |
||||
|
padding: 20px 0; |
||||
|
background-color: #ffffff; |
||||
|
color: #1a1a1a; |
||||
|
} |
||||
|
|
||||
|
section.section-muted { |
||||
|
background-color: #f8f8f8; |
||||
|
color: #2a2a2a; |
||||
|
} |
||||
|
|
||||
|
.common-title { |
||||
|
max-width: 640px; |
||||
|
margin: 0 auto 8px auto; |
||||
|
font-size: 1.5em; |
||||
|
} |
||||
|
|
||||
|
.common-greeting { |
||||
|
max-width: 640px; |
||||
|
margin: 0 auto 20px auto; |
||||
|
font-size: 1.1em; |
||||
|
} |
||||
|
|
||||
|
.common-slogan { |
||||
|
max-width: 640px; |
||||
|
margin: 0 auto; |
||||
|
font-size: 1.1em; |
||||
|
font-style: italic; |
||||
|
} |
||||
|
|
||||
|
.content-message { |
||||
|
max-width: 640px; |
||||
|
margin: 0 auto; |
||||
|
background: white; |
||||
|
color: black; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
.content-signature { |
||||
|
max-width: 640px; |
||||
|
margin: 0 auto; |
||||
|
background: white; |
||||
|
color: black; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
.common-footer { |
||||
|
max-width: 640px; |
||||
|
margin: 0 auto; |
||||
|
font-size: 10px; |
||||
|
} |
||||
|
|
||||
|
.channel-icon { |
||||
|
border-radius: 8px; |
||||
|
} |
||||
|
|
||||
|
.action-button { |
||||
|
padding: 6px 20px; |
||||
|
margin: 24px 0; |
||||
|
border: none; |
||||
|
border-radius: 20px; |
||||
|
outline: none; |
||||
|
background-color: #1093de; |
||||
|
color: #ffffff; |
||||
|
font-size: 16px; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
body |
||||
|
|
||||
|
include ../library |
||||
|
|
||||
|
section.section-muted |
||||
|
include ../../common/html/header |
||||
|
|
||||
|
section |
||||
|
.common-greeting |
||||
|
div Dear #{recipient.displayName || recipient.username}, |
||||
|
|
||||
|
block message-body |
||||
|
.content-message |
||||
|
block content |
||||
|
|
||||
|
.content-signature |
||||
|
p Thank you for your continued support! |
||||
|
p The #{site.name} team. |
||||
|
|
||||
|
section.section-muted |
||||
|
include ../../common/html/footer |
@ -0,0 +1,4 @@ |
|||||
|
- |
||||
|
function formatCount (count) { |
||||
|
return numeral(count).format((count > 1000) ? '0,0.0a' : '0,0'); |
||||
|
} |
@ -0,0 +1,10 @@ |
|||||
|
include ../library |
||||
|
include ../../common/text/header |
||||
|
| |
||||
|
block content |
||||
|
| |
||||
|
| Thank you for your continued support! |
||||
|
| |
||||
|
| The #{site.name} team. |
||||
|
| |
||||
|
include ../../common/text/footer |
@ -0,0 +1,5 @@ |
|||||
|
extends ../layouts/text/system-message |
||||
|
block content |
||||
|
| |
||||
|
| #{textMessage} |
||||
|
| |
@ -1,7 +1,7 @@ |
|||||
include ../common/text/header |
extends ../layouts/text/system-message |
||||
| |
block content |
||||
| Welcome to #{service.name}! Please visit #[a(href=`https://localredaction.com/verify?t=${emailVerifyToken}`)= `https://localredaction.com/verify?t=${emailVerifyToken}`] to verify your email address. |
| |
||||
| |
| Welcome to #{site.name}! Please visit #{`https://${site.domain}/email/verify?t=${emailVerifyToken}`} to verify your email address and enable all features on your new account. |
||||
| Thank you for supporting your local Republican committee and candidates! |
| |
||||
| |
| If you did not sign up for a new account at #{site.name}, please disregard this message. |
||||
include ../common/text/footer |
| |
@ -0,0 +1,12 @@ |
|||||
|
extends ../layouts/main |
||||
|
block content |
||||
|
|
||||
|
section.uk-section.uk-section-default.uk-section-small |
||||
|
.uk-container |
||||
|
h1 Email Verification |
||||
|
p You have successfully verified your email address, and have unlocked additional features of the site. |
||||
|
|
||||
|
a(href="/").uk-button.dtp-button-primary |
||||
|
span |
||||
|
i.fas.fa-home |
||||
|
span.uk-margin-small-left Home |
Loading…
Reference in new issue