diff --git a/apps/builder/src/features/billing/api/updateSubscription.ts b/apps/builder/src/features/billing/api/updateSubscription.ts index 3afe99a17..769a34e41 100644 --- a/apps/builder/src/features/billing/api/updateSubscription.ts +++ b/apps/builder/src/features/billing/api/updateSubscription.ts @@ -148,10 +148,7 @@ export const updateSubscription = authenticatedProcedure plan, additionalChatsIndex: additionalChats, additionalStorageIndex: additionalStorage, - chatsLimitFirstEmailSentAt: null, - chatsLimitSecondEmailSentAt: null, - storageLimitFirstEmailSentAt: null, - storageLimitSecondEmailSentAt: null, + isQuarantined: false, }, }) diff --git a/apps/builder/src/pages/api/stripe/webhook.ts b/apps/builder/src/pages/api/stripe/webhook.ts index 07ac2ff0c..7f968b692 100644 --- a/apps/builder/src/pages/api/stripe/webhook.ts +++ b/apps/builder/src/pages/api/stripe/webhook.ts @@ -69,10 +69,7 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => { stripeId: session.customer as string, additionalChatsIndex: parseInt(additionalChats), additionalStorageIndex: parseInt(additionalStorage), - chatsLimitFirstEmailSentAt: null, - chatsLimitSecondEmailSentAt: null, - storageLimitFirstEmailSentAt: null, - storageLimitSecondEmailSentAt: null, + isQuarantined: false, }, }) @@ -137,10 +134,6 @@ const webhookHandler = async (req: NextApiRequest, res: NextApiResponse) => { plan: Plan.FREE, additionalChatsIndex: 0, additionalStorageIndex: 0, - chatsLimitFirstEmailSentAt: null, - chatsLimitSecondEmailSentAt: null, - storageLimitFirstEmailSentAt: null, - storageLimitSecondEmailSentAt: null, customChatsLimit: null, customStorageLimit: null, customSeatsLimit: null, diff --git a/apps/viewer/src/features/blocks/inputs/fileUpload/api/getUploadUrl.ts b/apps/viewer/src/features/blocks/inputs/fileUpload/api/getUploadUrl.ts index 0490f2ca7..1f8c7dcfe 100644 --- a/apps/viewer/src/features/blocks/inputs/fileUpload/api/getUploadUrl.ts +++ b/apps/viewer/src/features/blocks/inputs/fileUpload/api/getUploadUrl.ts @@ -1,7 +1,6 @@ import { publicProcedure } from '@/helpers/server/trpc' import prisma from '@/lib/prisma' import { TRPCError } from '@trpc/server' -import { getStorageLimit } from '@typebot.io/lib/pricing' import { FileInputBlock, InputBlockType, @@ -9,16 +8,9 @@ import { PublicTypebot, TypebotLinkBlock, } from '@typebot.io/schemas' -import { byId, env, isDefined } from '@typebot.io/lib' +import { byId, isDefined } from '@typebot.io/lib' import { z } from 'zod' import { generatePresignedUrl } from '@typebot.io/lib/api/storage' -import { - sendAlmostReachedStorageLimitEmail, - sendReachedStorageLimitEmail, -} from '@typebot.io/emails' -import { WorkspaceRole } from '@typebot.io/prisma' - -const LIMIT_EMAIL_TRIGGER_PERCENT = 0.8 export const getUploadUrl = publicProcedure .meta({ @@ -58,7 +50,6 @@ export const getUploadUrl = publicProcedure 'S3 not properly configured. Missing one of those variables: S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY', }) - await checkIfStorageLimitReached(typebotId) const publicTypebot = (await prisma.publicTypebot.findFirst({ where: { typebotId }, select: { @@ -118,111 +109,3 @@ const getFileUploadBlock = async ( return fileUploadBlockFromLinkedTypebots return null } - -const checkIfStorageLimitReached = async ( - typebotId: string -): Promise => { - const typebot = await prisma.typebot.findUnique({ - where: { id: typebotId }, - select: { - workspace: { - select: { - id: true, - additionalStorageIndex: true, - plan: true, - storageLimitFirstEmailSentAt: true, - storageLimitSecondEmailSentAt: true, - customStorageLimit: true, - }, - }, - }, - }) - if (!typebot?.workspace) throw new Error('Workspace not found') - const { workspace } = typebot - const { - _sum: { storageUsed: totalStorageUsed }, - } = await prisma.answer.aggregate({ - where: { - storageUsed: { gt: 0 }, - result: { - typebot: { - workspaceId: typebot.workspace.id, - }, - }, - }, - _sum: { storageUsed: true }, - }) - if (!totalStorageUsed) return false - const hasSentFirstEmail = workspace.storageLimitFirstEmailSentAt !== null - const hasSentSecondEmail = workspace.storageLimitSecondEmailSentAt !== null - const storageLimit = getStorageLimit(typebot.workspace) - if (storageLimit === -1) return false - const storageLimitBytes = storageLimit * 1024 * 1024 * 1024 - if ( - totalStorageUsed >= storageLimitBytes * LIMIT_EMAIL_TRIGGER_PERCENT && - !hasSentFirstEmail && - env('E2E_TEST') !== 'true' - ) - await sendAlmostReachStorageLimitNotification({ - workspaceId: workspace.id, - storageLimit, - }) - if ( - totalStorageUsed >= storageLimitBytes && - !hasSentSecondEmail && - env('E2E_TEST') !== 'true' - ) - await sendReachStorageLimitNotification({ - workspaceId: workspace.id, - storageLimit, - }) - return totalStorageUsed >= storageLimitBytes -} - -const sendAlmostReachStorageLimitNotification = async ({ - workspaceId, - storageLimit, -}: { - workspaceId: string - storageLimit: number -}) => { - const members = await prisma.memberInWorkspace.findMany({ - where: { role: WorkspaceRole.ADMIN, workspaceId }, - include: { user: { select: { email: true } } }, - }) - - await sendAlmostReachedStorageLimitEmail({ - to: members.map((member) => member.user.email).filter(isDefined), - storageLimit, - url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspaceId}`, - }) - - await prisma.workspace.update({ - where: { id: workspaceId }, - data: { storageLimitFirstEmailSentAt: new Date() }, - }) -} - -const sendReachStorageLimitNotification = async ({ - workspaceId, - storageLimit, -}: { - workspaceId: string - storageLimit: number -}) => { - const members = await prisma.memberInWorkspace.findMany({ - where: { role: WorkspaceRole.ADMIN, workspaceId }, - include: { user: { select: { email: true } } }, - }) - - await sendReachedStorageLimitEmail({ - to: members.map((member) => member.user.email).filter(isDefined), - storageLimit, - url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspaceId}`, - }) - - await prisma.workspace.update({ - where: { id: workspaceId }, - data: { storageLimitSecondEmailSentAt: new Date() }, - }) -} diff --git a/apps/viewer/src/features/usage/checkChatsUsage.ts b/apps/viewer/src/features/usage/checkChatsUsage.ts deleted file mode 100644 index 16fbefa58..000000000 --- a/apps/viewer/src/features/usage/checkChatsUsage.ts +++ /dev/null @@ -1,145 +0,0 @@ -import prisma from '@/lib/prisma' -import { WorkspaceRole } from '@typebot.io/prisma' -import { - sendAlmostReachedChatsLimitEmail, - sendReachedChatsLimitEmail, -} from '@typebot.io/emails' -import { Workspace } from '@typebot.io/schemas' -import { env, isDefined } from '@typebot.io/lib' -import { getChatsLimit } from '@typebot.io/lib/pricing' - -const LIMIT_EMAIL_TRIGGER_PERCENT = 0.8 - -export const checkChatsUsage = async (props: { - typebotId: string - workspace?: Pick< - Workspace, - | 'id' - | 'plan' - | 'additionalChatsIndex' - | 'chatsLimitFirstEmailSentAt' - | 'chatsLimitSecondEmailSentAt' - | 'customChatsLimit' - > -}) => { - const typebot = props.workspace - ? null - : await prisma.typebot.findUnique({ - where: { - id: props.typebotId, - }, - include: { - workspace: { - select: { - id: true, - plan: true, - additionalChatsIndex: true, - chatsLimitFirstEmailSentAt: true, - chatsLimitSecondEmailSentAt: true, - customChatsLimit: true, - }, - }, - }, - }) - - const workspace = props.workspace || typebot?.workspace - - if (!workspace) return false - - const chatsLimit = getChatsLimit(workspace) - if (chatsLimit === -1) return - const now = new Date() - const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1) - const firstDayOfNextMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1) - const chatsCount = await prisma.$transaction(async (tx) => { - const typebotIds = await tx.typebot.findMany({ - where: { - workspaceId: workspace.id, - }, - select: { id: true }, - }) - - return tx.result.count({ - where: { - typebotId: { in: typebotIds.map((typebot) => typebot.id) }, - hasStarted: true, - createdAt: { gte: firstDayOfMonth, lte: firstDayOfNextMonth }, - }, - }) - }) - const hasSentFirstEmail = - workspace.chatsLimitFirstEmailSentAt !== null && - workspace.chatsLimitFirstEmailSentAt < firstDayOfNextMonth && - workspace.chatsLimitFirstEmailSentAt > firstDayOfMonth - const hasSentSecondEmail = - workspace.chatsLimitSecondEmailSentAt !== null && - workspace.chatsLimitSecondEmailSentAt < firstDayOfNextMonth && - workspace.chatsLimitSecondEmailSentAt > firstDayOfMonth - if ( - chatsCount >= chatsLimit * LIMIT_EMAIL_TRIGGER_PERCENT && - !hasSentFirstEmail && - env('E2E_TEST') !== 'true' - ) - await sendAlmostReachChatsLimitNotification({ - workspaceId: workspace.id, - chatsLimit, - }) - if ( - chatsCount >= chatsLimit && - !hasSentSecondEmail && - env('E2E_TEST') !== 'true' - ) - await sendReachedAlertNotification({ - workspaceId: workspace.id, - chatsLimit, - }) - return chatsCount >= chatsLimit -} - -const sendAlmostReachChatsLimitNotification = async ({ - workspaceId, - chatsLimit, -}: { - workspaceId: string - chatsLimit: number -}) => { - const members = await prisma.memberInWorkspace.findMany({ - where: { role: WorkspaceRole.ADMIN, workspaceId }, - include: { user: { select: { email: true } } }, - }) - - await sendAlmostReachedChatsLimitEmail({ - to: members.map((member) => member.user.email).filter(isDefined), - chatsLimit, - url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspaceId}`, - }) - - await prisma.workspace.update({ - where: { id: workspaceId }, - data: { chatsLimitFirstEmailSentAt: new Date() }, - }) -} - -const sendReachedAlertNotification = async ({ - workspaceId, - chatsLimit, -}: { - workspaceId: string - chatsLimit: number -}) => { - const members = await prisma.memberInWorkspace.findMany({ - where: { role: WorkspaceRole.ADMIN, workspaceId }, - include: { user: { select: { email: true } } }, - }) - - await sendReachedChatsLimitEmail({ - to: members.map((member) => member.user.email).filter(isDefined), - chatsLimit, - url: `${process.env.NEXTAUTH_URL}/typebots?workspaceId=${workspaceId}`, - }) - - await prisma.workspace.update({ - where: { id: workspaceId }, - data: { chatsLimitSecondEmailSentAt: new Date() }, - }) -} diff --git a/apps/viewer/src/pages/api/typebots/[typebotId]/results.ts b/apps/viewer/src/pages/api/typebots/[typebotId]/results.ts index ff206887d..d9fd0a299 100644 --- a/apps/viewer/src/pages/api/typebots/[typebotId]/results.ts +++ b/apps/viewer/src/pages/api/typebots/[typebotId]/results.ts @@ -3,7 +3,6 @@ import prisma from '@/lib/prisma' import { ResultWithAnswers } from '@typebot.io/schemas' import { NextApiRequest, NextApiResponse } from 'next' import { methodNotAllowed } from '@typebot.io/lib/api' -import { checkChatsUsage } from '@/features/usage/checkChatsUsage' const handler = async (req: NextApiRequest, res: NextApiResponse) => { if (req.method === 'GET') { @@ -26,8 +25,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => { } if (req.method === 'POST') { const typebotId = req.query.typebotId as string - const hasReachedLimit = await checkChatsUsage({ typebotId }) - if (hasReachedLimit) return res.send({ result: null, hasReachedLimit }) + const typebot = await prisma.typebot.findFirst({ + where: { id: typebotId }, + select: { workspace: { select: { isQuarantined: true } } }, + }) + if (typebot?.workspace.isQuarantined) + return res.send({ result: null, hasReachedLimit: true }) const result = await prisma.result.create({ data: { typebotId,