Skip to content

Commit

Permalink
🗃️ (results) Improve result delete queries
Browse files Browse the repository at this point in the history
  • Loading branch information
baptisteArno committed Feb 13, 2023
1 parent bac97a8 commit 1d4d39c
Show file tree
Hide file tree
Showing 7 changed files with 128 additions and 88 deletions.
16 changes: 4 additions & 12 deletions apps/builder/src/features/results/api/archiveResults.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
import prisma from '@/lib/prisma'
import { canWriteTypebots } from '@/utils/api/dbRules'
import { deleteFiles } from '@/utils/api/storage'
import { User, Prisma } from 'db'
import { Prisma } from 'db'
import { InputBlockType, Typebot } from 'models'

export const archiveResults = async ({
typebotId,
user,
typebot,
resultsFilter,
}: {
typebotId: string
user: User
typebot: Pick<Typebot, 'groups'>
resultsFilter?: Prisma.ResultWhereInput
}) => {
const typebot = await prisma.typebot.findFirst({
where: canWriteTypebots(typebotId, user),
select: { groups: true },
})
if (!typebot) return { success: false }
const fileUploadBlockIds = (typebot as Typebot).groups
const fileUploadBlockIds = typebot.groups
.flatMap((g) => g.blocks)
.filter((b) => b.type === InputBlockType.FILE)
.map((b) => b.id)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { canWriteTypebots } from '@/utils/api/dbRules'
import { getTypebot } from '@/features/typebot/api/utils/getTypebot'
import { authenticatedProcedure } from '@/utils/server/trpc'
import { TRPCError } from '@trpc/server'
import { Typebot } from 'models'
import { z } from 'zod'
import { archiveResults } from '../archiveResults'

Expand Down Expand Up @@ -29,12 +30,20 @@ export const deleteResultsProcedure = authenticatedProcedure
.mutation(async ({ input, ctx: { user } }) => {
const idsArray = input.resultIds?.split(',')
const { typebotId } = input
const { success } = await archiveResults({
const typebot = (await getTypebot({
accessLevel: 'write',
typebotId,
user,
select: {
groups: true,
},
})) as Pick<Typebot, 'groups'> | null
if (!typebot)
throw new TRPCError({ code: 'NOT_FOUND', message: 'Typebot not found' })
const { success } = await archiveResults({
typebot,
resultsFilter: {
id: (idsArray?.length ?? 0) > 0 ? { in: idsArray } : undefined,
typebot: canWriteTypebots(typebotId, user),
},
})

Expand Down
35 changes: 35 additions & 0 deletions apps/builder/src/features/typebot/api/utils/getTypebot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import prisma from '@/lib/prisma'
import { Prisma, User } from 'db'
import { isReadTypebotForbidden } from './isReadTypebotForbidden'
import { isWriteTypebotForbidden } from './isWriteTypebotForbidden'

type Props<T extends Prisma.TypebotSelect> = {
typebotId: string
user: Pick<User, 'id' | 'email'>
accessLevel: 'read' | 'write'
select?: T
}

export const getTypebot = async <T extends Prisma.TypebotSelect>({
typebotId,
user,
accessLevel,
select,
}: Props<T>) => {
const typebot = await prisma.typebot.findFirst({
where: {
id: typebotId,
},
select: {
...select,
workspaceId: true,
collaborators: { select: { userId: true, type: true } },
},
})
if (!typebot) return null
if (accessLevel === 'read' && (await isReadTypebotForbidden(typebot, user)))
return null
if (accessLevel === 'write' && (await isWriteTypebotForbidden(typebot, user)))
return null
return typebot
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import prisma from '@/lib/prisma'
import { CollaboratorsOnTypebots, User } from 'db'
import { Typebot } from 'models'
import { isNotDefined } from 'utils'

export const isReadTypebotForbidden = async (
typebot: Pick<Typebot, 'workspaceId'> & {
collaborators: Pick<CollaboratorsOnTypebots, 'userId' | 'type'>[]
},
user: Pick<User, 'email' | 'id'>
) => {
if (
process.env.ADMIN_EMAIL === user.email ||
typebot.collaborators.find(
(collaborator) => collaborator.userId === user.id
)
)
return false
const memberInWorkspace = await prisma.memberInWorkspace.findFirst({
where: {
workspaceId: typebot.workspaceId,
userId: user.id,
},
})
return isNotDefined(memberInWorkspace)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import prisma from '@/lib/prisma'
import { CollaborationType, CollaboratorsOnTypebots, User } from 'db'
import { Typebot } from 'models'
import { isNotDefined } from 'utils'

export const isWriteTypebotForbidden = async (
typebot: Pick<Typebot, 'workspaceId'> & {
collaborators: Pick<CollaboratorsOnTypebots, 'userId' | 'type'>[]
},
user: Pick<User, 'email' | 'id'>
) => {
if (
process.env.ADMIN_EMAIL === user.email ||
typebot.collaborators.find(
(collaborator) => collaborator.userId === user.id
)?.type === CollaborationType.WRITE
)
return false
const memberInWorkspace = await prisma.memberInWorkspace.findFirst({
where: {
workspaceId: typebot.workspaceId,
userId: user.id,
},
})
return isNotDefined(memberInWorkspace) || memberInWorkspace.role === 'GUEST'
}
96 changes: 24 additions & 72 deletions apps/builder/src/pages/api/typebots/[typebotId].ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { CollaborationType, CollaboratorsOnTypebots, Prisma, User } from 'db'
import { CollaborationType, Prisma } from 'db'
import prisma from '@/lib/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { methodNotAllowed, notAuthenticated } from 'utils/api'
import { getAuthenticatedUser } from '@/features/auth/api'
import { archiveResults } from '@/features/results/api'
import { Typebot, typebotSchema } from 'models'
import { captureEvent } from '@sentry/nextjs'
import { isDefined, omit } from 'utils'
import { omit } from 'utils'
import { getTypebot } from '@/features/typebot/api/utils/getTypebot'
import { isReadTypebotForbidden } from '@/features/typebot/api/utils/isReadTypebotForbidden'

const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getAuthenticatedUser(req)
Expand All @@ -25,7 +27,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
webhooks: true,
},
})
if (!typebot || !(await canReadTypebots(typebot, user)))
if (!typebot || (await isReadTypebotForbidden(typebot, user)))
return res.status(404).send({ typebot: null })

const { publishedTypebot, collaborators, webhooks, ...restOfTypebot } =
Expand All @@ -42,18 +44,17 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
}

if (req.method === 'DELETE') {
const typebot = await prisma.typebot.findFirst({
where: { id: typebotId },
const typebot = (await getTypebot({
accessLevel: 'write',
user,
typebotId,
select: {
workspaceId: true,
collaborators: { select: { userId: true, type: true } },
groups: true,
},
})
if (!typebot || !(await canWriteTypebots(typebot, user)))
return res.status(404).send({ typebot: null })
})) as Pick<Typebot, 'groups'> | null
if (!typebot) return res.status(404).send({ typebot: null })
const { success } = await archiveResults({
typebotId,
user,
typebot,
resultsFilter: { typebotId },
})
if (!success) return res.status(500).send({ success: false })
Expand Down Expand Up @@ -84,18 +85,17 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
})
}

const typebot = await prisma.typebot.findFirst({
where: { id: typebotId },
const typebot = await getTypebot({
accessLevel: 'write',
typebotId,
user,
select: {
updatedAt: true,
workspaceId: true,
collaborators: { select: { userId: true, type: true } },
},
})
if (!typebot || !(await canWriteTypebots(typebot, user)))
return res.status(404).send({ message: 'Typebot not found' })
if (!typebot) return res.status(404).send({ message: 'Typebot not found' })

if (typebot.updatedAt > new Date(data.updatedAt))
if ((typebot.updatedAt as Date) > new Date(data.updatedAt))
return res.send({ message: 'Found newer version of typebot in database' })
const typebots = await prisma.typebot.updateMany({
where: { id: typebotId },
Expand All @@ -110,16 +110,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
}

if (req.method === 'PATCH') {
const typebot = await prisma.typebot.findFirst({
where: { id: typebotId },
select: {
updatedAt: true,
workspaceId: true,
collaborators: { select: { userId: true, type: true } },
},
const typebot = await getTypebot({
accessLevel: 'write',
typebotId,
user,
})
if (!typebot || !(await canWriteTypebots(typebot, user)))
return res.status(404).send({ message: 'Typebot not found' })
if (!typebot) return res.status(404).send({ message: 'Typebot not found' })
const data = typeof req.body === 'string' ? JSON.parse(req.body) : req.body
const typebots = await prisma.typebot.updateMany({
where: { id: typebotId },
Expand All @@ -130,50 +126,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
return methodNotAllowed(res)
}

const canReadTypebots = async (
typebot: Pick<Typebot, 'workspaceId'> & {
collaborators: Pick<CollaboratorsOnTypebots, 'userId' | 'type'>[]
},
user: Pick<User, 'email' | 'id'>
) => {
if (
process.env.ADMIN_EMAIL === user.email ||
typebot.collaborators.find(
(collaborator) => collaborator.userId === user.id
)
)
return true
const memberInWorkspace = await prisma.memberInWorkspace.findFirst({
where: {
workspaceId: typebot.workspaceId,
userId: user.id,
},
})
return isDefined(memberInWorkspace)
}

const canWriteTypebots = async (
typebot: Pick<Typebot, 'workspaceId'> & {
collaborators: Pick<CollaboratorsOnTypebots, 'userId' | 'type'>[]
},
user: Pick<User, 'email' | 'id'>
) => {
if (
process.env.ADMIN_EMAIL === user.email ||
typebot.collaborators.find(
(collaborator) => collaborator.userId === user.id
)?.type === CollaborationType.WRITE
)
return true
const memberInWorkspace = await prisma.memberInWorkspace.findFirst({
where: {
workspaceId: typebot.workspaceId,
userId: user.id,
},
})
return memberInWorkspace && memberInWorkspace?.role !== 'GUEST'
}

// TODO: Remove in a month
const removeOldProperties = (data: unknown) => {
if (data && typeof data === 'object' && 'publishedTypebotId' in data) {
Expand Down
2 changes: 1 addition & 1 deletion packages/db/mysql/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ model Credentials {
id String @id @default(cuid())
createdAt DateTime @default(now())
workspaceId String
data String
data String @db.Text
name String
type String
iv String
Expand Down

4 comments on commit 1d4d39c

@vercel
Copy link

@vercel vercel bot commented on 1d4d39c Feb 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

builder-v2 – ./apps/builder

app.typebot.io
builder-v2-git-main-typebot-io.vercel.app
builder-v2-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 1d4d39c Feb 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 1d4d39c Feb 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

viewer-v2 – ./apps/viewer

bot.winglabs.com.br
carsalesenquiry.com
chat.marius.digital
chatbot.matthesv.de
chatbot.repplai.com
demo.botscientis.us
demo.wemakebots.xyz
forms.webisharp.com
kbsub.wpwakanda.com
live.botscientis.us
mentoria.omelhor.vc
nutrisamirbayde.com
order.maitempah.com
help.comebackreward.com
link.venturasuceder.com
mainmenu.diddancing.com
manualhandlingcourse.ie
register.kandabrand.com
signup.hypemarketing.in
subfooter.wpwakanda.com
survey.hypemarketing.in
testbot.afterorigin.com
typebot.influencer.love
91181264.your-access.one
ai.chromebookstoreph.com
form.sergiolimajr.com.br
hunterbot.saleshunter.ai
invite.bridesquadapp.com
link.cascadigital.com.br
onboarding.growthside.io
reward.onlinebotdemo.xyz
stap.venturemarketing.in
type.opaulovieira.com.br
aibot.angrybranding.co.uk
bot.aidigitalmarketing.kr
bot.amicidisanfaustino.it
bot.arraesecenteno.com.br
bot.blackboxsports.com.br
bot.cabinrentalagency.com
bot.fusionstarreviews.com
boyfriend-breakup.riku.ai
brigadeirosemdrama.com.br
chat.ertcrebateportal.com
chat.thehomebuyersusa.com
chat.thisiscrushhouse.com
forms.hiabhaykulkarni.com
healthandsafetycourses.uk
sellmyharleylouisiana.com
testbot.sharemyreview.net
typebot-viewer.vercel.app
verfica.botmachine.com.br
bot.adventureconsulting.hu
casestudyemb.wpwakanda.com
chat.atlasoutfittersk9.com
configurator.bouclidom.com
help.atlasoutfittersk9.com
herbalife.barrettamario.it
homepageonly.wpwakanda.com
liveconvert.kandalearn.com
mainmenu1one.wpwakanda.com
tarian.theiofoundation.org
ted.meujalecobrasil.com.br
type.dericsoncalari.com.br
bot.pinpointinteractive.com
bot.polychromes-project.com
bot.seidinembroseanchetu.it
chatbot.berbelanjabiz.trade
designguide.techyscouts.com
liveconvert2.kandalearn.com
presente.empresarias.com.mx
sell.sellthemotorhome.co.uk
anamnese.odontopavani.com.br
austin.channelautomation.com
bot.marketingplusmindset.com
bot.seidibergamoseanchetu.it
desabafe.sergiolimajr.com.br
download.venturemarketing.in
piazzatorre.barrettamario.it
type.cookieacademyonline.com
bot.brigadeirosemdrama.com.br
forms.escoladeautomacao.com.br
onboarding.libertydreamcare.ie
type.talitasouzamarques.com.br
agendamento.sergiolimajr.com.br
anamnese.clinicamegasjdr.com.br
bookings.littlepartymonkeys.com
bot.comercializadoraomicron.com
elevateyourmind.groovepages.com
viewer-v2-typebot-io.vercel.app
yourfeedback.comebackreward.com
gerador.verificadordehospedes.com
personal-trainer.barrettamario.it
preagendamento.sergiolimajr.com.br
studiotecnicoimmobiliaremerelli.it
download.thailandmicespecialist.com
register.thailandmicespecialist.com
bot.studiotecnicoimmobiliaremerelli.it
pesquisa.escolamodacomproposito.com.br
anamnese.clinicaramosodontologia.com.br
viewer-v2-git-main-typebot-io.vercel.app

@vercel
Copy link

@vercel vercel bot commented on 1d4d39c Feb 13, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

docs – ./apps/docs

docs-git-main-typebot-io.vercel.app
docs-typebot-io.vercel.app
docs.typebot.io

Please sign in to comment.