This commit is contained in:
Florian Zia 2023-02-28 10:58:47 -05:00
Родитель 654cbc5d4c
Коммит 70b05456b5
6 изменённых файлов: 137 добавлений и 31 удалений

101
package-lock.json сгенерированный
Просмотреть файл

@ -17,7 +17,7 @@
"@maxmind/geoip2-node": "^3.1.0",
"@sentry/node": "7.36.0",
"@sentry/tracing": "^7.36.0",
"body-parser": "^1.20.1",
"body-parser": "^1.20.2",
"client-oauth2": "4.3.3",
"connect-redis": "6.1.3",
"cookie-parser": "^1.4.6",
@ -2775,12 +2775,12 @@
"dev": true
},
"node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
@ -2788,7 +2788,7 @@
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
@ -5257,6 +5257,29 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
},
"node_modules/express/node_modules/body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8",
"npm": "1.2.8000 || >= 1.4.16"
}
},
"node_modules/express/node_modules/cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
@ -5265,6 +5288,20 @@
"node": ">= 0.6"
}
},
"node_modules/express/node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@ -9700,9 +9737,9 @@
}
},
"node_modules/raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"dependencies": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
@ -13886,12 +13923,12 @@
"dev": true
},
"body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"version": "1.20.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
"requires": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"content-type": "~1.0.5",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
@ -13899,7 +13936,7 @@
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
}
@ -15667,10 +15704,40 @@
"vary": "~1.1.2"
},
"dependencies": {
"body-parser": {
"version": "1.20.1",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
"requires": {
"bytes": "3.1.2",
"content-type": "~1.0.4",
"debug": "2.6.9",
"depd": "2.0.0",
"destroy": "1.2.0",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
"qs": "6.11.0",
"raw-body": "2.5.1",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
}
},
"cookie": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw=="
},
"raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"requires": {
"bytes": "3.1.2",
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"unpipe": "1.0.0"
}
}
}
},
@ -18997,9 +19064,9 @@
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raw-body": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
"requires": {
"bytes": "3.1.2",
"http-errors": "2.0.0",

Просмотреть файл

@ -11,7 +11,7 @@
"@maxmind/geoip2-node": "^3.1.0",
"@sentry/node": "7.36.0",
"@sentry/tracing": "^7.36.0",
"body-parser": "^1.20.1",
"body-parser": "^1.20.2",
"client-oauth2": "4.3.3",
"connect-redis": "6.1.3",
"cookie-parser": "^1.4.6",

Просмотреть файл

@ -32,6 +32,7 @@ function getTemplatesData () {
return {
[EmailTemplateType.Verification]: {
label: 'Email verification',
data: getVerificationDummyData(EMAIL_TEST_RECIPIENT), // TODO: Remove - only for dev testing
template: getPreviewTemplate(
getVerificationDummyData(EMAIL_TEST_RECIPIENT),
verifyPartial
@ -39,6 +40,7 @@ function getTemplatesData () {
},
[EmailTemplateType.Notification]: {
label: 'Breach notification',
data: getNotifictionDummyData(EMAIL_TEST_RECIPIENT), // TODO: Remove - only for dev testing
template: getPreviewTemplate(
getNotifictionDummyData(EMAIL_TEST_RECIPIENT),
breachAlertEmailPartial
@ -46,6 +48,7 @@ function getTemplatesData () {
},
[EmailTemplateType.Monthly]: {
label: 'Monthly unresolved breaches',
data: getMonthlyDummyData(EMAIL_TEST_RECIPIENT), // TODO: Remove - only for dev testing
template: getPreviewTemplate(
getMonthlyDummyData(EMAIL_TEST_RECIPIENT),
monthlyUnresolvedEmailPartial
@ -53,6 +56,7 @@ function getTemplatesData () {
},
[EmailTemplateType.SignupReport]: {
label: 'Signup report',
data: getSignupReportDummyData(EMAIL_TEST_RECIPIENT), // TODO: Remove - only for dev testing
template: getPreviewTemplate(
getSignupReportDummyData(EMAIL_TEST_RECIPIENT),
signupReportEmailPartial

Просмотреть файл

@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import bodyParser from 'body-parser'
import { Router } from 'express'
import { asyncMiddleware } from '../middleware/util.js'
@ -11,8 +12,14 @@ import { dashboardPage } from '../controllers/dashboard.js'
import { breachesPage } from '../controllers/breaches.js'
import { dataRemovalPage } from '../controllers/data-removal.js'
import { settingsPage } from '../controllers/settings.js'
import {
getUnsubscribeUrl,
getMonthlyUnsubscribeUrl,
postUnsubscribe
} from '../utils/email.js'
const router = Router()
const urlEncodedParser = bodyParser.urlencoded({ extended: false })
// dashboard page
router.get('/dashboard', requireSessionUser, dashboardPage)
@ -29,4 +36,18 @@ router.get('/settings', requireSessionUser, settingsPage)
// sign the user out
router.get('/logout', asyncMiddleware(logout))
// unsubscribe from emails
router.use('/unsubscribe', urlEncodedParser)
router.get('/unsubscribe', urlEncodedParser, asyncMiddleware(getUnsubscribeUrl))
router.get(
'/unsubscribe-monthly',
urlEncodedParser,
asyncMiddleware(getMonthlyUnsubscribeUrl)
)
router.post(
'/unsubscribe',
requireSessionUser,
asyncMiddleware(postUnsubscribe)
)
export default router

Просмотреть файл

@ -109,6 +109,7 @@ function getVerificationUrl (subscriber) {
}
function getUnsubscribeUrl (subscriber, emailType) {
console.log('getUnsubscribeUrl')
// TODO: email unsubscribe is broken for most emails
const token = Object.hasOwn(subscriber, 'verification_token')
? subscriber.verification_token
@ -125,6 +126,7 @@ function getUnsubscribeUrl (subscriber, emailType) {
}
function getMonthlyUnsubscribeUrl (subscriber, campaign, content) {
console.log('getMonthlyUnsubscribeUrl')
// TODO: create new subscriptions section in settings to manage
// all emails and avoid one-off routes like this
if (!subscriber.primary_verification_token) {
@ -138,6 +140,10 @@ function getMonthlyUnsubscribeUrl (subscriber, campaign, content) {
return appendUtmParams(url, campaign, content)
}
function postUnsubscribe () {
console.log('postUnsubscribe')
}
/**
* Dummy data for populating the breach notification email preview
*
@ -175,7 +181,7 @@ const getNotifictionDummyData = (recipient) => ({
recipientEmail: recipient,
subscriberId: 123,
supportedLocales: ['en'],
unsubscribeUrl: SERVER_URL,
unsubscribeUrl: getUnsubscribeUrl(recipient, 'email-type'),
utmCampaign: ''
})
@ -186,12 +192,12 @@ const getNotifictionDummyData = (recipient) => ({
* @returns {object} Email verification dummy data
*/
const getVerificationDummyData = (recipient) => ({
recipientEmail: recipient,
ctaHref: SERVER_URL,
utmCampaign: 'email_verify',
unsubscribeUrl: SERVER_URL,
heading: getMessage('email-verify-heading'),
subheading: getMessage('email-verify-subhead')
recipientEmail: recipient,
subheading: getMessage('email-verify-subhead'),
unsubscribeUrl: getUnsubscribeUrl(recipient, 'email-type'),
utmCampaign: 'email_verify'
})
/**
@ -201,13 +207,9 @@ const getVerificationDummyData = (recipient) => ({
* @returns {object} Monthly unresolved breaches dummy data
*/
const getMonthlyDummyData = (recipient) => ({
recipientEmail: recipient,
ctaHref: SERVER_URL,
utmCampaign: '',
unsubscribeUrl: SERVER_URL,
heading: getMessage('email-unresolved-heading'),
subheading: getMessage('email-unresolved-subhead'),
breachedEmail: 'breached@email.com',
ctaHref: SERVER_URL,
heading: getMessage('email-unresolved-heading'),
monitoredEmails: {
count: 2
},
@ -215,7 +217,11 @@ const getMonthlyDummyData = (recipient) => ({
count: 3,
numResolved: 2,
numUnresolved: 1
}
},
recipientEmail: recipient,
subheading: getMessage('email-unresolved-subhead'),
unsubscribeUrl: getMonthlyUnsubscribeUrl(recipient, 'campaign', 'content'),
utmCampaign: ''
})
/**
@ -257,7 +263,7 @@ const getSignupReportDummyData = (recipient) => {
recipientEmail: recipient,
subscriberId: 123,
unsafeBreachesForEmail,
unsubscribeUrl: SERVER_URL,
unsubscribeUrl: getUnsubscribeUrl(recipient, 'email-type'),
utmCampaign: ''
}
}
@ -273,5 +279,6 @@ export {
getVerificationDummyData,
getVerificationUrl,
initEmail,
postUnsubscribe,
sendEmail
}

Просмотреть файл

@ -52,6 +52,13 @@ export const emailPreview = (data) => {
<custom-select name='email-template'>
${getPreviewOptions(currentTemplateKey, emailTemplates)}
</custom-select>
<pre>
${JSON.stringify(
data.email.data[currentTemplateKey].data,
null,
2
)}
</pre>
${
isAdminPreview