2
0

🛂 Reset isQuarantined on the first of month

This commit is contained in:
Baptiste Arnaud
2023-04-23 21:07:46 +02:00
parent c6983c952c
commit 3fbd044d92
8 changed files with 54 additions and 239 deletions

View File

@ -13,6 +13,7 @@ import { SendMailOptions } from 'nodemailer'
import { sendEmail } from '../sendEmail'
type AlmostReachedChatsLimitEmailProps = {
usagePercent: number
chatsLimit: number
url: string
}
@ -26,6 +27,7 @@ const readableResetDate = firstDayOfNextMonth
.join(' ')
export const AlmostReachedChatsLimitEmail = ({
usagePercent,
chatsLimit,
url,
}: AlmostReachedChatsLimitEmailProps) => {
@ -45,7 +47,8 @@ export const AlmostReachedChatsLimitEmail = ({
<Text>Your bots are chatting a lot. That&apos;s amazing. 💙</Text>
<Text>
This means you&apos;ve almost reached your monthly chats limit.
You currently reached 80% of {readableChatsLimit} chats.
You currently reached {usagePercent}% of {readableChatsLimit}{' '}
chats.
</Text>
<Text>This limit will be reset on {readableResetDate}.</Text>
<Text fontWeight="800">

View File

@ -1,65 +0,0 @@
import React, { ComponentProps } from 'react'
import {
Mjml,
MjmlBody,
MjmlSection,
MjmlColumn,
MjmlSpacer,
} from '@faire/mjml-react'
import { render } from '@faire/mjml-react/utils/render'
import { Button, Head, HeroImage, Text } from '../components'
import { SendMailOptions } from 'nodemailer'
import { sendEmail } from '../sendEmail'
type AlmostReachedStorageLimitEmailProps = {
storageLimit: number
url: string
}
export const AlmostReachedStorageLimitEmail = ({
storageLimit,
url,
}: AlmostReachedStorageLimitEmailProps) => {
const readableStorageLimit = `${storageLimit} GB`
return (
<Mjml>
<Head />
<MjmlBody width={600}>
<MjmlSection padding="0">
<MjmlColumn>
<HeroImage src="https://typebot.s3.fr-par.scw.cloud/public/assets/yourBotIsFlyingEmailBanner.png" />
</MjmlColumn>
</MjmlSection>
<MjmlSection padding="0 24px" cssClass="smooth">
<MjmlColumn>
<Text>Your bots are working a lot. That&apos;s amazing. 🤖</Text>
<Text>
This means you&apos;ve almost reached your storage limit. You
currently reached 80% of your {readableStorageLimit} storage
limit.
</Text>
<Text fontWeight="800">
Upon this limit your bots will still continue to collect new
files, but we ask you kindly to upgrade your storage limit or
delete existing results to free up space.
</Text>
<MjmlSpacer height="24px" />
<Button link={url}>Upgrade workspace</Button>
</MjmlColumn>
</MjmlSection>
</MjmlBody>
</Mjml>
)
}
export const sendAlmostReachedStorageLimitEmail = ({
to,
...props
}: Pick<SendMailOptions, 'to'> &
ComponentProps<typeof AlmostReachedStorageLimitEmail>) =>
sendEmail({
to,
subject: "You're close to your storage limit",
html: render(<AlmostReachedStorageLimitEmail {...props} />).html,
})

View File

@ -1,64 +0,0 @@
import { ComponentProps } from 'react'
import {
Mjml,
MjmlBody,
MjmlSection,
MjmlColumn,
MjmlSpacer,
} from '@faire/mjml-react'
import { render } from '@faire/mjml-react/utils/render'
import { Button, Head, HeroImage, Text } from '../components'
import { parseNumberWithCommas } from '@typebot.io/lib'
import { SendMailOptions } from 'nodemailer'
import { sendEmail } from '../sendEmail'
type ReachedChatsLimitEmailProps = {
chatsLimit: number
url: string
}
export const ReachedChatsLimitEmail = ({
chatsLimit,
url,
}: ReachedChatsLimitEmailProps) => {
const readableChatsLimit = parseNumberWithCommas(chatsLimit)
return (
<Mjml>
<Head />
<MjmlBody width={600}>
<MjmlSection padding="0">
<MjmlColumn>
<HeroImage src="https://typebot.s3.fr-par.scw.cloud/public/assets/actionRequiredEmailBanner.png" />
</MjmlColumn>
</MjmlSection>
<MjmlSection padding="0 24px" cssClass="smooth">
<MjmlColumn>
<Text>
It just happened, you&apos;ve reached your monthly{' '}
{readableChatsLimit} chats limit 😮
</Text>
<Text>
If you&apos;d like your bots to continue chatting with your users
this month, then you need to upgrade your plan. 🚀
</Text>
<MjmlSpacer height="24px" />
<Button link={url}>Upgrade workspace</Button>
</MjmlColumn>
</MjmlSection>
</MjmlBody>
</Mjml>
)
}
export const sendReachedChatsLimitEmail = ({
to,
...props
}: Pick<SendMailOptions, 'to'> &
ComponentProps<typeof ReachedChatsLimitEmail>) =>
sendEmail({
to,
subject: "You've reached your chats limit",
html: render(<ReachedChatsLimitEmail {...props} />).html,
})

View File

@ -1,63 +0,0 @@
import React, { ComponentProps } from 'react'
import {
Mjml,
MjmlBody,
MjmlSection,
MjmlColumn,
MjmlSpacer,
} from '@faire/mjml-react'
import { render } from '@faire/mjml-react/utils/render'
import { Button, Head, HeroImage, Text } from '../components'
import { SendMailOptions } from 'nodemailer'
import { sendEmail } from '../sendEmail'
type ReachedStorageLimitEmailProps = {
storageLimit: number
url: string
}
export const ReachedStorageLimitEmail = ({
storageLimit,
url,
}: ReachedStorageLimitEmailProps) => {
const readableStorageLimit = `${storageLimit} GB`
return (
<Mjml>
<Head />
<MjmlBody width={600}>
<MjmlSection padding="0">
<MjmlColumn>
<HeroImage src="https://typebot.s3.fr-par.scw.cloud/public/assets/actionRequiredEmailBanner.png" />
</MjmlColumn>
</MjmlSection>
<MjmlSection padding="0 24px" cssClass="smooth">
<MjmlColumn>
<Text>
It just happened, you&apos;ve reached your {readableStorageLimit}{' '}
storage limit 😮
</Text>
<Text>
If you&apos;d like to continue collecting files, then you need to
upgrade your plan or remove existing results to free up space. 🚀
</Text>
<MjmlSpacer height="24px" />
<Button link={url}>Upgrade workspace</Button>
</MjmlColumn>
</MjmlSection>
</MjmlBody>
</Mjml>
)
}
export const sendReachedStorageLimitEmail = ({
to,
...props
}: Pick<SendMailOptions, 'to'> &
ComponentProps<typeof ReachedStorageLimitEmail>) =>
sendEmail({
to,
subject: "You've reached your storage limit",
html: render(<ReachedStorageLimitEmail {...props} />).html,
})

View File

@ -1,8 +1,5 @@
export * from './AlmostReachedChatsLimitEmail'
export * from './AlmostReachedStorageLimitEmail'
export * from './DefaultBotNotificationEmail'
export * from './GuestInvitationEmail'
export * from './ReachedChatsLimitEmail'
export * from './ReachedStorageLimitEmail'
export * from './WorkspaceMemberInvitationEmail'
export * from './MagicLinkEmail'

View File

@ -3,11 +3,8 @@ import fs from 'fs'
import path from 'path'
import {
AlmostReachedChatsLimitEmail,
AlmostReachedStorageLimitEmail,
DefaultBotNotificationEmail,
GuestInvitationEmail,
ReachedChatsLimitEmail,
ReachedStorageLimitEmail,
WorkspaceMemberInvitation,
} from './emails'
import { MagicLinkEmail } from './emails/MagicLinkEmail'
@ -47,38 +44,12 @@ const createHtmlFile = () => {
path.resolve(__dirname, 'dist', 'almostReachedChatsLimit.html'),
render(
<AlmostReachedChatsLimitEmail
usagePercent={86}
url={'https://app.typebot.io'}
chatsLimit={2000}
/>
).html
)
fs.writeFileSync(
path.resolve(__dirname, 'dist', 'almostReachedStorageLimit.html'),
render(
<AlmostReachedStorageLimitEmail
url={'https://app.typebot.io'}
storageLimit={4}
/>
).html
)
fs.writeFileSync(
path.resolve(__dirname, 'dist', 'reachedChatsLimit.html'),
render(
<ReachedChatsLimitEmail
url={'https://app.typebot.io'}
chatsLimit={10000}
/>
).html
)
fs.writeFileSync(
path.resolve(__dirname, 'dist', 'reachedStorageLimit.html'),
render(
<ReachedStorageLimitEmail
url={'https://app.typebot.io'}
storageLimit={8}
/>
).html
)
fs.writeFileSync(
path.resolve(__dirname, 'dist', 'defaultBotNotification.html'),
render(

View File

@ -14,6 +14,7 @@ export const cleanDatabase = async () => {
if (isFirstOfMonth) {
await deleteArchivedResults()
await deleteArchivedTypebots()
await resetQuarantinedWorkspaces()
}
console.log('Done!')
}
@ -118,4 +119,14 @@ const deleteExpiredVerificationTokens = async () => {
console.log(`Deleted ${count} expired verifiations tokens.`)
}
const resetQuarantinedWorkspaces = async () =>
prisma.workspace.updateMany({
where: {
isQuarantined: true,
},
data: {
isQuarantined: false,
},
})
cleanDatabase().then()

View File

@ -11,6 +11,22 @@ import { sendTelemetryEvents } from '@typebot.io/lib/telemetry/sendTelemetryEven
import { Workspace } from '@typebot.io/schemas'
const prisma = new PrismaClient()
const LIMIT_EMAIL_TRIGGER_PERCENT = 0.8
type WorkspaceForDigest = Pick<
Workspace,
| 'id'
| 'plan'
| 'customChatsLimit'
| 'customStorageLimit'
| 'additionalChatsIndex'
| 'additionalStorageIndex'
| 'isQuarantined'
> & {
members: (Pick<MemberInWorkspace, 'role'> & {
user: { id: string; email: string | null }
})[]
}
export const sendTotalResultsDigest = async () => {
await promptAndSetEnvironment('production')
@ -53,12 +69,15 @@ export const sendTotalResultsDigest = async () => {
select: {
id: true,
typebots: { select: { id: true } },
members: { select: { userId: true, role: true } },
members: {
select: { user: { select: { id: true, email: true } }, role: true },
},
additionalChatsIndex: true,
additionalStorageIndex: true,
customChatsLimit: true,
customStorageLimit: true,
plan: true,
isQuarantined: true,
},
})
@ -71,7 +90,7 @@ export const sendTotalResultsDigest = async () => {
return workspace.members
.filter((member) => member.role !== WorkspaceRole.GUEST)
.map((member, memberIndex) => ({
userId: member.userId,
userId: member.user.id,
workspace: workspace,
typebotId: result.typebotId,
totalResultsYesterday: result._count._all,
@ -115,20 +134,13 @@ export const sendTotalResultsDigest = async () => {
}
const sendAlertIfLimitReached = async (
workspaces: (Pick<
Workspace,
| 'id'
| 'plan'
| 'customChatsLimit'
| 'customStorageLimit'
| 'additionalChatsIndex'
| 'additionalStorageIndex'
> & { members: Pick<MemberInWorkspace, 'userId' | 'role'>[] })[]
workspaces: WorkspaceForDigest[]
): Promise<TelemetryEvent[]> => {
const events: TelemetryEvent[] = []
const taggedWorkspaces: string[] = []
for (const workspace of workspaces) {
if (taggedWorkspaces.includes(workspace.id)) continue
if (taggedWorkspaces.includes(workspace.id) || workspace.isQuarantined)
continue
taggedWorkspaces.push(workspace.id)
const { totalChatsUsed, totalStorageUsed } = await getUsage(workspace.id)
const totalStorageUsedInGb = totalStorageUsed / 1024 / 1024 / 1024
@ -145,7 +157,7 @@ const sendAlertIfLimitReached = async (
(member) =>
({
name: 'Workspace limit reached',
userId: member.userId,
userId: member.user.id,
workspaceId: workspace.id,
data: {
totalChatsUsed,
@ -156,7 +168,20 @@ const sendAlertIfLimitReached = async (
} satisfies TelemetryEvent)
)
)
continue
}
// if (
// chatsLimit > 0 &&
// totalChatsUsed >= chatsLimit * LIMIT_EMAIL_TRIGGER_PERCENT
// )
// await sendAlmostReachedChatsLimitEmail({
// to: workspace.members
// .map((member) => member.user.email)
// .filter(isDefined),
// usagePercent: Math.round((totalChatsUsed / chatsLimit) * 100),
// chatsLimit,
// url: `https://app.typebot.io/typebots?workspaceId=${workspace.id}`,
// })
}
return events
}