From b3b421a67408a4a48d23c15341fcdf7aaf19b25a Mon Sep 17 00:00:00 2001 From: TheCatLady <52870424+TheCatLady@users.noreply.github.com> Date: Mon, 18 Oct 2021 10:08:50 -0400 Subject: [PATCH] fix(email): do not attempt to display logo if app URL not configured (#2125) * fix(email): do not attempt to display logo if app URL not configured * fix(email): prevent Gmail from turning usernames with periods into hyperlinks * fix(email): fix(email): use displayName instead of username/plexUserName and improve Gmail link fix --- server/entity/User.ts | 3 + server/lib/notifications/agents/email.ts | 22 +++++-- server/routes/user/index.ts | 2 +- .../email/generatedpassword/html.pug | 54 +++++++--------- server/templates/email/media-request/html.pug | 52 ++++++---------- server/templates/email/resetpassword/html.pug | 61 ++++++++----------- server/templates/email/test-email/html.pug | 45 ++++++-------- 7 files changed, 105 insertions(+), 134 deletions(-) diff --git a/server/entity/User.ts b/server/entity/User.ts index fc2729a5f..77f0e8b11 100644 --- a/server/entity/User.ts +++ b/server/entity/User.ts @@ -178,6 +178,7 @@ export class User { password: password, applicationUrl, applicationTitle, + recipientName: this.username, }, }); } catch (e) { @@ -214,6 +215,8 @@ export class User { resetPasswordLink, applicationUrl, applicationTitle, + recipientName: this.displayName, + recipientEmail: this.email, }, }); } catch (e) { diff --git a/server/lib/notifications/agents/email.ts b/server/lib/notifications/agents/email.ts index 6a06d718a..7cf45b47f 100644 --- a/server/lib/notifications/agents/email.ts +++ b/server/lib/notifications/agents/email.ts @@ -46,7 +46,8 @@ class EmailAgent private buildMessage( type: Notification, payload: NotificationPayload, - toEmail: string + recipientEmail: string, + recipientName?: string ): EmailOptions | undefined { const { applicationUrl, applicationTitle } = getSettings().main; @@ -54,12 +55,14 @@ class EmailAgent return { template: path.join(__dirname, '../../../templates/email/test-email'), message: { - to: toEmail, + to: recipientEmail, }, locals: { body: payload.message, applicationUrl, applicationTitle, + recipientName, + recipientEmail, }, }; } @@ -127,7 +130,7 @@ class EmailAgent '../../../templates/email/media-request' ), message: { - to: toEmail, + to: recipientEmail, }, locals: { requestType, @@ -143,6 +146,8 @@ class EmailAgent : undefined, applicationUrl, applicationTitle, + recipientName, + recipientEmail, }, }; } @@ -179,7 +184,12 @@ class EmailAgent payload.notifyUser.settings?.pgpKey ); await email.send( - this.buildMessage(type, payload, payload.notifyUser.email) + this.buildMessage( + type, + payload, + payload.notifyUser.email, + payload.notifyUser.displayName + ) ); } catch (e) { logger.error('Error sending email notification', { @@ -228,7 +238,9 @@ class EmailAgent this.getSettings(), user.settings?.pgpKey ); - await email.send(this.buildMessage(type, payload, user.email)); + await email.send( + this.buildMessage(type, payload, user.email, user.displayName) + ); } catch (e) { logger.error('Error sending email notification', { label: 'Notifications', diff --git a/server/routes/user/index.ts b/server/routes/user/index.ts index bb58e68b6..e6fa09cdb 100644 --- a/server/routes/user/index.ts +++ b/server/routes/user/index.ts @@ -109,7 +109,7 @@ router.post( const user = new User({ avatar: body.avatar ?? avatar, - username: body.username ?? body.email, + username: body.username, email: body.email, password: body.password, permissions: settings.main.defaultPermissions, diff --git a/server/templates/email/generatedpassword/html.pug b/server/templates/email/generatedpassword/html.pug index 129695abb..2fcb2e095 100644 --- a/server/templates/email/generatedpassword/html.pug +++ b/server/templates/email/generatedpassword/html.pug @@ -6,25 +6,6 @@ head meta(name='viewport' content='width=device-width, initial-scale=1') meta(name='format-detection' content='telephone=no, date=no, address=no, email=no') link(href='https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap' rel='stylesheet' media='screen') - //if mso - xml - o:officedocumentsettings - o:pixelsperinch 96 - style. - td, - th, - div, - p, - a, - h1, - h2, - h3, - h4, - h5, - h6 { - font-family: 'Segoe UI', sans-serif; - mso-line-height-rule: exactly; - } style. .title:hover * { text-decoration: underline; @@ -35,28 +16,35 @@ head width: 100% !important; } } -div(style='display: block; background-color: #111827;') - table(style='margin: 0 auto; font-family: Inter, Arial, Sans-Serif; color: #fff; font-size: 16px; width: 26rem;') +div(style='display: block; background-color: #111827; padding: 2.5rem 0;') + table(style='margin: 0 auto; font-family: Inter, Arial, sans-serif; color: #fff; font-size: 16px; width: 26rem;') tr td(style="text-align: center;") - a(href=applicationUrl) - img(src=applicationUrl +'/logo_full.png' style='width: 26rem; padding: 1rem; image-rendering: crisp-edges; image-rendering: -webkit-optimize-contrast;') + if applicationUrl + a(href=applicationUrl style='margin: 0 1rem;') + img(src=applicationUrl +'/logo_full.png' style='width: 26rem; image-rendering: crisp-edges; image-rendering: -webkit-optimize-contrast;') + else + div(style='margin: 0 1rem 2.5rem; font-size: 3em; font-weight: 700;') + | #{applicationTitle} + if recipientName !== recipientEmail + tr + td(style='text-align: center;') + div(style='margin: 1rem 0 0; font-size: 1.25em;') + | Hi, #{recipientName.replace(/\.|@/g, ((x) => x + '\ufeff'))}! tr td(style='text-align: center;') - div(style='margin: 0rem 1rem 1rem; font-size: 1.25em;') - span - | An account has been created for you at #{applicationTitle}. + div(style='margin: 1rem 0 0; font-size: 1.25em;') + | An account has been created for you at #{applicationTitle}. tr td(style='text-align: center;') - div(style='margin: 1rem 1rem 1rem; font-size: 1.25em;') - span - | Your new password is: - div(style='margin: 0rem 1rem 1rem; font-size: 1.25em;') - span + div(style='margin: 1rem 0 0; font-size: 1.25em;') + | Your password is: + div(style='font-size: 1.25em; font-weight: 500; line-height: 2.25em;') + span(style='padding: 0.5rem; font-weight: 500; border: 1px solid rgb(100,100,100); font-family: monospace;') | #{password} if applicationUrl tr td - a(href=applicationUrl style='display: block; margin: 1.5rem 3rem 2.5rem 3rem; text-decoration: none; font-size: 1.0em; line-height: 2.25em;') - span(style='padding: 0.2rem; font-weight: 500; text-align: center; border-radius: 10px; background-color: rgb(99,102,241); color: #fff; display: block; border: 1px solid rgba(255, 255, 255, 0.2);') + a(href=applicationUrl style='display: block; margin: 1.5rem 3rem 0; text-decoration: none; font-size: 1.0em; line-height: 2.25em;') + span(style='padding: 0.2rem; font-weight: 500; text-align: center; border-radius: 10px; background-color: rgb(99,102,241); color: #fff; display: block; border: 1px solid rgba(255,255,255,0.2);') | Open #{applicationTitle} diff --git a/server/templates/email/media-request/html.pug b/server/templates/email/media-request/html.pug index 6d3a97403..83db48d4b 100644 --- a/server/templates/email/media-request/html.pug +++ b/server/templates/email/media-request/html.pug @@ -6,25 +6,6 @@ head meta(name='viewport' content='width=device-width, initial-scale=1') meta(name='format-detection' content='telephone=no, date=no, address=no, email=no') link(href='https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap' rel='stylesheet' media='screen') - //if mso - xml - o:officedocumentsettings - o:pixelsperinch 96 - style. - td, - th, - div, - p, - a, - h1, - h2, - h3, - h4, - h5, - h6 { - font-family: 'Segoe UI', sans-serif; - mso-line-height-rule: exactly; - } style. .title:hover * { text-decoration: underline; @@ -35,31 +16,38 @@ head width: 100% !important; } } -div(style='display: block; background-color: #111827;') - table(style='margin: 0 auto; font-family: Inter, Arial, Sans-Serif; color: #fff; font-size: 16px; width: 26rem;') +div(style='display: block; background-color: #111827; padding: 2.5rem 0;') + table(style='margin: 0 auto; font-family: Inter, Arial, sans-serif; color: #fff; font-size: 16px; width: 26rem;') tr td(style="text-align: center;") - a(href=applicationUrl) - img(src=applicationUrl +'/logo_full.png' style='width: 26rem; padding: 1rem; image-rendering: crisp-edges; image-rendering: -webkit-optimize-contrast;') + if applicationUrl + a(href=applicationUrl style='margin: 0 1rem;') + img(src=applicationUrl +'/logo_full.png' style='width: 26rem; image-rendering: crisp-edges; image-rendering: -webkit-optimize-contrast;') + else + div(style='margin: 0 1rem 2.5rem; font-size: 3em; font-weight: 700;') + | #{applicationTitle} + if recipientName !== recipientEmail + tr + td(style='text-align: center;') + div(style='margin: 1rem 0 0; font-size: 1.25em;') + | Hi, #{recipientName.replace(/\.|@/g, ((x) => x + '\ufeff'))}! tr td(style='text-align: center;') - div(style='margin: 0rem 1rem 1rem; font-size: 1.25em;') - span - | #{body} + div(style='margin: 1rem 0 0; font-size: 1.25em;') + | #{body} tr td - div(style='box-sizing: border-box; margin: 0; width: 100%; color: #fff; border-radius: .75rem; padding: 1rem; border: 1px solid rgba(100, 100, 100, 1); background: linear-gradient(135deg, rgba(17, 24, 39, 0.47) 0%, rgb(17, 24, 39) 75%), url(' + imageUrl + ') center 25%/cover') + div(style='box-sizing: border-box; margin: 1.5rem 0 0; width: 100%; color: #fff; border-radius: .75rem; padding: 1rem; border: 1px solid rgb(100,100,100); background: linear-gradient(135deg, rgba(17,24,39,0.47) 0%, rgb(17,24,39) 75%), url(' + imageUrl + ') center 25%/cover') table(style='color: #fff; width: 100%;') tr td(style='vertical-align: top;') a(href=actionUrl style='display: block; max-width: 20rem; color: #fff; font-weight: 700; text-decoration: none; margin: 0 1rem 0.25rem 0; font-size: 1.3em; line-height: 1.25em; margin-bottom: 5px;' class='title') - span - | #{mediaName} + | #{mediaName} div(style='overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #d1d5db; font-size: .975em; line-height: 1.45em; padding-top: .25rem; padding-bottom: .25rem;') span(style='display: block;') b(style='color: #9ca3af; font-weight: 700;') | Requested By  - | #{requestedBy} + | #{requestedBy.replace(/\.|@/g, ((x) => x + '\ufeff'))} each extra in mediaExtra span(style='display: block;') b(style='color: #9ca3af; font-weight: 700;') @@ -76,6 +64,6 @@ div(style='display: block; background-color: #111827;') if actionUrl tr td - a(href=actionUrl style='display: block; margin: 1.5rem 3rem 2.5rem 3rem; text-decoration: none; font-size: 1.0em; line-height: 2.25em;') - span(style='padding: 0.2rem; font-weight: 500; text-align: center; border-radius: 10px; background-color: rgb(99,102,241); color: #fff; display: block; border: 1px solid rgba(255, 255, 255, 0.2);') + a(href=actionUrl style='display: block; margin: 1.5rem 3rem 0; text-decoration: none; font-size: 1.0em; line-height: 2.25em;') + span(style='padding: 0.2rem; font-weight: 500; text-align: center; border-radius: 10px; background-color: rgb(99,102,241); color: #fff; display: block; border: 1px solid rgba(255,255,255,0.2);') | Open in #{applicationTitle} diff --git a/server/templates/email/resetpassword/html.pug b/server/templates/email/resetpassword/html.pug index a6fcc6468..3c800663a 100644 --- a/server/templates/email/resetpassword/html.pug +++ b/server/templates/email/resetpassword/html.pug @@ -6,25 +6,6 @@ head meta(name='viewport' content='width=device-width, initial-scale=1') meta(name='format-detection' content='telephone=no, date=no, address=no, email=no') link(href='https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap' rel='stylesheet' media='screen') - //if mso - xml - o:officedocumentsettings - o:pixelsperinch 96 - style. - td, - th, - div, - p, - a, - h1, - h2, - h3, - h4, - h5, - h6 { - font-family: 'Segoe UI', sans-serif; - mso-line-height-rule: exactly; - } style. .title:hover * { text-decoration: underline; @@ -35,25 +16,35 @@ head width: 100% !important; } } -div(style='display: block; background-color: #111827;') - table(style='margin: 0 auto; font-family: Inter, Arial, Sans-Serif; color: #fff; font-size: 16px; width: 26rem;') +div(style='display: block; background-color: #111827; padding: 2.5rem 0;') + table(style='margin: 0 auto; font-family: Inter, Arial, sans-serif; color: #fff; font-size: 16px; width: 26rem;') tr td(style="text-align: center;") - a(href=applicationUrl) - img(src=applicationUrl +'/logo_full.png' style='width: 26rem; padding: 1rem; image-rendering: crisp-edges; image-rendering: -webkit-optimize-contrast;') + if applicationUrl + a(href=applicationUrl style='margin: 0 1rem;') + img(src=applicationUrl +'/logo_full.png' style='width: 26rem; image-rendering: crisp-edges; image-rendering: -webkit-optimize-contrast;') + else + div(style='margin: 0 1rem 2.5rem; font-size: 3em; font-weight: 700;') + | #{applicationTitle} + if recipientName !== recipientEmail + tr + td(style='text-align: center;') + div(style='margin: 1rem 0 0; font-size: 1.25em;') + | Hi, #{recipientName.replace(/\.|@/g, ((x) => x + '\ufeff'))}! tr td(style='text-align: center;') - div(style='margin: 0rem 1rem 1rem; font-size: 1.25em;') - span - | Your #{applicationTitle} account password was requested to be reset. Click below to reset your password. - if resetPasswordLink - tr - td - a(href=resetPasswordLink style='display: block; margin: 1.5rem 3rem 2.5rem 3rem; text-decoration: none; font-size: 1.0em; line-height: 2.25em;') - span(style='padding: 0.2rem; font-weight: 500; text-align: center; border-radius: 10px; background-color: rgb(99,102,241); color: #fff; display: block; border: 1px solid rgba(255, 255, 255, 0.2);') - | Reset Password + div(style='margin: 1rem 0 0; font-size: 1.25em;') + | A request has been received to change the password for your #{applicationTitle} account. + tr + td + a(href=resetPasswordLink style='display: block; margin: 1.5rem 3rem; text-decoration: none; font-size: 1.0em; line-height: 2.25em;') + span(style='padding: 0.2rem; font-weight: 500; text-align: center; border-radius: 10px; background-color: rgb(99,102,241); color: #fff; display: block; border: 1px solid rgba(255,255,255,0.2);') + | Reset Password + tr + td(style='text-align: center;') + div(style='margin: 1rem 0 0; font-size: 1.25em;') + | The above link will expire in 24 hours. tr td(style='text-align: center;') - div(style='margin: 1rem; font-size: .85em;') - span - | If you did not request that your password be reset, you can safely ignore this email. + div(style='margin: 1rem 1rem 0; font-size: 1.25em;') + | If you did not initiate this request, you may safely disregard this message. \ No newline at end of file diff --git a/server/templates/email/test-email/html.pug b/server/templates/email/test-email/html.pug index 9dc9044c0..3ba83a936 100644 --- a/server/templates/email/test-email/html.pug +++ b/server/templates/email/test-email/html.pug @@ -6,25 +6,6 @@ head meta(name='viewport' content='width=device-width, initial-scale=1') meta(name='format-detection' content='telephone=no, date=no, address=no, email=no') link(href='https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap' rel='stylesheet' media='screen') - //if mso - xml - o:officedocumentsettings - o:pixelsperinch 96 - style. - td, - th, - div, - p, - a, - h1, - h2, - h3, - h4, - h5, - h6 { - font-family: 'Segoe UI', sans-serif; - mso-line-height-rule: exactly; - } style. .title:hover * { text-decoration: underline; @@ -35,20 +16,28 @@ head width: 100% !important; } } -div(style='display: block; background-color: #111827;') - table(style='margin: 0 auto; font-family: Inter, Arial, Sans-Serif; color: #fff; font-size: 16px; width: 26rem;') +div(style='display: block; background-color: #111827; padding: 2.5rem 0;') + table(style='margin: 0 auto; font-family: Inter, Arial, sans-serif; color: #fff; font-size: 16px; width: 26rem;') tr td(style="text-align: center;") - a(href=applicationUrl) - img(src=applicationUrl +'/logo_full.png' style='width: 26rem; padding: 1rem; image-rendering: crisp-edges; image-rendering: -webkit-optimize-contrast;') + if applicationUrl + a(href=applicationUrl style='margin: 0 1rem;') + img(src=applicationUrl +'/logo_full.png' style='width: 26rem; image-rendering: crisp-edges; image-rendering: -webkit-optimize-contrast;') + else + div(style='margin: 0 1rem 2.5rem; font-size: 3em; font-weight: 700;') + | #{applicationTitle} + if recipientName !== recipientEmail + tr + td(style='text-align: center;') + div(style='margin: 1rem 0 0; font-size: 1.25em;') + | Hi, #{recipientName.replace(/\.|@/g, ((x) => x + '\ufeff'))}! tr td(style='text-align: center;') - div(style='margin: 0rem 1rem 1rem; font-size: 1.25em;') - span - | #{body} + div(style='margin: 1rem 0 0; font-size: 1.25em;') + | #{body} if applicationUrl tr td - a(href=applicationUrl style='display: block; margin: 1.5rem 3rem 2.5rem 3rem; text-decoration: none; font-size: 1.0em; line-height: 2.25em;') - span(style='padding: 0.2rem; font-weight: 500; text-align: center; border-radius: 10px; background-color: rgb(99,102,241); color: #fff; display: block; border: 1px solid rgba(255, 255, 255, 0.2);') + a(href=applicationUrl style='display: block; margin: 1.5rem 3rem 0; text-decoration: none; font-size: 1.0em; line-height: 2.25em;') + span(style='padding: 0.2rem; font-weight: 500; text-align: center; border-radius: 10px; background-color: rgb(99,102,241); color: #fff; display: block; border: 1px solid rgba(255,255,255,0.2);') | Open #{applicationTitle}