From 6c5fe906f8b91f254063cb5eabceaefb6842c4bd Mon Sep 17 00:00:00 2001 From: Chigala <92148630+Chigala@users.noreply.github.com> Date: Fri, 11 Aug 2023 15:49:06 +0100 Subject: [PATCH 1/5] added SMTP support and removed resend dependency --- apps/webapp/app/env.server.ts | 4 ++++ apps/webapp/app/services/email.server.ts | 10 ++++++++- packages/emails/package.json | 5 +++-- packages/emails/src/index.tsx | 28 ++++++++++++++++++------ 4 files changed, 37 insertions(+), 10 deletions(-) diff --git a/apps/webapp/app/env.server.ts b/apps/webapp/app/env.server.ts index ad953b4610..bdde162d13 100644 --- a/apps/webapp/app/env.server.ts +++ b/apps/webapp/app/env.server.ts @@ -28,6 +28,10 @@ const EnvironmentSchema = z.object({ FROM_EMAIL: z.string().optional(), REPLY_TO_EMAIL: z.string().optional(), RESEND_API_KEY: z.string().optional(), + SMTP_HOST: z.string(), + SMTP_PORT: z.number(), + SMTP_USER: z.string(), + SMTP_PASSWORD: z.string(), PLAIN_API_KEY: z.string().optional(), RUNTIME_PLATFORM: z.enum(["docker-compose", "ecs", "local"]).default("local"), }); diff --git a/apps/webapp/app/services/email.server.ts b/apps/webapp/app/services/email.server.ts index bc8014fcca..29d7fca675 100644 --- a/apps/webapp/app/services/email.server.ts +++ b/apps/webapp/app/services/email.server.ts @@ -8,7 +8,15 @@ import type { AuthUser } from "./authUser"; import { workerQueue } from "./worker.server"; const client = new EmailClient({ - apikey: env.RESEND_API_KEY, + smtpConfig: { + host:env.SMTP_HOST, + secure: true, + port: env.SMTP_PORT, + auth: { + user: env.SMTP_USER, + pass: env.SMTP_PASSWORD, + }, + }, imagesBaseUrl: env.APP_ORIGIN, from: env.FROM_EMAIL ?? "team@email.trigger.dev", replyTo: env.REPLY_TO_EMAIL ?? "help@email.trigger.dev", diff --git a/packages/emails/package.json b/packages/emails/package.json index ccdfd6e96b..5c6a26f1cf 100644 --- a/packages/emails/package.json +++ b/packages/emails/package.json @@ -21,16 +21,17 @@ "@react-email/render": "^0.0.7", "@react-email/section": "^0.0.1", "@react-email/text": "^0.0.2", + "nodemailer": "^6.9.4", "react": "^18.2.0", "react-email": "^1.6.1", - "resend": "^0.9.1", "tiny-invariant": "^1.2.0", "zod": "3.21.4" }, "devDependencies": { - "@types/react": "18.2.17", "@trigger.dev/tsconfig": "workspace:*", "@types/node": "16", + "@types/nodemailer": "^6.4.9", + "@types/react": "18.2.17", "typescript": "^4.9.4" }, "engines": { diff --git a/packages/emails/src/index.tsx b/packages/emails/src/index.tsx index e07ee63c26..6edc375344 100644 --- a/packages/emails/src/index.tsx +++ b/packages/emails/src/index.tsx @@ -8,7 +8,7 @@ import WorkflowIntegration from "../emails/workflow-integration"; import InviteEmail, { InviteEmailSchema } from "../emails/invite"; import { render } from "@react-email/render"; -import { Resend } from "resend"; +import nodemailer from "nodemailer"; import { z } from "zod"; import React from "react"; @@ -42,15 +42,29 @@ export const DeliverEmailSchema = z export type DeliverEmail = z.infer; +export type NodeMailerTransportOptions = { + host: string; + port: number; + secure: boolean; + auth: { + user: string; + pass: string; + }; +}; + export class EmailClient { - #client?: Resend; + #client?: nodemailer.Transporter; #imagesBaseUrl: string; #from: string; #replyTo: string; - constructor(config: { apikey?: string; imagesBaseUrl: string; from: string; replyTo: string }) { - this.#client = - config.apikey && config.apikey.startsWith("re_") ? new Resend(config.apikey) : undefined; + constructor(config: { + smtpConfig: NodeMailerTransportOptions; + imagesBaseUrl: string; + from: string; + replyTo: string; + }) { + this.#client = nodemailer.createTransport(config.smtpConfig); this.#imagesBaseUrl = config.imagesBaseUrl; this.#from = config.from; this.#replyTo = config.replyTo; @@ -110,12 +124,12 @@ export class EmailClient { async #sendEmail({ to, subject, react }: { to: string; subject: string; react: ReactElement }) { if (this.#client) { - await this.#client.sendEmail({ + await this.#client.sendMail({ from: this.#from, to, replyTo: this.#replyTo, subject, - react, + html: render(react), }); return; From ea9b67877b197d978b1fc153537068cb7437d6ba Mon Sep 17 00:00:00 2001 From: Chigala <92148630+Chigala@users.noreply.github.com> Date: Tue, 15 Aug 2023 01:31:04 +0100 Subject: [PATCH 2/5] made the types optional based on requested changes --- apps/webapp/app/env.server.ts | 8 ++++---- apps/webapp/app/services/email.server.ts | 4 ++-- packages/emails/src/index.tsx | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/webapp/app/env.server.ts b/apps/webapp/app/env.server.ts index bdde162d13..b727e60cf2 100644 --- a/apps/webapp/app/env.server.ts +++ b/apps/webapp/app/env.server.ts @@ -28,10 +28,10 @@ const EnvironmentSchema = z.object({ FROM_EMAIL: z.string().optional(), REPLY_TO_EMAIL: z.string().optional(), RESEND_API_KEY: z.string().optional(), - SMTP_HOST: z.string(), - SMTP_PORT: z.number(), - SMTP_USER: z.string(), - SMTP_PASSWORD: z.string(), + SMTP_HOST: z.string().optional(), + SMTP_PORT: z.number().optional(), + SMTP_USER: z.string().optional(), + SMTP_PASSWORD: z.string().optional(), PLAIN_API_KEY: z.string().optional(), RUNTIME_PLATFORM: z.enum(["docker-compose", "ecs", "local"]).default("local"), }); diff --git a/apps/webapp/app/services/email.server.ts b/apps/webapp/app/services/email.server.ts index 29d7fca675..b12b30c40b 100644 --- a/apps/webapp/app/services/email.server.ts +++ b/apps/webapp/app/services/email.server.ts @@ -9,14 +9,14 @@ import { workerQueue } from "./worker.server"; const client = new EmailClient({ smtpConfig: { - host:env.SMTP_HOST, + host: env.SMTP_HOST, secure: true, port: env.SMTP_PORT, auth: { user: env.SMTP_USER, pass: env.SMTP_PASSWORD, }, - }, + }, imagesBaseUrl: env.APP_ORIGIN, from: env.FROM_EMAIL ?? "team@email.trigger.dev", replyTo: env.REPLY_TO_EMAIL ?? "help@email.trigger.dev", diff --git a/packages/emails/src/index.tsx b/packages/emails/src/index.tsx index 6edc375344..b7744089fa 100644 --- a/packages/emails/src/index.tsx +++ b/packages/emails/src/index.tsx @@ -43,12 +43,12 @@ export const DeliverEmailSchema = z export type DeliverEmail = z.infer; export type NodeMailerTransportOptions = { - host: string; - port: number; - secure: boolean; - auth: { - user: string; - pass: string; + host?: string; + port?: number; + secure?: boolean; + auth?: { + user?: string; + pass?: string; }; }; @@ -59,12 +59,12 @@ export class EmailClient { #replyTo: string; constructor(config: { - smtpConfig: NodeMailerTransportOptions; + smtpConfig?: NodeMailerTransportOptions; imagesBaseUrl: string; from: string; replyTo: string; }) { - this.#client = nodemailer.createTransport(config.smtpConfig); + this.#client = config.smtpConfig ? nodemailer.createTransport(config.smtpConfig) : undefined; this.#imagesBaseUrl = config.imagesBaseUrl; this.#from = config.from; this.#replyTo = config.replyTo; From 242928ff35defcbcff425a1d0d888fef6f5b6946 Mon Sep 17 00:00:00 2001 From: Chigala <92148630+Chigala@users.noreply.github.com> Date: Tue, 15 Aug 2023 02:05:56 +0100 Subject: [PATCH 3/5] updated the docs to reflect the changes made --- .env.example | 8 +++++--- apps/webapp/app/env.server.ts | 1 - docs/documentation/guides/self-hosting/flyio.mdx | 9 ++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index 46be04c209..d97fd242ec 100644 --- a/.env.example +++ b/.env.example @@ -14,12 +14,14 @@ NODE_ENV=development # AUTH_GITHUB_CLIENT_ID= # AUTH_GITHUB_CLIENT_SECRET= -# Resend is an email service used for signing in to Trigger.dev via a Magic Link. +# EMAIL VARIABLES. # Emails will print to the console if you leave these commented out -### Visit https://resend.com, create an account and get your API key. Then insert it below along with your From and Reply To email addresses. Visit https://resend.com/docs for more information. -# RESEND_API_KEY= # FROM_EMAIL= # REPLY_TO_EMAIL= +# SMTP_HOST= +# SMTP_PORT= +# SMTP_USER= +# SMTP_PASSWORD= # CLOUD VARIABLES POSTHOG_PROJECT_KEY= diff --git a/apps/webapp/app/env.server.ts b/apps/webapp/app/env.server.ts index b727e60cf2..be661e53a2 100644 --- a/apps/webapp/app/env.server.ts +++ b/apps/webapp/app/env.server.ts @@ -27,7 +27,6 @@ const EnvironmentSchema = z.object({ AUTH_GITHUB_CLIENT_SECRET: z.string().optional(), FROM_EMAIL: z.string().optional(), REPLY_TO_EMAIL: z.string().optional(), - RESEND_API_KEY: z.string().optional(), SMTP_HOST: z.string().optional(), SMTP_PORT: z.number().optional(), SMTP_USER: z.string().optional(), diff --git a/docs/documentation/guides/self-hosting/flyio.mdx b/docs/documentation/guides/self-hosting/flyio.mdx index 2c99ccad6e..e3da73993d 100644 --- a/docs/documentation/guides/self-hosting/flyio.mdx +++ b/docs/documentation/guides/self-hosting/flyio.mdx @@ -82,9 +82,9 @@ Both of these secrets should be set to the base URL of your fly application. For ![github copy secrets](/images/github-secrets.png) -`RESEND_API_KEY`, `FROM_EMAIL` and `REPLY_TO_EMAIL` +`FROM_EMAIL`,`REPLY_TO_EMAIL`, `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER` and `SMTP_PASSWORD` -We use [Resend.com](https://resend.com) for email sending (including the magic-link signup/login system). They have a generous free tier of 100 emails a day that should be sufficient. Signup for Resend.com and enter the environment vars below +We use SMTP for email sending (including the magic-link signup/login system). ## Set the secrets @@ -99,7 +99,10 @@ fly secrets set \ APP_ORIGIN="https://.fly.dev" \ FROM_EMAIL="Acme Inc. " \ REPLY_TO_EMAIL="Acme Inc. " \ - RESEND_API_KEY= \ + SMTP_HOST= < your SMTP host > \ + SMTP_PORT= < your SMTP port > \ + SMTP_USER= < your SMTP user > \ + SMTP_PASSWORD= < your SMTP password > \ AUTH_GITHUB_CLIENT_ID= \ AUTH_GITHUB_CLIENT_SECRET= From 63f0c4521c73528afb223682342d3009f5ae2acf Mon Sep 17 00:00:00 2001 From: Matt Aitken Date: Fri, 18 Aug 2023 10:12:22 +0100 Subject: [PATCH 4/5] SMTP_PORT env var has to be coerced to a number --- apps/webapp/app/env.server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/app/env.server.ts b/apps/webapp/app/env.server.ts index be661e53a2..976782dd6e 100644 --- a/apps/webapp/app/env.server.ts +++ b/apps/webapp/app/env.server.ts @@ -28,7 +28,7 @@ const EnvironmentSchema = z.object({ FROM_EMAIL: z.string().optional(), REPLY_TO_EMAIL: z.string().optional(), SMTP_HOST: z.string().optional(), - SMTP_PORT: z.number().optional(), + SMTP_PORT: z.coerce.number().optional(), SMTP_USER: z.string().optional(), SMTP_PASSWORD: z.string().optional(), PLAIN_API_KEY: z.string().optional(), From 55c5df63ec210b8426fb74561aba780e96d89c0c Mon Sep 17 00:00:00 2001 From: Chigala <92148630+Chigala@users.noreply.github.com> Date: Wed, 23 Aug 2023 12:20:03 +0100 Subject: [PATCH 5/5] refactor: improved the smtpConfig validation --- packages/emails/src/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/emails/src/index.tsx b/packages/emails/src/index.tsx index b7744089fa..ea248caa01 100644 --- a/packages/emails/src/index.tsx +++ b/packages/emails/src/index.tsx @@ -64,7 +64,9 @@ export class EmailClient { from: string; replyTo: string; }) { - this.#client = config.smtpConfig ? nodemailer.createTransport(config.smtpConfig) : undefined; + this.#client = config.smtpConfig?.host + ? nodemailer.createTransport(config.smtpConfig) + : undefined; this.#imagesBaseUrl = config.imagesBaseUrl; this.#from = config.from; this.#replyTo = config.replyTo;