🗃️ (results) Improve result delete queries
This commit is contained in:
@@ -1,24 +1,16 @@
|
|||||||
import prisma from '@/lib/prisma'
|
import prisma from '@/lib/prisma'
|
||||||
import { canWriteTypebots } from '@/utils/api/dbRules'
|
|
||||||
import { deleteFiles } from '@/utils/api/storage'
|
import { deleteFiles } from '@/utils/api/storage'
|
||||||
import { User, Prisma } from 'db'
|
import { Prisma } from 'db'
|
||||||
import { InputBlockType, Typebot } from 'models'
|
import { InputBlockType, Typebot } from 'models'
|
||||||
|
|
||||||
export const archiveResults = async ({
|
export const archiveResults = async ({
|
||||||
typebotId,
|
typebot,
|
||||||
user,
|
|
||||||
resultsFilter,
|
resultsFilter,
|
||||||
}: {
|
}: {
|
||||||
typebotId: string
|
typebot: Pick<Typebot, 'groups'>
|
||||||
user: User
|
|
||||||
resultsFilter?: Prisma.ResultWhereInput
|
resultsFilter?: Prisma.ResultWhereInput
|
||||||
}) => {
|
}) => {
|
||||||
const typebot = await prisma.typebot.findFirst({
|
const fileUploadBlockIds = typebot.groups
|
||||||
where: canWriteTypebots(typebotId, user),
|
|
||||||
select: { groups: true },
|
|
||||||
})
|
|
||||||
if (!typebot) return { success: false }
|
|
||||||
const fileUploadBlockIds = (typebot as Typebot).groups
|
|
||||||
.flatMap((g) => g.blocks)
|
.flatMap((g) => g.blocks)
|
||||||
.filter((b) => b.type === InputBlockType.FILE)
|
.filter((b) => b.type === InputBlockType.FILE)
|
||||||
.map((b) => b.id)
|
.map((b) => b.id)
|
||||||
|
|||||||
@@ -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 { authenticatedProcedure } from '@/utils/server/trpc'
|
||||||
import { TRPCError } from '@trpc/server'
|
import { TRPCError } from '@trpc/server'
|
||||||
|
import { Typebot } from 'models'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { archiveResults } from '../archiveResults'
|
import { archiveResults } from '../archiveResults'
|
||||||
|
|
||||||
@@ -29,12 +30,20 @@ export const deleteResultsProcedure = authenticatedProcedure
|
|||||||
.mutation(async ({ input, ctx: { user } }) => {
|
.mutation(async ({ input, ctx: { user } }) => {
|
||||||
const idsArray = input.resultIds?.split(',')
|
const idsArray = input.resultIds?.split(',')
|
||||||
const { typebotId } = input
|
const { typebotId } = input
|
||||||
const { success } = await archiveResults({
|
const typebot = (await getTypebot({
|
||||||
|
accessLevel: 'write',
|
||||||
typebotId,
|
typebotId,
|
||||||
user,
|
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: {
|
resultsFilter: {
|
||||||
id: (idsArray?.length ?? 0) > 0 ? { in: idsArray } : undefined,
|
id: (idsArray?.length ?? 0) > 0 ? { in: idsArray } : undefined,
|
||||||
typebot: canWriteTypebots(typebotId, user),
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
35
apps/builder/src/features/typebot/api/utils/getTypebot.ts
Normal file
35
apps/builder/src/features/typebot/api/utils/getTypebot.ts
Normal file
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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'
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CollaborationType, CollaboratorsOnTypebots, Prisma, User } from 'db'
|
import { CollaborationType, Prisma } from 'db'
|
||||||
import prisma from '@/lib/prisma'
|
import prisma from '@/lib/prisma'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { methodNotAllowed, notAuthenticated } from 'utils/api'
|
import { methodNotAllowed, notAuthenticated } from 'utils/api'
|
||||||
@@ -6,7 +6,9 @@ import { getAuthenticatedUser } from '@/features/auth/api'
|
|||||||
import { archiveResults } from '@/features/results/api'
|
import { archiveResults } from '@/features/results/api'
|
||||||
import { Typebot, typebotSchema } from 'models'
|
import { Typebot, typebotSchema } from 'models'
|
||||||
import { captureEvent } from '@sentry/nextjs'
|
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 handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
const user = await getAuthenticatedUser(req)
|
const user = await getAuthenticatedUser(req)
|
||||||
@@ -25,7 +27,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
webhooks: true,
|
webhooks: true,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if (!typebot || !(await canReadTypebots(typebot, user)))
|
if (!typebot || (await isReadTypebotForbidden(typebot, user)))
|
||||||
return res.status(404).send({ typebot: null })
|
return res.status(404).send({ typebot: null })
|
||||||
|
|
||||||
const { publishedTypebot, collaborators, webhooks, ...restOfTypebot } =
|
const { publishedTypebot, collaborators, webhooks, ...restOfTypebot } =
|
||||||
@@ -42,18 +44,17 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (req.method === 'DELETE') {
|
if (req.method === 'DELETE') {
|
||||||
const typebot = await prisma.typebot.findFirst({
|
const typebot = (await getTypebot({
|
||||||
where: { id: typebotId },
|
accessLevel: 'write',
|
||||||
select: {
|
|
||||||
workspaceId: true,
|
|
||||||
collaborators: { select: { userId: true, type: true } },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (!typebot || !(await canWriteTypebots(typebot, user)))
|
|
||||||
return res.status(404).send({ typebot: null })
|
|
||||||
const { success } = await archiveResults({
|
|
||||||
typebotId,
|
|
||||||
user,
|
user,
|
||||||
|
typebotId,
|
||||||
|
select: {
|
||||||
|
groups: true,
|
||||||
|
},
|
||||||
|
})) as Pick<Typebot, 'groups'> | null
|
||||||
|
if (!typebot) return res.status(404).send({ typebot: null })
|
||||||
|
const { success } = await archiveResults({
|
||||||
|
typebot,
|
||||||
resultsFilter: { typebotId },
|
resultsFilter: { typebotId },
|
||||||
})
|
})
|
||||||
if (!success) return res.status(500).send({ success: false })
|
if (!success) return res.status(500).send({ success: false })
|
||||||
@@ -84,18 +85,17 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const typebot = await prisma.typebot.findFirst({
|
const typebot = await getTypebot({
|
||||||
where: { id: typebotId },
|
accessLevel: 'write',
|
||||||
|
typebotId,
|
||||||
|
user,
|
||||||
select: {
|
select: {
|
||||||
updatedAt: true,
|
updatedAt: true,
|
||||||
workspaceId: true,
|
|
||||||
collaborators: { select: { userId: true, type: true } },
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if (!typebot || !(await canWriteTypebots(typebot, user)))
|
if (!typebot) return res.status(404).send({ message: 'Typebot not found' })
|
||||||
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' })
|
return res.send({ message: 'Found newer version of typebot in database' })
|
||||||
const typebots = await prisma.typebot.updateMany({
|
const typebots = await prisma.typebot.updateMany({
|
||||||
where: { id: typebotId },
|
where: { id: typebotId },
|
||||||
@@ -110,16 +110,12 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (req.method === 'PATCH') {
|
if (req.method === 'PATCH') {
|
||||||
const typebot = await prisma.typebot.findFirst({
|
const typebot = await getTypebot({
|
||||||
where: { id: typebotId },
|
accessLevel: 'write',
|
||||||
select: {
|
typebotId,
|
||||||
updatedAt: true,
|
user,
|
||||||
workspaceId: true,
|
|
||||||
collaborators: { select: { userId: true, type: true } },
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
if (!typebot || !(await canWriteTypebots(typebot, user)))
|
if (!typebot) return res.status(404).send({ message: 'Typebot not found' })
|
||||||
return res.status(404).send({ message: 'Typebot not found' })
|
|
||||||
const data = typeof req.body === 'string' ? JSON.parse(req.body) : req.body
|
const data = typeof req.body === 'string' ? JSON.parse(req.body) : req.body
|
||||||
const typebots = await prisma.typebot.updateMany({
|
const typebots = await prisma.typebot.updateMany({
|
||||||
where: { id: typebotId },
|
where: { id: typebotId },
|
||||||
@@ -130,50 +126,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
return methodNotAllowed(res)
|
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
|
// TODO: Remove in a month
|
||||||
const removeOldProperties = (data: unknown) => {
|
const removeOldProperties = (data: unknown) => {
|
||||||
if (data && typeof data === 'object' && 'publishedTypebotId' in data) {
|
if (data && typeof data === 'object' && 'publishedTypebotId' in data) {
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ model Credentials {
|
|||||||
id String @id @default(cuid())
|
id String @id @default(cuid())
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
workspaceId String
|
workspaceId String
|
||||||
data String
|
data String @db.Text
|
||||||
name String
|
name String
|
||||||
type String
|
type String
|
||||||
iv String
|
iv String
|
||||||
|
|||||||
Reference in New Issue
Block a user