fix: Use cached logos and fallback icons for breach email
This commit is contained in:
Родитель
8df707bb8b
Коммит
6d760674cc
|
@ -112,6 +112,7 @@ async function confirmed (req, res, next, client = FxAOAuthClient) {
|
|||
|
||||
const data = {
|
||||
breachedEmail: email,
|
||||
breachLogos: req.app.locals.breachLogoMap,
|
||||
ctaHref: getEmailCtaHref(utmCampaignId, 'dashboard-cta'),
|
||||
heading: getMessage('email-breach-summary'),
|
||||
recipientEmail: email,
|
||||
|
|
|
@ -20,7 +20,7 @@ import { getMessage } from '../utils/fluent.js'
|
|||
import { generateToken } from '../utils/csrf.js'
|
||||
import {
|
||||
EmailTemplateType,
|
||||
getNotifictionDummyData,
|
||||
getNotificationDummyData,
|
||||
getVerificationDummyData,
|
||||
getMonthlyDummyData,
|
||||
getSignupReportDummyData,
|
||||
|
@ -41,7 +41,7 @@ function getTemplatesData () {
|
|||
[EmailTemplateType.Notification]: {
|
||||
label: 'Breach notification',
|
||||
template: getPreviewTemplate(
|
||||
getNotifictionDummyData(EMAIL_TEST_RECIPIENT),
|
||||
getNotificationDummyData(EMAIL_TEST_RECIPIENT),
|
||||
breachAlertEmailPartial
|
||||
)
|
||||
},
|
||||
|
|
|
@ -151,6 +151,7 @@ async function notify (req, res) {
|
|||
if (!notifiedRecipients.includes(breachedEmail)) {
|
||||
const data = {
|
||||
breachData: breachAlert,
|
||||
breachLogos: req.app.locals.breachLogoMap,
|
||||
breachedEmail,
|
||||
ctaHref: getEmailCtaHref(utmCampaignId, 'dashboard-cta'),
|
||||
heading: getMessage('email-spotted-new-breach'),
|
||||
|
|
|
@ -5,30 +5,63 @@
|
|||
/**
|
||||
* @param {object} breach
|
||||
* @param {Map<string, string>} logos Map of URLs to logos indexed by the domain name of the respective company
|
||||
* @param {boolean} isEmail Is the icon being used in an email template?
|
||||
* @returns {string} HTML for a breach logo (either an `img`, or a `span.breach-logo` containing the breached company's first letter)
|
||||
*/
|
||||
export function getBreachLogo (breach, logos) {
|
||||
const logo = logos.has(breach.Domain)
|
||||
? `<img src='${logos.get(breach.Domain)}' alt='' loading="lazy" class='breach-logo' height='32' />`
|
||||
: `<span role="img" aria-hidden='true' class='breach-logo' style='background-color: var(${getColorForName(breach.Name)});'>${breach.Name.substring(0, 1)}</span>`
|
||||
export function getBreachLogo (breach, logos, isEmail = false) {
|
||||
const logoIsAvailable = logos?.has(breach.Domain)
|
||||
|
||||
return logo
|
||||
if (logoIsAvailable) {
|
||||
return `<img src='${logos.get(breach.Domain)}' alt='' loading="lazy" class='breach-logo' height='32' />`
|
||||
}
|
||||
|
||||
const { className, variableName } = getColorForName(breach.Name)
|
||||
const backgroundStyle = !isEmail
|
||||
? `background-color: var(${variableName});`
|
||||
: ''
|
||||
const classNames = `breach-logo ${isEmail ? `breach-logo-email ${className}` : ''}`
|
||||
|
||||
return `<span role="img" aria-hidden='true' class='${classNames}' style='${backgroundStyle}'>${breach.Name.substring(0, 1)}</span>`
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @returns string CSS variable for a string-specific color
|
||||
* @returns {string} CSS variable for a string-specific color
|
||||
*/
|
||||
function getColorForName (name) {
|
||||
const logoColors = [
|
||||
'--blue-5',
|
||||
'--purple-5',
|
||||
'--green-05',
|
||||
'--violet-5',
|
||||
'--orange-5',
|
||||
'--yellow-5',
|
||||
'--red-5',
|
||||
'--pink-5'
|
||||
{
|
||||
className: 'bg-blue-5',
|
||||
variableName: '--blue-5'
|
||||
},
|
||||
{
|
||||
className: 'bg-purple-5',
|
||||
variableName: '--purple-5'
|
||||
},
|
||||
{
|
||||
className: 'bg-green-05',
|
||||
variableName: '--green-05'
|
||||
},
|
||||
{
|
||||
className: 'bg-violet-5',
|
||||
variableName: '--violet-5'
|
||||
},
|
||||
{
|
||||
className: 'bg-orange-5',
|
||||
variableName: '--orange-5'
|
||||
},
|
||||
{
|
||||
className: 'bg-yellow-5',
|
||||
variableName: '--yellow-5'
|
||||
},
|
||||
{
|
||||
className: 'bg-red-5',
|
||||
variableName: '--red-5'
|
||||
},
|
||||
{
|
||||
className: 'bg-pink-5',
|
||||
variableName: '--pink-5'
|
||||
}
|
||||
]
|
||||
|
||||
const charValue = name
|
||||
|
|
|
@ -210,13 +210,17 @@ async function unsubscribeFromMonthlyReport (req) {
|
|||
await updateMonthlyEmailOptout(urlQuery.token)
|
||||
}
|
||||
|
||||
const breachDummyLogo = new Map([
|
||||
['adobe.com', '/images/logo_cache/adobe.com.ico']
|
||||
])
|
||||
|
||||
/**
|
||||
* Dummy data for populating the breach notification email preview.
|
||||
*
|
||||
* @param {string} recipient
|
||||
* @returns {object} Breach dummy data
|
||||
*/
|
||||
const getNotifictionDummyData = (recipient) => ({
|
||||
const getNotificationDummyData = (recipient) => ({
|
||||
breachData: {
|
||||
Id: 1,
|
||||
Name: 'Adobe',
|
||||
|
@ -227,7 +231,6 @@ const getNotifictionDummyData = (recipient) => ({
|
|||
ModifiedDate: '2023-01-01T00:00:00.000Z',
|
||||
PwnCount: 123,
|
||||
Description: 'Example description',
|
||||
LogoPath: '/images/favicon-144.webp',
|
||||
DataClasses: [
|
||||
'email-addresses',
|
||||
'password-hints',
|
||||
|
@ -242,6 +245,7 @@ const getNotifictionDummyData = (recipient) => ({
|
|||
IsMalware: false
|
||||
},
|
||||
breachedEmail: recipient,
|
||||
breachLogos: breachDummyLogo,
|
||||
ctaHref: getEmailCtaHref('email-test-notification', 'dashboard-cta'),
|
||||
heading: getMessage('email-spotted-new-breach'),
|
||||
recipientEmail: recipient,
|
||||
|
@ -270,6 +274,7 @@ const getVerificationDummyData = (recipient) => ({
|
|||
*/
|
||||
const getMonthlyDummyData = (recipient) => ({
|
||||
breachedEmail: 'breached@email.com',
|
||||
breachLogos: breachDummyLogo,
|
||||
ctaHref: `${SERVER_URL}/user/breaches`,
|
||||
heading: getMessage('email-unresolved-heading'),
|
||||
monitoredEmails: {
|
||||
|
@ -294,7 +299,7 @@ const getMonthlyDummyData = (recipient) => ({
|
|||
|
||||
const getSignupReportDummyData = (recipient) => {
|
||||
const unsafeBreachesForEmail = [
|
||||
getNotifictionDummyData(recipient).breachData
|
||||
getNotificationDummyData(recipient).breachData
|
||||
]
|
||||
const breachesCount = unsafeBreachesForEmail.length
|
||||
const numPasswordsExposed = 1
|
||||
|
@ -316,6 +321,7 @@ const getSignupReportDummyData = (recipient) => {
|
|||
|
||||
return {
|
||||
breachedEmail: recipient,
|
||||
breachLogos: breachDummyLogo,
|
||||
ctaHref: getEmailCtaHref('email-test-notification', 'dashboard-cta'),
|
||||
heading: unsafeBreachesForEmail.length
|
||||
? getMessage('email-subject-found-breaches')
|
||||
|
@ -331,7 +337,7 @@ export {
|
|||
EmailTemplateType,
|
||||
getEmailCtaHref,
|
||||
getMonthlyDummyData,
|
||||
getNotifictionDummyData,
|
||||
getNotificationDummyData,
|
||||
getSignupReportDummyData,
|
||||
getUnsubscribeCtaHref,
|
||||
getVerificationDummyData,
|
||||
|
|
|
@ -202,6 +202,51 @@ const getStyles = () => `
|
|||
background-image: url('${images.logoDark}')
|
||||
}
|
||||
}
|
||||
|
||||
.bg-blue-5 {
|
||||
background-color: #aaf2ff;
|
||||
}
|
||||
|
||||
.bg-purple-5 {
|
||||
background-color: #e7dfff;
|
||||
}
|
||||
|
||||
.bg-green-05 {
|
||||
background-color: #e3fff3;
|
||||
}
|
||||
|
||||
.bg-violet-5 {
|
||||
background-color: #f7e2ff;
|
||||
}
|
||||
|
||||
.bg-orange-5 {
|
||||
background-color: #fff4de;
|
||||
}
|
||||
|
||||
.bg-yellow-5 {
|
||||
background-color: #ffc;
|
||||
}
|
||||
|
||||
.bg-red-5 {
|
||||
background-color: #ffdfe7;
|
||||
}
|
||||
|
||||
.bg-pink-5 {
|
||||
background-color: #ffdef0;
|
||||
}
|
||||
|
||||
.breach-logo {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.breach-logo-email {
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
`
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ const breachAlertCtaStyle = `
|
|||
`
|
||||
|
||||
const breachAlertEmailPartial = data => {
|
||||
const { breachData, breachedEmail, ctaHref } = data
|
||||
const { breachData, breachedEmail, breachLogos, ctaHref } = data
|
||||
|
||||
return `
|
||||
<tr>
|
||||
|
@ -32,7 +32,7 @@ const breachAlertEmailPartial = data => {
|
|||
'email-address': `<strong>${breachedEmail}</strong>`
|
||||
})}
|
||||
</p>
|
||||
${breachCardPartial(breachData)}
|
||||
${breachCardPartial(breachData, breachLogos)}
|
||||
<a
|
||||
href='${ctaHref}'
|
||||
style='${breachAlertCtaStyle}'
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
import { getLocale, getMessage } from '../../utils/fluent.js'
|
||||
import { formatDate } from '../../utils/format-date.js'
|
||||
import { getBreachLogo } from '../../utils/breach-logo.js'
|
||||
|
||||
const breachAlertTableStyle = `
|
||||
margin: auto
|
||||
|
@ -29,7 +30,14 @@ const breachAlertCardsTitleStyle = `
|
|||
`
|
||||
|
||||
const breachAlertCardsTitleImageStyle = `
|
||||
vertical-align: bottom;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
line-height: 2rem;
|
||||
overflow: hidden;
|
||||
margin-right: 0.5rem;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
width: 2rem;
|
||||
`
|
||||
|
||||
const breachAlertLabelStyle = `
|
||||
|
@ -50,9 +58,8 @@ const breachAlertValueStyle = `
|
|||
padding-bottom: 15px;
|
||||
`
|
||||
|
||||
const breachCardPartial = breachData => {
|
||||
const breachCardPartial = (breachData, breachLogos) => {
|
||||
const {
|
||||
LogoPath,
|
||||
AddedDate,
|
||||
DataClasses,
|
||||
Title
|
||||
|
@ -64,15 +71,15 @@ const breachCardPartial = breachData => {
|
|||
<td>
|
||||
<table style='${breachAlertCardsContainerStyle}'>
|
||||
<tr>
|
||||
<td style='${breachAlertCardsTitleStyle}'>
|
||||
<img
|
||||
height='25'
|
||||
src='${LogoPath}'
|
||||
style='${breachAlertCardsTitleImageStyle}'
|
||||
width='25'
|
||||
>
|
||||
${Title}
|
||||
</td>
|
||||
<td style='${breachAlertCardsTitleStyle}'>
|
||||
<span
|
||||
class='breachLogoWrapper'
|
||||
style='${breachAlertCardsTitleImageStyle}'
|
||||
>
|
||||
${getBreachLogo(breachData, breachLogos, true)}
|
||||
</span>
|
||||
${Title}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style='padding: 24px;'>
|
||||
|
|
|
@ -47,6 +47,7 @@ const ctaStyle = `
|
|||
const signupReportEmailPartial = data => {
|
||||
const {
|
||||
breachedEmail,
|
||||
breachLogos,
|
||||
emailBreachStats,
|
||||
unsafeBreachesForEmail
|
||||
} = data
|
||||
|
@ -90,7 +91,7 @@ const signupReportEmailPartial = data => {
|
|||
${
|
||||
unsafeBreachesForEmail?.length
|
||||
? unsafeBreachesForEmail.map(unsafeBreach => (
|
||||
breachCardPartial(unsafeBreach)
|
||||
breachCardPartial(unsafeBreach, breachLogos)
|
||||
)).join('')
|
||||
: ''
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче