Skip to content

Commit

Permalink
Format emails WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
mrkvon committed Feb 18, 2024
1 parent e668da8 commit 5b9c13e
Show file tree
Hide file tree
Showing 10 changed files with 184 additions and 10 deletions.
3 changes: 3 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# port on which the service will run
PORT=3005

# name of the app that sends the emails - featured in the message subject or body
APP_NAME=sleepy.bike

# server base url, e.g. to construct correct email verification link
# this is the base url that end users see
BASE_URL=http://localhost:3005
Expand Down
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
"version": "0.0.1",
"main": "dist/index.js",
"scripts": {
"start": "tsc && node dist/index.js",
"start": "yarn build && node dist/index.js",
"build": "rm -rf dist && tsc && yarn copy-hbs",
"copy-hbs": "cp src/templates/*.hbs dist/templates && cp src/templates/*.css dist/templates",
"format": "prettier 'src/**/*.ts' '**/*.{md,yml,yaml,json}' --write",
"lint": "eslint . --ext .ts",
"test": "mocha",
Expand Down Expand Up @@ -64,7 +66,9 @@
"css-authn": "^0.0.14",
"dotenv": "^16.0.0",
"fs-extra": "^11.2.0",
"handlebars": "^4.7.8",
"jsonwebtoken": "^9.0.2",
"juice": "^10.0.0",
"koa": "^2.14.2",
"koa-helmet": "^7.0.2",
"koa-static": "^5.0.0",
Expand Down
5 changes: 4 additions & 1 deletion src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import SMTPTransport from 'nodemailer/lib/smtp-transport'
// server base url, e.g. to construct correct email verification links
export const baseUrl = process.env.BASE_URL ?? 'http://localhost:3005'

export const appName = process.env.APP_NAME ?? 'sleepy.bike'

// identity under which the mailer is operating
export const mailerCredentials = {
email: process.env.MAILER_IDENTITY_EMAIL ?? 'bot@example',
Expand Down Expand Up @@ -39,7 +41,8 @@ export const smtpTransportOptions: SMTPTransport.Options = {
}

// email address which will be the sender of the notifications and email verification messages
export const emailSender = process.env.EMAIL_SENDER
export const emailSender =
process.env.EMAIL_SENDER ?? '[email protected]'

export const port: number = +(process.env.PORT ?? 3005)

Expand Down
2 changes: 1 addition & 1 deletion src/controllers/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const initializeIntegration: Middleware = async ctx => {
await sendMail({
from: config.emailSender,
to: email,
subject: 'Verify your email for sleepy.bike notifications',
subject: `Verify your email for ${config.appName} notifications`,
html: `Please verify your email <a href="${emailVerificationLink}">click here</a>`,
text: `Please verify your email ${emailVerificationLink}`,
})
Expand Down
19 changes: 14 additions & 5 deletions src/controllers/notification.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { DefaultContext, Middleware } from 'koa'
import { emailSender } from '../config'
import { appName, emailSender } from '../config'
import { sendMail } from '../services/mailerService'
import { generateHtmlMessage } from '../templates/generateMessage'
import { getVerifiedEmails } from './status'

export type GoodBody = {
Expand Down Expand Up @@ -31,10 +32,18 @@ export const notification: Middleware<

for (const email of emails) {
await sendMail({
from: emailSender,
to: email,
subject: 'You have a new message from sleepy.bike!', // TODO generalize
html: body.object.content,
from: {
name: body.actor.name
? `${body.actor.name} (via ${appName})`
: `${appName} notifications`,
address: emailSender,
},
to: {
name: body.target.name ?? '',
address: email,
},
subject: `${body.actor.name || 'Someone'} wrote you from ${appName}`,
html: await generateHtmlMessage('message', body),
text: body.object.content,
})
}
Expand Down
17 changes: 17 additions & 0 deletions src/templates/generateMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as fs from 'fs-extra'
import Handlebars from 'handlebars'
import juice from 'juice'
import path from 'path'

export const generateHtmlMessage = async <T>(type: string, data: T) => {
const layout = await fs.readFile(path.join(__dirname, 'layout.hbs'), 'utf8')
const layoutTemplate = Handlebars.compile(layout)
const content = await fs.readFile(path.join(__dirname, `${type}.hbs`), 'utf8')
const contentTemplate = Handlebars.compile<T>(content)

const compiledContent = contentTemplate(data)
const emailHtml = layoutTemplate({ title: 'asdf', body: compiledContent })
const emailHtmlInlineCss = juice(emailHtml)

return emailHtmlInlineCss
}
16 changes: 16 additions & 0 deletions src/templates/layout.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<html>
<head>
<meta charset='utf-8' />
<title>{{title}}</title>
<style>
/* Email styles go here */ body { font-family: Arial, sans-serif; }
</style>
</head>
<body>
{{{body}}}
<footer>
<p>Contact us at
<a href='mailto:[email protected]'>[email protected]</a></p>
</footer>
</body>
</html>
10 changes: 10 additions & 0 deletions src/templates/message.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<p>
Hello
{{target.name}}!</p>
<a href={{actor.id}}>{{actor.name}}</a>
sent you a message from
{{appName}}.

<p>{{object.content}}</p>

<a href='https://sleepy.bike/messages/{{actor.id}}'>Reply on {{appName}}</a>
Empty file added src/templates/styles.css
Empty file.
116 changes: 114 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3565,6 +3565,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==

ansi-colors@^4.1.1:
version "4.1.3"
resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==

ansi-escapes@^4.2.1:
version "4.3.2"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
Expand Down Expand Up @@ -4130,6 +4135,11 @@ combined-stream@^1.0.8:
dependencies:
delayed-stream "~1.0.0"

commander@^6.1.0:
version "6.2.1"
resolved "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==

commander@^9.0.0:
version "9.5.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30"
Expand Down Expand Up @@ -4500,6 +4510,15 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"

dom-serializer@^1.0.1:
version "1.4.1"
resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
dependencies:
domelementtype "^2.0.1"
domhandler "^4.2.0"
entities "^2.0.0"

dom-serializer@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
Expand All @@ -4509,7 +4528,7 @@ dom-serializer@^2.0.0:
domhandler "^5.0.2"
entities "^4.2.0"

domelementtype@^2.3.0:
domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
Expand All @@ -4521,6 +4540,20 @@ domexception@^4.0.0:
dependencies:
webidl-conversions "^7.0.0"

domhandler@^3.3.0:
version "3.3.0"
resolved "https://registry.npmjs.org/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a"
integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==
dependencies:
domelementtype "^2.0.1"

domhandler@^4.2.0:
version "4.3.1"
resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
dependencies:
domelementtype "^2.2.0"

domhandler@^5.0.2, domhandler@^5.0.3:
version "5.0.3"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
Expand All @@ -4533,6 +4566,15 @@ dompurify@^2.3.6:
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.7.tgz#277adeb40a2c84be2d42a8bcd45f582bfa4d0cfc"
integrity sha512-kxxKlPEDa6Nc5WJi+qRgPbOAbgTpSULL+vI3NUXsZMlkJxTqYI9wg5ZTay2sFrdZRWHPWNi+EdAhcJf81WtoMQ==

domutils@^2.4.2:
version "2.8.0"
resolved "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
dependencies:
dom-serializer "^1.0.1"
domelementtype "^2.2.0"
domhandler "^4.2.0"

domutils@^3.0.1, domutils@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.1.0.tgz#c47f551278d3dc4b0b1ab8cbb42d751a6f0d824e"
Expand Down Expand Up @@ -4617,6 +4659,11 @@ enhanced-resolve@^5.12.0:
graceful-fs "^4.2.4"
tapable "^2.2.0"

entities@^2.0.0:
version "2.2.0"
resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==

entities@^4.2.0, entities@^4.4.0, entities@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
Expand Down Expand Up @@ -4697,6 +4744,11 @@ escalade@^3.1.1:
resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==

escape-goat@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/escape-goat/-/escape-goat-3.0.0.tgz#e8b5fb658553fe8a3c4959c316c6ebb8c842b19c"
integrity sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==

escape-html@^1.0.3, escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
Expand Down Expand Up @@ -5405,7 +5457,7 @@ graphql@^16.8.1:

handlebars@^4.7.8:
version "4.7.8"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9"
resolved "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9"
integrity sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==
dependencies:
minimist "^1.2.5"
Expand Down Expand Up @@ -5493,6 +5545,16 @@ html-encoding-sniffer@^3.0.0:
dependencies:
whatwg-encoding "^2.0.0"

htmlparser2@^5.0.0:
version "5.0.1"
resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-5.0.1.tgz#7daa6fc3e35d6107ac95a4fc08781f091664f6e7"
integrity sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==
dependencies:
domelementtype "^2.0.1"
domhandler "^3.3.0"
domutils "^2.4.2"
entities "^2.0.0"

htmlparser2@^8.0.0, htmlparser2@^8.0.1:
version "8.0.2"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21"
Expand Down Expand Up @@ -6130,6 +6192,17 @@ jsonwebtoken@^9.0.2:
ms "^2.1.1"
semver "^7.5.4"

juice@^10.0.0:
version "10.0.0"
resolved "https://registry.npmjs.org/juice/-/juice-10.0.0.tgz#c6b717ded8be4b969f12503ac9cfbd2604d35937"
integrity sha512-9f68xmhGrnIi6DBkiiP3rUrQN33SEuaKu1+njX6VgMP+jwZAsnT33WIzlrWICL9matkhYu3OyrqSUP55YTIdGg==
dependencies:
cheerio "^1.0.0-rc.12"
commander "^6.1.0"
mensch "^0.3.4"
slick "^1.12.2"
web-resource-inliner "^6.0.1"

just-extend@^4.0.2:
version "4.2.1"
resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744"
Expand Down Expand Up @@ -6397,6 +6470,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==

mensch@^0.3.4:
version "0.3.4"
resolved "https://registry.npmjs.org/mensch/-/mensch-0.3.4.tgz#770f91b46cb16ea5b204ee735768c3f0c491fecd"
integrity sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==

[email protected]:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
Expand Down Expand Up @@ -6458,6 +6536,11 @@ [email protected]:
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5"
integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==

mime@^2.4.6:
version "2.6.0"
resolved "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==

mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
Expand Down Expand Up @@ -6652,6 +6735,13 @@ nise@^5.1.4:
just-extend "^4.0.2"
path-to-regexp "^1.7.0"

node-fetch@^2.6.0:
version "2.7.0"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies:
whatwg-url "^5.0.0"

node-fetch@^2.6.12, node-fetch@^2.6.7:
version "2.6.12"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba"
Expand Down Expand Up @@ -7781,6 +7871,11 @@ slash@^3.0.0:
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==

slick@^1.12.2:
version "1.12.2"
resolved "https://registry.npmjs.org/slick/-/slick-1.12.2.tgz#bd048ddb74de7d1ca6915faa4a57570b3550c2d7"
integrity sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==

[email protected]:
version "3.11.0"
resolved "https://registry.yarnpkg.com/smtp-server/-/smtp-server-3.11.0.tgz#8820c191124fab37a8f16c8325a7f1fd38092c4f"
Expand Down Expand Up @@ -8378,6 +8473,11 @@ v8-compile-cache-lib@^3.0.1:
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==

valid-data-url@^3.0.0:
version "3.0.1"
resolved "https://registry.npmjs.org/valid-data-url/-/valid-data-url-3.0.1.tgz#826c1744e71b5632e847dd15dbd45b9fb38aa34f"
integrity sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==

validate-iri@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/validate-iri/-/validate-iri-1.0.1.tgz#4f7289a479e2ed96d3b8b613e12674c725fbb29b"
Expand Down Expand Up @@ -8409,6 +8509,18 @@ wcwidth@^1.0.1:
dependencies:
defaults "^1.0.3"

web-resource-inliner@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz#df0822f0a12028805fe80719ed52ab6526886e02"
integrity sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==
dependencies:
ansi-colors "^4.1.1"
escape-goat "^3.0.0"
htmlparser2 "^5.0.0"
mime "^2.4.6"
node-fetch "^2.6.0"
valid-data-url "^3.0.0"

web-streams-ponyfill@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/web-streams-ponyfill/-/web-streams-ponyfill-1.4.2.tgz#0ae863cc5f7493903679f16b08cbf14d432b62f4"
Expand Down

0 comments on commit 5b9c13e

Please sign in to comment.