feat: add trpc route to send emails

This commit is contained in:
Ephraim Atta-Duncan
2023-06-19 16:33:30 +00:00
parent aa740864b8
commit bc11abda08
11 changed files with 1095 additions and 4 deletions

View File

@ -17,3 +17,10 @@ NEXT_PUBLIC_SUBSCRIPTIONS_ENABLED=false
# This is only required for the marketing site # This is only required for the marketing site
NEXT_PRIVATE_REDIS_URL= NEXT_PRIVATE_REDIS_URL=
NEXT_PRIVATE_REDIS_TOKEN= NEXT_PRIVATE_REDIS_TOKEN=
# Mailserver
NEXT_PRIVATE_SENDGRID_API_KEY=
NEXT_PRIVATE_SMTP_MAIL_HOST=
NEXT_PRIVATE_SMTP_MAIL_PORT=
NEXT_PRIVATE_SMTP_MAIL_USER=
NEXT_PRIVATE_SMTP_MAIL_PASSWORD=

View File

@ -43,9 +43,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
</head> </head>
<body> <body>
<PlausibleProvider> <PlausibleProvider>{children}</PlausibleProvider>
{children}
</PlausibleProvider>
<Toaster /> <Toaster />
</body> </body>
</html> </html>

View File

@ -25,6 +25,8 @@
"next-auth": "^4.22.1", "next-auth": "^4.22.1",
"next-plausible": "^3.7.2", "next-plausible": "^3.7.2",
"next-themes": "^0.2.1", "next-themes": "^0.2.1",
"nodemailer": "^6.9.3",
"nodemailer-sendgrid": "^1.0.3",
"perfect-freehand": "^1.2.0", "perfect-freehand": "^1.2.0",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",
@ -38,6 +40,8 @@
"devDependencies": { "devDependencies": {
"@types/formidable": "^2.0.6", "@types/formidable": "^2.0.6",
"@types/node": "20.1.0", "@types/node": "20.1.0",
"@types/nodemailer": "^6.4.8",
"@types/nodemailer-sendgrid": "^1.0.0",
"@types/react": "18.2.6", "@types/react": "18.2.6",
"@types/react-dom": "18.2.4" "@types/react-dom": "18.2.4"
} }

View File

@ -11,5 +11,11 @@ declare namespace NodeJS {
NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET: string; NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET: string;
NEXT_PUBLIC_SUBSCRIPTIONS_ENABLED: string; NEXT_PUBLIC_SUBSCRIPTIONS_ENABLED: string;
NEXT_PRIVATE_SENDGRID_API_KEY: string;
NEXT_PRIVATE_SMTP_MAIL_HOST: string;
NEXT_PRIVATE_SMTP_MAIL_PORT: string;
NEXT_PRIVATE_SMTP_MAIL_USER: string;
NEXT_PRIVATE_SMTP_MAIL_PASSWORD: string;
} }
} }

View File

@ -0,0 +1,24 @@
'use client';
import React from 'react';
import { trpc } from '@documenso/trpc/react';
export default function Send() {
const { mutateAsync: sendMail } = trpc.document.sendEmail.useMutation();
return (
<div className="p-20">
<button
className="rounded-md border-2 border-solid border-black px-4 py-2 text-2xl"
onClick={async () => {
console.log('clicked');
await sendMail({ email: 'duncan@documenso.com' });
}}
>
Send
</button>
</div>
);
}

972
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
import nodemailer from 'nodemailer';
import nodemailerSendgrid from 'nodemailer-sendgrid';
export const sendMail = async ({ email }: { email: string }) => {
let transporter;
if (process.env.NEXT_PRIVATE_SENDGRID_API_KEY) {
transporter = nodemailer.createTransport(
nodemailerSendgrid({
apiKey: process.env.NEXT_PRIVATE_SENDGRID_API_KEY,
}),
);
}
if (process.env.NEXT_PRIVATE_SMTP_MAIL_HOST) {
transporter = nodemailer.createTransport({
host: process.env.NEXT_PRIVATE_SMTP_MAIL_HOST,
port: Number(process.env.NEXT_PRIVATE_SMTP_MAIL_PORT),
auth: {
user: process.env.NEXT_PRIVATE_SMTP_MAIL_USER,
pass: process.env.NEXT_PRIVATE_SMTP_MAIL_PASSWORD,
},
});
}
if (!transporter) {
throw new Error(
'No mail transport configured. Probably Sendgrid API Key nor SMTP Mail host was set',
);
}
await transporter.sendMail({
from: 'Documenso <hi@documenso.com>',
to: email,
subject: 'Welcome to Documenso!',
text: 'Welcome to Documenso!',
html: '<p>Welcome to Documenso!</p>',
});
};

View File

@ -0,0 +1,26 @@
import { TRPCError } from '@trpc/server';
import { sendMail } from '@documenso/lib/server-only/document/send-document';
import { authenticatedProcedure, router } from '../trpc';
import { ZSendMailMutationSchema } from './schema';
export const documentRouter = router({
sendEmail: authenticatedProcedure
.input(ZSendMailMutationSchema)
.mutation(async ({ input, ctx }) => {
try {
const { email } = input;
console.log('Send Mail Context', ctx);
return await sendMail({ email });
} catch (err) {
console.error(err);
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'We were unable to send an email.',
});
}
}),
});

View File

@ -0,0 +1,7 @@
import { z } from 'zod';
export const ZSendMailMutationSchema = z.object({
email: z.string().min(1).email(),
});
export type TSendMailMutationSchema = z.infer<typeof ZSendMailMutationSchema>;

View File

@ -1,4 +1,5 @@
import { authRouter } from './auth-router/router'; import { authRouter } from './auth-router/router';
import { documentRouter } from './document-router/router';
import { profileRouter } from './profile-router/router'; import { profileRouter } from './profile-router/router';
import { procedure, router } from './trpc'; import { procedure, router } from './trpc';
@ -6,6 +7,7 @@ export const appRouter = router({
hello: procedure.query(() => 'Hello, world!'), hello: procedure.query(() => 'Hello, world!'),
auth: authRouter, auth: authRouter,
profile: profileRouter, profile: profileRouter,
document: documentRouter,
}); });
export type AppRouter = typeof appRouter; export type AppRouter = typeof appRouter;

View File

@ -18,6 +18,12 @@
"NEXT_PUBLIC_SITE_URL", "NEXT_PUBLIC_SITE_URL",
"NEXT_PRIVATE_DATABASE_URL", "NEXT_PRIVATE_DATABASE_URL",
"NEXT_PRIVATE_NEXT_AUTH_SECRET", "NEXT_PRIVATE_NEXT_AUTH_SECRET",
"NEXT_PUBLIC_SUBSCRIPTIONS_ENABLED" "NEXT_PUBLIC_SUBSCRIPTIONS_ENABLED",
"NEXT_PRIVATE_SENDGRID_API_KEY",
"NEXT_PRIVATE_SMTP_MAIL_HOST",
"NEXT_PRIVATE_SMTP_MAIL_PORT",
"NEXT_PRIVATE_SMTP_MAIL_USER",
"NEXT_PRIVATE_SMTP_MAIL_PASSWORD"
] ]
} }