From f712d939a44f40bb1a0c53f1f08d5d062f41eb1f Mon Sep 17 00:00:00 2001 From: Tyler Myracle Date: Sat, 20 Jan 2024 14:41:41 -0600 Subject: [PATCH] noop if no provider or api key --- .env.example | 10 +++--- README.md | 4 +-- apps/server/src/app/lib/email.ts | 6 ++-- apps/server/src/app/routes/users.router.ts | 2 +- apps/server/src/env.ts | 1 + apps/workers/src/app/lib/email.ts | 6 ++-- apps/workers/src/env.ts | 2 ++ .../features/src/email/email.service.ts | 36 +++++++++++-------- .../features/src/email/providers/index.ts | 1 + 9 files changed, 41 insertions(+), 27 deletions(-) create mode 100644 libs/server/features/src/email/providers/index.ts diff --git a/.env.example b/.env.example index 31348766..9e738fad 100644 --- a/.env.example +++ b/.env.example @@ -44,11 +44,13 @@ NX_TELLER_ENV=sandbox # EMAIL ######################################################################## -# We use Postmark for transactional emails. You can sign up for a free account +# We currently support Postmark for transactional emails. You can sign up for a free account # and get a free API key at https://postmarkapp.com -NX_POSTMARK_FROM_ADDRESS=account@example.com -NX_POSTMARK_REPLY_TO_ADDRESS=support@example.com -NX_POSTMARK_API_TOKEN= +NX_EMAIL_FROM_ADDRESS=account@example.com +NX_EMAIL_REPLY_TO_ADDRESS=support@example.com +NX_EMAIL_PROVIDER= +NX_EMAIL_PROVIDER_API_TOKEN= + ######################################################################## diff --git a/README.md b/README.md index 52cf4586..ee4d0c15 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ And dozens upon dozens of smaller features. This is the current state of building the app. We're actively working to make this process much more streamlined! -*You'll need Docker installed to run the app locally.* +_You'll need Docker installed to run the app locally._ [Docker Desktop](https://www.docker.com/products/docker-desktop/) is an easy way to get started. First, copy the `.env.example` file to `.env`: @@ -48,7 +48,7 @@ cp .env.example .env Then, create a new secret using `openssl rand -base64 32` and populate `NEXTAUTH_SECRET` in your `.env` file with it. -To enable transactional emails, you'll need to create a [Postmark](https://postmarkapp.com/) account and add your API key to your `.env` file (`NX_POSTMARK_API_TOKEN`). You can also set the from and reply-to email addresses (`NX_POSTMARK_FROM_ADDRESS` and `NX_POSTMARK_REPLY_TO_ADDRESS`). If you want to run the app without email, you can set `NX_POSTMARK_API_TOKEN` to a dummy value. +To enable transactional emails, you'll need to create a [Postmark](https://postmarkapp.com/) account and add your API key to your `.env` file (`NX_EMAIL_PROVIDER_API_TOKEN`) and set `NX_EMAIL_PROVIDER` to `postmark`. You can also set the from and reply-to email addresses (`NX_EMAIL_FROM_ADDRESS` and `NX_EMAIL_REPLY_TO_ADDRESS`). If you want to run the app without email, you can set `NX_EMAIL_PROVIDER_API_TOKEN` to a dummy value. Maybe uses [Teller](https://teller.io/) for connecting financial accounts. To get started with Teller, you'll need to create an account. Once you've created an account: diff --git a/apps/server/src/app/lib/email.ts b/apps/server/src/app/lib/email.ts index 0b4d8ab9..8dc6b9b6 100644 --- a/apps/server/src/app/lib/email.ts +++ b/apps/server/src/app/lib/email.ts @@ -2,14 +2,14 @@ import { ServerClient as PostmarkServerClient } from 'postmark' import env from '../../env' export function initializeEmailClient() { - switch (process.env.NX_EMAIL_PROVIDER) { + switch (env.NX_EMAIL_PROVIDER) { case 'postmark': if (env.NX_EMAIL_PROVIDER_API_TOKEN) { return new PostmarkServerClient(env.NX_EMAIL_PROVIDER_API_TOKEN) } else { - throw new Error('Missing Postmark API token') + return undefined } default: - throw new Error('Invalid email provider') + return undefined } } diff --git a/apps/server/src/app/routes/users.router.ts b/apps/server/src/app/routes/users.router.ts index 2e6c1f88..a2e63a93 100644 --- a/apps/server/src/app/routes/users.router.ts +++ b/apps/server/src/app/routes/users.router.ts @@ -259,7 +259,7 @@ router.get( }) ) -// TODO: Implement verification email using Postmark instead of Auth0 +// TODO: Implement verification email using internal email service instead of Auth0 router.post( '/resend-verification-email', endpoint.create({ diff --git a/apps/server/src/env.ts b/apps/server/src/env.ts index 59dd5ee8..83e98869 100644 --- a/apps/server/src/env.ts +++ b/apps/server/src/env.ts @@ -68,6 +68,7 @@ const envSchema = z.object({ NX_EMAIL_FROM_ADDRESS: z.string().default('account@maybe.co'), NX_EMAIL_REPLY_TO_ADDRESS: z.string().default('support@maybe.co'), + NX_EMAIL_PROVIDER: z.string().optional(), NX_EMAIL_PROVIDER_API_TOKEN: z.string().optional(), }) diff --git a/apps/workers/src/app/lib/email.ts b/apps/workers/src/app/lib/email.ts index 0b4d8ab9..8dc6b9b6 100644 --- a/apps/workers/src/app/lib/email.ts +++ b/apps/workers/src/app/lib/email.ts @@ -2,14 +2,14 @@ import { ServerClient as PostmarkServerClient } from 'postmark' import env from '../../env' export function initializeEmailClient() { - switch (process.env.NX_EMAIL_PROVIDER) { + switch (env.NX_EMAIL_PROVIDER) { case 'postmark': if (env.NX_EMAIL_PROVIDER_API_TOKEN) { return new PostmarkServerClient(env.NX_EMAIL_PROVIDER_API_TOKEN) } else { - throw new Error('Missing Postmark API token') + return undefined } default: - throw new Error('Invalid email provider') + return undefined } } diff --git a/apps/workers/src/env.ts b/apps/workers/src/env.ts index 4428f66a..24de876c 100644 --- a/apps/workers/src/env.ts +++ b/apps/workers/src/env.ts @@ -23,7 +23,9 @@ const envSchema = z.object({ NX_EMAIL_FROM_ADDRESS: z.string().default('account@maybe.co'), NX_EMAIL_REPLY_TO_ADDRESS: z.string().default('support@maybe.co'), + NX_EMAIL_PROVIDER: z.string().optional(), NX_EMAIL_PROVIDER_API_TOKEN: z.string().optional(), + NX_STRIPE_SECRET_KEY: z.string().default('sk_test_REPLACE_THIS'), NX_CDN_PRIVATE_BUCKET: z.string().default('REPLACE_THIS'), diff --git a/libs/server/features/src/email/email.service.ts b/libs/server/features/src/email/email.service.ts index fde0ce08..d9bec7ef 100644 --- a/libs/server/features/src/email/email.service.ts +++ b/libs/server/features/src/email/email.service.ts @@ -1,10 +1,8 @@ import type { Logger } from 'winston' import type { ServerClient as PostmarkServerClient } from 'postmark' -import type { MessageSendingResponse } from 'postmark/dist/client/models' +import { PostmarkEmailProvider } from './providers' import type { SharedType } from '@maybe-finance/shared' -import { PostmarkEmailProvider } from './providers/postmark.provider' - export interface IEmailProvider { send(messages: SharedType.PlainEmailMessage): Promise send(messages: SharedType.PlainEmailMessage[]): Promise @@ -23,13 +21,13 @@ export interface IEmailProvider { } export class EmailService implements IEmailProvider { - private emailProvider: IEmailProvider + private emailProvider: IEmailProvider | undefined constructor( private readonly logger: Logger, private readonly client: PostmarkServerClient | undefined, private readonly defaultAddresses: { from: string; replyTo?: string } ) { - const provider = process.env.EMAIL_PROVIDER + const provider = process.env.NX_EMAIL_PROVIDER switch (provider) { case 'postmark': @@ -40,7 +38,7 @@ export class EmailService implements IEmailProvider { ) break default: - throw new Error('Unsupported email provider') + undefined } } @@ -49,24 +47,34 @@ export class EmailService implements IEmailProvider { * * @returns success boolean(s) */ - send(messages: SharedType.PlainEmailMessage): Promise - send(messages: SharedType.PlainEmailMessage[]): Promise - send( + async send(messages: SharedType.PlainEmailMessage): Promise + async send(messages: SharedType.PlainEmailMessage[]): Promise + async send( messages: SharedType.PlainEmailMessage | SharedType.PlainEmailMessage[] - ): Promise { - return this.emailProvider.send(messages) + ): Promise { + if (!this.emailProvider || !this.client) { + //no-op + return undefined as unknown as SharedType.EmailSendingResponse + } + return await this.emailProvider.send(messages) } /** * Sends template email(s) */ - async sendTemplate(messages: SharedType.TemplateEmailMessage): Promise + async sendTemplate( + messages: SharedType.TemplateEmailMessage + ): Promise async sendTemplate( messages: SharedType.TemplateEmailMessage[] - ): Promise + ): Promise async sendTemplate( messages: SharedType.TemplateEmailMessage | SharedType.TemplateEmailMessage[] - ): Promise { + ): Promise { + if (!this.emailProvider || !this.client) { + //no-op + return undefined as unknown as SharedType.EmailSendingResponse + } return this.emailProvider.sendTemplate(messages) } } diff --git a/libs/server/features/src/email/providers/index.ts b/libs/server/features/src/email/providers/index.ts new file mode 100644 index 00000000..db0bc624 --- /dev/null +++ b/libs/server/features/src/email/providers/index.ts @@ -0,0 +1 @@ +export * from './postmark.provider'