2
0

🧑‍💻 (emails) Add decent emails management

Use mjml-react to generate emails. Put all emails in a independent package.
This commit is contained in:
Baptiste Arnaud
2022-10-01 07:00:05 +02:00
parent e1f2d49342
commit 1654de3c1f
50 changed files with 4811 additions and 4048 deletions

View File

@ -1,31 +1,16 @@
import Document, {
Html,
Head,
Main,
NextScript,
DocumentContext,
} from 'next/document'
/* eslint-disable @next/next/no-sync-scripts */
import { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
const Document = () => (
<Html>
<Head>
<script src="/__env.js" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
render() {
return (
<Html>
<Head>
{/* eslint-disable-next-line @next/next/no-sync-scripts */}
<script src="/__env.js" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
export default Document

View File

@ -1,17 +1,14 @@
import { withSentry } from '@sentry/nextjs'
import { almostReachedStorageLimitEmail } from 'assets/emails/almostReachedStorageLimitEmail'
import { reachedStorageLimitEmail } from 'assets/emails/reachedStorageLimitEmail'
import { WorkspaceRole } from 'db'
import prisma from 'libs/prisma'
import { InputBlockType, PublicTypebot } from 'models'
import { NextApiRequest, NextApiResponse } from 'next'
import {
badRequest,
generatePresignedUrl,
methodNotAllowed,
sendEmailNotification,
} from 'utils/api'
import { badRequest, generatePresignedUrl, methodNotAllowed } from 'utils/api'
import { byId, getStorageLimit, isDefined, env } from 'utils'
import {
sendAlmostReachedStorageLimitEmail,
sendReachedStorageLimitEmail,
} from 'emails'
const LIMIT_EMAIL_TRIGGER_PERCENT = 0.8
@ -60,6 +57,7 @@ const handler = async (
return methodNotAllowed(res)
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const checkStorageLimit = async (typebotId: string) => {
const typebot = await prisma.typebot.findFirst({
where: { id: typebotId },
@ -102,7 +100,7 @@ const checkStorageLimit = async (typebotId: string) => {
!hasSentFirstEmail &&
env('E2E_TEST') !== 'true'
)
await sendAlmostReachStorageLimitEmail({
await sendAlmostReachStorageLimitNotification({
workspaceId: workspace.id,
storageLimit,
})
@ -111,14 +109,14 @@ const checkStorageLimit = async (typebotId: string) => {
!hasSentSecondEmail &&
env('E2E_TEST') !== 'true'
)
await sendReachStorageLimitEmail({
await sendReachStorageLimitNotification({
workspaceId: workspace.id,
storageLimit,
})
return totalStorageUsed >= storageLimitBytes
}
const sendAlmostReachStorageLimitEmail = async ({
const sendAlmostReachStorageLimitNotification = async ({
workspaceId,
storageLimit,
}: {
@ -129,22 +127,20 @@ const sendAlmostReachStorageLimitEmail = async ({
where: { role: WorkspaceRole.ADMIN, workspaceId },
include: { user: { select: { email: true } } },
})
const readableStorageLimit = `${storageLimit}GB`
await sendEmailNotification({
await sendAlmostReachedStorageLimitEmail({
to: members.map((member) => member.user.email).filter(isDefined),
subject: "You're close to your storage limit",
html: almostReachedStorageLimitEmail({
readableStorageLimit,
url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspaceId}`,
}),
storageLimit,
url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspaceId}`,
})
await prisma.workspace.update({
where: { id: workspaceId },
data: { storageLimitFirstEmailSentAt: new Date() },
})
}
const sendReachStorageLimitEmail = async ({
const sendReachStorageLimitNotification = async ({
workspaceId,
storageLimit,
}: {
@ -155,15 +151,13 @@ const sendReachStorageLimitEmail = async ({
where: { role: WorkspaceRole.ADMIN, workspaceId },
include: { user: { select: { email: true } } },
})
const readableStorageLimit = `${storageLimit}GB`
await sendEmailNotification({
await sendReachedStorageLimitEmail({
to: members.map((member) => member.user.email).filter(isDefined),
subject: "You've hit your storage limit",
html: reachedStorageLimitEmail({
readableStorageLimit,
url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspaceId}`,
}),
storageLimit,
url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspaceId}`,
})
await prisma.workspace.update({
where: { id: workspaceId },
data: { storageLimitSecondEmailSentAt: new Date() },

View File

@ -18,7 +18,7 @@ import {
saveSuccessLog,
} from 'services/api/utils'
import Mail from 'nodemailer/lib/mailer'
import { newLeadEmailContent } from 'assets/emails/newLeadEmailContent'
import { DefaultBotNotificationEmail, render } from 'emails'
const cors = initMiddleware(Cors())
@ -84,6 +84,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
typebotId,
resultValues,
})
if (!emailBody) {
await saveErrorLog(resultId, 'Email not sent', {
transportConfig,
@ -121,6 +122,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
await saveErrorLog(resultId, 'Email not sent', {
transportConfig,
email,
error: err,
})
return res.status(500).send({
message: `Email not sent. Error: ${err}`,
@ -177,10 +179,12 @@ const getEmailBody = async ({
],
})(resultValues)
return {
html: newLeadEmailContent(
`${process.env.NEXTAUTH_URL}/typebots/${typebot.id}/results`,
omit(answers, 'submittedAt')
),
html: render(
<DefaultBotNotificationEmail
resultsUrl={`${process.env.NEXTAUTH_URL}/typebots/${typebot.id}/results`}
answers={omit(answers, 'submittedAt')}
/>
).html,
}
}

View File

@ -1,12 +1,14 @@
import { almostReachedChatsLimitEmail } from 'assets/emails/almostReachedChatsLimitEmail'
import { reachedSChatsLimitEmail } from 'assets/emails/reachedChatsLimitEmail'
import { Workspace, WorkspaceRole } from 'db'
import {
sendAlmostReachedChatsLimitEmail,
sendReachedChatsLimitEmail,
} from 'emails'
import prisma from 'libs/prisma'
import { ResultWithAnswers } from 'models'
import { NextApiRequest, NextApiResponse } from 'next'
import { authenticateUser } from 'services/api/utils'
import { env, getChatsLimit, isDefined, parseNumberWithCommas } from 'utils'
import { sendEmailNotification, methodNotAllowed } from 'utils/api'
import { env, getChatsLimit, isDefined } from 'utils'
import { methodNotAllowed } from 'utils/api'
const LIMIT_EMAIL_TRIGGER_PERCENT = 0.8
@ -60,6 +62,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
methodNotAllowed(res)
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const checkChatsUsage = async (
workspace: Pick<
Workspace,
@ -96,87 +99,64 @@ const checkChatsUsage = async (
!hasSentFirstEmail &&
env('E2E_TEST') !== 'true'
)
await sendAlmostReachChatsLimitEmail({
await sendAlmostReachChatsLimitNotification({
workspaceId: workspace.id,
chatLimit: chatsLimit,
firstDayOfNextMonth,
chatsLimit,
})
if (
chatsCount >= chatsLimit &&
!hasSentSecondEmail &&
env('E2E_TEST') !== 'true'
)
await sendReachedAlertEmail({
await sendReachedAlertNotification({
workspaceId: workspace.id,
chatLimit: chatsLimit,
firstDayOfNextMonth,
chatsLimit,
})
return chatsCount >= chatsLimit
}
const sendAlmostReachChatsLimitEmail = async ({
const sendAlmostReachChatsLimitNotification = async ({
workspaceId,
chatLimit,
firstDayOfNextMonth,
chatsLimit,
}: {
workspaceId: string
chatLimit: number
firstDayOfNextMonth: Date
chatsLimit: number
}) => {
const members = await prisma.memberInWorkspace.findMany({
where: { role: WorkspaceRole.ADMIN, workspaceId },
include: { user: { select: { email: true } } },
})
const readableChatsLimit = parseNumberWithCommas(chatLimit)
const readableResetDate = firstDayOfNextMonth
.toDateString()
.split(' ')
.slice(1, 4)
.join(' ')
await sendEmailNotification({
await sendAlmostReachedChatsLimitEmail({
to: members.map((member) => member.user.email).filter(isDefined),
subject: "You're close to your chats limit",
html: almostReachedChatsLimitEmail({
readableChatsLimit,
readableResetDate,
url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspaceId}`,
}),
chatsLimit,
url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspaceId}`,
})
await prisma.workspace.update({
where: { id: workspaceId },
data: { chatsLimitFirstEmailSentAt: new Date() },
})
}
const sendReachedAlertEmail = async ({
const sendReachedAlertNotification = async ({
workspaceId,
chatLimit,
firstDayOfNextMonth,
chatsLimit,
}: {
workspaceId: string
chatLimit: number
firstDayOfNextMonth: Date
chatsLimit: number
}) => {
const members = await prisma.memberInWorkspace.findMany({
where: { role: WorkspaceRole.ADMIN, workspaceId },
include: { user: { select: { email: true } } },
})
const readableChatsLimit = parseNumberWithCommas(chatLimit)
const readableResetDate = firstDayOfNextMonth
.toDateString()
.split(' ')
.slice(1, 4)
.join(' ')
await sendEmailNotification({
await sendReachedChatsLimitEmail({
to: members.map((member) => member.user.email).filter(isDefined),
subject: "You've hit your monthly chats limit",
html: reachedSChatsLimitEmail({
readableChatsLimit,
readableResetDate,
url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspaceId}`,
}),
chatsLimit,
url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspaceId}`,
})
await prisma.workspace.update({
where: { id: workspaceId },
data: { chatsLimitSecondEmailSentAt: new Date() },