Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
6 changes: 3 additions & 3 deletions lib/mail/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -283,9 +283,9 @@ export function Footer(): JSX.Element {
</A>
</P>
<P style={{ color: '#666666' }}>
If this is spam, let me know at{' '}
<a style={{ color: '#666666' }} href='mailto:nicholas@tutorbook.org'>
nicholas@tutorbook.org
If this is annoying, you can always{' '}
<a style={{ color: '#666666' }} href='{{{ pm:unsubscribe }}}'>
unsubscribe
</a>
</P>
</div>
Expand Down
1 change: 1 addition & 0 deletions lib/mail/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import send from 'lib/mail/send';
export default function mail(email: string, location: string, link: string): Promise<void> {
return send({
to: [{ email }],
stream: 'login',
subject: `Login Confirmation (${location})`,
template: (
<Message name='Login'>
Expand Down
1 change: 1 addition & 0 deletions lib/mail/meetings/1hr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default function mail(meeting: Meeting): Promise<void> {
const to = meeting.people.filter((p) => p.email);
return send({
to,
stream: 'meeting-1hr',
subject: `Reminder - ${meeting.subjects[0].name} lesson today`,
template: (
<Message name='1HR Reminder'>
Expand Down
1 change: 1 addition & 0 deletions lib/mail/meetings/24hr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default function mail(meeting: Meeting): Promise<void> {
const to = meeting.people.filter((p) => p.email);
return send({
to,
stream: 'meeting-24hr',
subject: `Reminder - ${meeting.subjects[0].name} lesson tomorrow`,
template: (
<Message name='24HR Reminder'>
Expand Down
1 change: 1 addition & 0 deletions lib/mail/meetings/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export default function mail(meeting: Meeting): Promise<void> {
return send({
to,
cc: meeting.creator,
stream: 'meeting-created',
subject: `${meeting.creator.firstName} booked a meeting with you`,
template: (
<Message name='Meeting Created'>
Expand Down
1 change: 1 addition & 0 deletions lib/mail/meetings/delete.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default function mail(meeting: Meeting, deleter: User): Promise<void> {
return send({
to,
cc: deleter,
stream: 'meeting-deleted',
subject: `${deleter.firstName} canceled a meeting with you`,
template: (
<Message name='Meeting Canceled'>
Expand Down
1 change: 1 addition & 0 deletions lib/mail/meetings/recur.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default function mail(meeting: Meeting): Promise<void> {
const to = meeting.people.filter((p) => p.email);
return send({
to,
stream: 'meeting-recur',
subject: `Enjoy your ${meeting.subjects[0].name.toLowerCase()} lesson? Make it recurring`,
template: (
<Message name='Recur'>
Expand Down
1 change: 1 addition & 0 deletions lib/mail/meetings/update.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default function mail(meeting: Meeting, updater: User): Promise<void> {
return send({
to,
cc: updater,
stream: 'meeting-updated',
subject: `${updater.firstName} updated a meeting with you`,
template: (
<Message name='Meeting Updated'>
Expand Down
1 change: 1 addition & 0 deletions lib/mail/request.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export default function mail(subjects: Subject[], description: string, user: Use
return send({
to: admins.filter((p) => p.email),
cc: user,
stream: 'request',
subject: `Request - ${user.firstName} for ${join(subjects)}`,
template: (
<Message name='Login'>
Expand Down
57 changes: 35 additions & 22 deletions lib/mail/send.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,47 @@
import { MailData } from '@sendgrid/helpers/classes/mail';
import { ReactElement } from 'react';
import mail from '@sendgrid/mail';
import { ServerClient } from 'postmark';
import { renderToStaticMarkup } from 'react-dom/server';
import to from 'await-to-js';

import { APIError } from 'lib/model/error';

export type Email = {
export type EmailStream =
| 'login'
| 'request'
| 'user-created'
| 'meeting-created'
| 'meeting-updated'
| 'meeting-deleted'
| 'meeting-24hr'
| 'meeting-1hr'
| 'meeting-recur';

export interface Email {
subject: string;
stream: EmailStream;
template: ReactElement;
replyTo?: { name?: string; email: string };
from?: { name?: string; email: string };
to: { name?: string; email: string }[];
} & Omit<MailData, 'replyTo' | 'from' | 'to'>;
cc?: { name?: string; email: string };
replyTo?: { name?: string; email: string };
}

export default async function send(email: Email): Promise<void> {
if (['development', 'test'].includes(process.env.APP_ENV as string)) return;
if (typeof process.env.SENDGRID_API_KEY !== 'string') {
throw new APIError('Cannot send emails without SendGrid API key.');
} else {
mail.setApiKey(process.env.SENDGRID_API_KEY);
const [e] = await to(
mail.send({
...email,
from: { name: 'Tutorbook', email: 'team@tutorbook.org' },
bcc: { name: 'Tutorbook', email: 'team@tutorbook.org' },
replyTo: email.replyTo?.email ? email.replyTo : undefined,
html: renderToStaticMarkup(email.template),
to: email.to.filter((p) => p.email),
})
);
if (e) throw new APIError(`${e.name} sending email: ${e.message}`, 500);
}
const key = email.stream.includes('meeting') ?
process.env.POSTMARK_MTG_KEY :
process.env.POSTMARK_API_KEY;
const client = new ServerClient(key as string);
const [e] = await to(
client.sendEmail({
From: 'team@tutorbook.org',
Bcc: 'team@tutorbook.org',
To: email.to.map((u) => u.email).join(', '),
Cc: email.cc?.email,
ReplyTo: email.replyTo?.email,
Subject: email.subject,
HtmlBody: renderToStaticMarkup(email.template),
MessageStream: email.stream,
})
);
if (e) throw new APIError(`${e.name} sending email: ${e.message}`, 500);
}
1 change: 1 addition & 0 deletions lib/mail/users/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import send from 'lib/mail/send';
export default function mail(user: User, org: Org, admins: User[]): Promise<void> {
return send({
to: admins.filter((p) => p.email),
stream: 'user-created',
subject: `${user.firstName} signed up on Tutorbook`,
template: (
<Message name='New User'>
Expand Down
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,6 @@
"@rmwc/textfield": "^6.1.4",
"@rmwc/tooltip": "^6.1.4",
"@segment/analytics.js-core": "^4.1.11",
"@sendgrid/client": "^7.4.6",
"@sendgrid/helpers": "^7.4.6",
"@sendgrid/mail": "^7.4.6",
"@supabase/supabase-js": "^1.25.0",
"accept-language-parser": "^1.5.0",
"algoliasearch": "^4.10.5",
Expand All @@ -149,6 +146,7 @@
"nprogress": "^0.2.0",
"password-generator": "^2.3.2",
"phone": "^3.1.6",
"postmark": "^2.7.8",
"prop-types": "^15.7.2",
"re-resizable": "^6.5.4",
"react": "^17.0.2",
Expand Down
53 changes: 20 additions & 33 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3737,35 +3737,6 @@ __metadata:
languageName: node
linkType: hard

"@sendgrid/client@npm:^7.4.6":
version: 7.4.6
resolution: "@sendgrid/client@npm:7.4.6"
dependencies:
"@sendgrid/helpers": ^7.4.6
axios: ^0.21.1
checksum: 45554a1c1f0f31758df31637c810ded4d5d993d1846413c633d80ee89531ba31aa2e85704290419d8529b751f498ea892a1c6e4c7f43d2c0dbffa8db6e609cdb
languageName: node
linkType: hard

"@sendgrid/helpers@npm:^7.4.6":
version: 7.4.6
resolution: "@sendgrid/helpers@npm:7.4.6"
dependencies:
deepmerge: ^4.2.2
checksum: 64d6e489087ff90e3f8fee2ffb07876911140bdebe7713be129f8438329c8bfbedff2f87a0e89b1043e000780ca706b25a48cb86248c5d2b2f46b6c2c828c054
languageName: node
linkType: hard

"@sendgrid/mail@npm:^7.4.6":
version: 7.4.6
resolution: "@sendgrid/mail@npm:7.4.6"
dependencies:
"@sendgrid/client": ^7.4.6
"@sendgrid/helpers": ^7.4.6
checksum: e923fc3bd4ce84abfb5294c580dc6066c92f9b9159829685e9fe5eb070fbdd887a734618ce472875c71077a12619b5bb0582bceb04c82627e9ca699c51cadcde
languageName: node
linkType: hard

"@sideway/address@npm:^4.1.0":
version: 4.1.2
resolution: "@sideway/address@npm:4.1.2"
Expand Down Expand Up @@ -5244,6 +5215,15 @@ __metadata:
languageName: node
linkType: hard

"axios@npm:^0.21.4":
version: 0.21.4
resolution: "axios@npm:0.21.4"
dependencies:
follow-redirects: ^1.14.0
checksum: 44245f24ac971e7458f3120c92f9d66d1fc695e8b97019139de5b0cc65d9b8104647db01e5f46917728edfc0cfd88eb30fc4c55e6053eef4ace76768ce95ff3c
languageName: node
linkType: hard

"axobject-query@npm:^2.2.0":
version: 2.2.0
resolution: "axobject-query@npm:2.2.0"
Expand Down Expand Up @@ -7822,7 +7802,7 @@ __metadata:
languageName: node
linkType: hard

"deepmerge@npm:^4.0.0, deepmerge@npm:^4.2.2":
"deepmerge@npm:^4.0.0":
version: 4.2.2
resolution: "deepmerge@npm:4.2.2"
checksum: a8c43a1ed8d6d1ed2b5bf569fa4c8eb9f0924034baf75d5d406e47e157a451075c4db353efea7b6bcc56ec48116a8ce72fccf867b6e078e7c561904b5897530b
Expand Down Expand Up @@ -15099,6 +15079,15 @@ fsevents@^1.2.7:
languageName: node
linkType: hard

"postmark@npm:^2.7.8":
version: 2.7.8
resolution: "postmark@npm:2.7.8"
dependencies:
axios: ^0.21.4
checksum: a4882fcf28bf31312a6440c7b23bd5654df0ba2adee60009d0c708c816a16c3f730185b39ba1a8d0e7bd23706cdb6eb75772ad8ab9aa5916c5774e951f6395ec
languageName: node
linkType: hard

"prebuild-install@npm:^6.1.4":
version: 6.1.4
resolution: "prebuild-install@npm:6.1.4"
Expand Down Expand Up @@ -18394,9 +18383,6 @@ resolve@^2.0.0-next.3:
"@rmwc/textfield": ^6.1.4
"@rmwc/tooltip": ^6.1.4
"@segment/analytics.js-core": ^4.1.11
"@sendgrid/client": ^7.4.6
"@sendgrid/helpers": ^7.4.6
"@sendgrid/mail": ^7.4.6
"@supabase/supabase-js": ^1.25.0
"@types/accept-language-parser": ^1.5.2
"@types/analytics-node": ^3.1.5
Expand Down Expand Up @@ -18455,6 +18441,7 @@ resolve@^2.0.0-next.3:
nyc: ^15.1.0
password-generator: ^2.3.2
phone: ^3.1.6
postmark: ^2.7.8
prettier: ^2.3.2
pretty-quick: ^3.1.1
prompt-sync: ^4.2.0
Expand Down