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 ad953b4610..976782dd6e 100644 --- a/apps/webapp/app/env.server.ts +++ b/apps/webapp/app/env.server.ts @@ -27,7 +27,10 @@ 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.coerce.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 bc8014fcca..b12b30c40b 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/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= 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..ea248caa01 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,31 @@ 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 = config.smtpConfig?.host + ? nodemailer.createTransport(config.smtpConfig) + : undefined; this.#imagesBaseUrl = config.imagesBaseUrl; this.#from = config.from; this.#replyTo = config.replyTo; @@ -110,12 +126,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;