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 |
|||
|
|||
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 |
|||
div Local Red Action |
|||
div P.O. Box ######## |
|||
div McKees Rocks, PA 15136 |
|||
div USA |
|||
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 You can request to stop receiving these emails in writing at: |
|||
//- address |
|||
//- div Digital Telepresence, LLC |
|||
//- div P.O. Box ######## |
|||
//- 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: |
|||
| |
|||
| Local Red Action |
|||
| P.O. Box ######## |
|||
| McKees Rocks, PA 15136 |
|||
| USA |
|||
//- | You can request to stop receiving these emails in writing at: |
|||
//- | |
|||
//- | #{site.company} |
|||
//- | P.O. Box ######## |
|||
//- | McKees Rocks, PA 15136 |
|||
//- | 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 |
|||
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 { |
|||
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 |
|||
extends ../layouts/html/system-message |
|||
block content |
|||
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. |
|||
p If you did not sign up for a new account at #{site.name}, please disregard this message. |
@ -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 |
|||
| |
|||
| 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. |
|||
| |
|||
| Thank you for supporting your local Republican committee and candidates! |
|||
| |
|||
include ../common/text/footer |
|||
extends ../layouts/text/system-message |
|||
block content |
|||
| |
|||
| 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. |
|||
| |
|||
| If you did not sign up for a new account at #{site.name}, please disregard this message. |
|||
| |
@ -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