Revert "♻️ Improve email abstraction (#861)"

This reverts commit 93cc5b9b4d.
This commit is contained in:
Luke Vella 2023-09-15 15:32:37 +01:00
parent 93cc5b9b4d
commit 1199ca5c53
10 changed files with 141 additions and 738 deletions

View file

@ -3,7 +3,6 @@ import { defaultProvider } from "@aws-sdk/credential-provider-node";
import { render } from "@react-email/render";
import { createTransport, Transporter } from "nodemailer";
import type Mail from "nodemailer/lib/mailer";
import previewEmail from "preview-email";
import React from "react";
import * as templates from "./templates";
@ -18,121 +17,30 @@ type TemplateProps<T extends TemplateName> = React.ComponentProps<
type TemplateComponent<T extends TemplateName> = Templates[T];
type SendEmailOptions<T extends TemplateName> = {
to: string;
subject: string;
props: TemplateProps<T>;
attachments?: Mail.Options["attachments"];
};
const env = process.env["NODE" + "_ENV"] || "development";
type EmailProviderConfig =
| {
name: "ses";
// config defined through env vars
}
| {
name: "smtp";
// config defined through env vars
};
let transport: Transporter;
export type SupportedEmailProviders = EmailProviderConfig["name"];
type EmailClientConfig = {
/**
* Whether to open previews of each email in the browser
*/
openPreviews?: boolean;
/**
* Whether to send emails to the test server
*/
useTestServer: boolean;
/**
* Email provider config
*/
provider: EmailProviderConfig;
/**
* Mail config
*/
mail: {
from: {
name: string;
address: string;
};
};
};
export class EmailClient {
private config: EmailClientConfig;
private cachedTransport?: Transporter;
constructor(config: EmailClientConfig) {
this.config = config;
const getTransport = () => {
if (transport) {
// Reuse the transport if it exists
return transport;
}
async sendTemplate<T extends TemplateName>(
templateName: T,
options: SendEmailOptions<T>,
) {
if (!process.env.SUPPORT_EMAIL) {
console.info("SUPPORT_EMAIL not configured - skipping email send");
return;
}
const Template = templates[templateName] as TemplateComponent<T>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const html = render(<Template {...(options.props as any)} />);
try {
await this.sendEmail({
from: this.config.mail.from,
to: options.to,
subject: options.subject,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
html,
attachments: options.attachments,
});
} catch (e) {
console.error("Error sending email", templateName, e);
}
if (env === "test") {
transport = createTransport({ port: 4025 });
return transport;
}
async sendEmail(options: Mail.Options) {
if (this.config.openPreviews) {
await previewEmail(options, {
openSimulator: false,
});
return;
}
try {
await this.transport.sendMail(options);
return;
} catch (e) {
console.error("Error sending email", e);
}
}
private get transport() {
if (this.config.useTestServer) {
this.cachedTransport = createTransport({
port: 4025,
});
return this.cachedTransport;
}
if (this.cachedTransport) {
// Reuse the transport if it exists
return this.cachedTransport;
}
switch (this.config.provider.name) {
case "ses": {
switch (process.env.EMAIL_PROVIDER) {
case "ses":
{
const ses = new aws.SES({
region: process.env["AWS" + "_REGION"],
credentialDefaultProvider: defaultProvider,
});
this.cachedTransport = createTransport({
transport = createTransport({
SES: {
ses,
aws,
@ -140,31 +48,78 @@ export class EmailClient {
},
});
}
case "smtp": {
const hasAuth = process.env.SMTP_USER || process.env.SMTP_PWD;
this.cachedTransport = createTransport({
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT
? parseInt(process.env.SMTP_PORT)
: undefined,
secure: process.env.SMTP_SECURE === "true",
auth: hasAuth
break;
default: {
const hasAuth = process.env.SMTP_USER || process.env.SMTP_PWD;
transport = createTransport({
host: process.env.SMTP_HOST,
port: process.env.SMTP_PORT
? parseInt(process.env.SMTP_PORT)
: undefined,
secure: process.env.SMTP_SECURE === "true",
auth: hasAuth
? {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PWD,
}
: undefined,
tls:
process.env.SMTP_TLS_ENABLED === "true"
? {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PWD,
ciphers: "SSLv3",
rejectUnauthorized: false,
}
: undefined,
tls:
process.env.SMTP_TLS_ENABLED === "true"
? {
ciphers: "SSLv3",
rejectUnauthorized: false,
}
: undefined,
});
}
});
}
return this.cachedTransport;
}
}
return transport;
};
type SendEmailOptions<T extends TemplateName> = {
to: string;
subject: string;
props: TemplateProps<T>;
attachments?: Mail.Options["attachments"];
};
export const sendEmail = async <T extends TemplateName>(
templateName: T,
options: SendEmailOptions<T>,
) => {
if (!process.env.SUPPORT_EMAIL) {
console.info("SUPPORT_EMAIL not configured - skipping email send");
return;
}
const Template = templates[templateName] as TemplateComponent<T>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const html = render(<Template {...(options.props as any)} />);
try {
await sendRawEmail({
from: {
name: "Rallly",
address: process.env.NOREPLY_EMAIL || process.env.SUPPORT_EMAIL,
},
to: options.to,
subject: options.subject,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
html,
attachments: options.attachments,
});
} catch (e) {
console.error("Error sending email", templateName, e);
}
};
export const sendRawEmail = async (options: Mail.Options) => {
const transport = getTransport();
try {
await transport.sendMail(options);
return;
} catch (e) {
console.error("Error sending email", e);
}
};