Skip to content

Commit

Permalink
⚡️ Add support for using SES API (#573)
Browse files Browse the repository at this point in the history
  • Loading branch information
lukevella committed Mar 16, 2023
1 parent d5c3017 commit 8ab6768
Show file tree
Hide file tree
Showing 6 changed files with 810 additions and 18 deletions.
16 changes: 16 additions & 0 deletions apps/web/declarations/environment.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ declare global {
* "true" to require authentication for creating new polls and accessing admin pages
*/
AUTH_REQUIRED?: string;
/**
* Determines what email provider to use. "smtp" or "ses"
*/
EMAIL_PROVIDER?: "smtp" | "ses";
/**
* AWS access key ID
*/
AWS_ACCESS_KEY_ID?: string;
/**
* AWS secret access key
*/
AWS_SECRET_ACCESS_KEY?: string;
/**
* AWS region
*/
AWS_REGION?: string;
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion apps/web/src/components/forms/user-details-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ export const UserDetailsForm: React.FunctionComponent<
handleSubmit,
register,
watch,
formState: { errors },
formState: { errors, isSubmitting, isSubmitSuccessful },
} = useForm<UserDetailsData>({ defaultValues });

const isWorking = isSubmitting || isSubmitSuccessful;

React.useEffect(() => {
if (onChange) {
const subscription = watch(onChange);
Expand All @@ -44,6 +46,7 @@ export const UserDetailsForm: React.FunctionComponent<
className={clsx("input w-full", {
"input-error": errors.name,
})}
disabled={isWorking}
placeholder={t("namePlaceholder")}
{...register("name", { validate: requiredString })}
/>
Expand All @@ -58,6 +61,7 @@ export const UserDetailsForm: React.FunctionComponent<
className={clsx("input w-full", {
"input-error": errors.contact,
})}
disabled={isWorking}
placeholder={t("emailPlaceholder")}
{...register("contact", {
validate: validEmail,
Expand Down
2 changes: 2 additions & 0 deletions packages/emails/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"main": "./src/index.tsx",
"types": "./src/index.tsx",
"dependencies": {
"@aws-sdk/client-ses": "^3.292.0",
"@aws-sdk/credential-provider-node": "^3.292.0",
"@react-email/components": "0.0.2",
"@react-email/render": "0.0.6",
"@react-email/tailwind": "0.0.6",
Expand Down
59 changes: 44 additions & 15 deletions packages/emails/src/send-email.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as aws from "@aws-sdk/client-ses";
import { defaultProvider } from "@aws-sdk/credential-provider-node";
import { render } from "@react-email/render";
import { createTransport, Transporter } from "nodemailer";
import React from "react";
Expand All @@ -19,22 +21,47 @@ const env = process.env["NODE" + "_ENV"] || "development";
let transport: Transporter;

const getTransport = () => {
if (transport) {
// Reuse the transport if it exists
return transport;
}

if (env === "test") {
transport = createTransport({ port: 4025 });
} else {
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,
});
return transport;
}

switch (process.env.EMAIL_PROVIDER) {
case "ses":
{
const ses = new aws.SES({
region: process.env["AWS" + "_REGION"],
credentialDefaultProvider: defaultProvider,
});

transport = createTransport({
SES: { ses, aws },
});
}
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,
});
}
}

return transport;
};

Expand All @@ -53,11 +80,12 @@ export const sendEmail = async <T extends TemplateName>(
console.info("SUPPORT_EMAIL not configured - skipping email send");
return;
}

const transport = getTransport();
const Template = templates[templateName] as TemplateComponent<T>;

try {
return await transport.sendMail({
await transport.sendMail({
from: {
name: "Rallly",
address: process.env.SUPPORT_EMAIL,
Expand All @@ -67,8 +95,9 @@ export const sendEmail = async <T extends TemplateName>(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
html: render(<Template {...(options.props as any)} />),
});
return;
} catch (e) {
console.error("Error sending email", templateName);
console.error("Error sending email", templateName, e);
options.onError?.();
}
};
6 changes: 5 additions & 1 deletion turbo.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@
"outputs": [".next/**"],
"env": [
"ALLOWED_EMAILS",
"AUTH_REQUIRED",
"ANALYZE",
"API_SECRET",
"AUTH_REQUIRED",
"AWS_REGION",
"AWS_SECRET_ACCESS_KEY",
"AWS_ACCESS_KEY_ID",
"DISABLE_LANDING_PAGE",
"EMAIL_PROVIDER",
"MAINTENANCE_MODE",
"NEXT_PUBLIC_BASE_URL",
"NEXT_PUBLIC_BETA",
Expand Down
Loading

1 comment on commit 8ab6768

@vercel
Copy link

@vercel vercel bot commented on 8ab6768 Mar 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

web – ./

rallly.co
web-git-main-rallly.vercel.app
web-rallly.vercel.app
www.rallly.co

Please sign in to comment.