🔒 Improve workspace API role filtering
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
import prisma from '@/lib/prisma'
|
||||
import { authenticatedProcedure } from '@/helpers/server/trpc'
|
||||
import { TRPCError } from '@trpc/server'
|
||||
import { Typebot, typebotSchema } from '@typebot.io/schemas'
|
||||
import { LogicBlockType, typebotSchema } from '@typebot.io/schemas'
|
||||
import { z } from 'zod'
|
||||
import { getUserRoleInWorkspace } from '@/features/workspace/helpers/getUserRoleInWorkspace'
|
||||
import { isReadTypebotForbidden } from '@/features/typebot/helpers/isReadTypebotForbidden'
|
||||
import { isDefined } from '@typebot.io/lib'
|
||||
|
||||
export const getLinkedTypebots = authenticatedProcedure
|
||||
.meta({
|
||||
openapi: {
|
||||
method: 'GET',
|
||||
path: '/linkedTypebots',
|
||||
path: '/typebots/{typebotId}/linkedTypebots',
|
||||
protect: true,
|
||||
summary: 'Get linked typebots',
|
||||
tags: ['Typebot'],
|
||||
@@ -17,8 +18,7 @@ export const getLinkedTypebots = authenticatedProcedure
|
||||
})
|
||||
.input(
|
||||
z.object({
|
||||
workspaceId: z.string(),
|
||||
typebotIds: z.string().describe('Comma separated list of typebot ids'),
|
||||
typebotId: z.string(),
|
||||
})
|
||||
)
|
||||
.output(
|
||||
@@ -33,20 +33,10 @@ export const getLinkedTypebots = authenticatedProcedure
|
||||
),
|
||||
})
|
||||
)
|
||||
.query(async ({ input: { workspaceId, typebotIds }, ctx: { user } }) => {
|
||||
const typebotIdsArray = typebotIds.split(',')
|
||||
const workspace = await prisma.workspace.findUnique({
|
||||
where: { id: workspaceId },
|
||||
select: { members: true },
|
||||
})
|
||||
const userRole = getUserRoleInWorkspace(user.id, workspace?.members)
|
||||
if (userRole === undefined)
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'Workspace not found' })
|
||||
const typebots = (await prisma.typebot.findMany({
|
||||
.query(async ({ input: { typebotId }, ctx: { user } }) => {
|
||||
const typebot = await prisma.typebot.findFirst({
|
||||
where: {
|
||||
isArchived: { not: true },
|
||||
id: { in: typebotIdsArray },
|
||||
workspaceId,
|
||||
id: typebotId,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
@@ -54,18 +44,69 @@ export const getLinkedTypebots = authenticatedProcedure
|
||||
variables: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
workspaceId: true,
|
||||
collaborators: {
|
||||
select: {
|
||||
type: true,
|
||||
userId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})) as Pick<Typebot, 'id' | 'groups' | 'variables' | 'name' | 'createdAt'>[]
|
||||
|
||||
// To avoid the Out of sort memory error, we sort the typebots manually
|
||||
const sortedTypebots = typebots.sort((a, b) => {
|
||||
return b.createdAt.getTime() - a.createdAt.getTime()
|
||||
})
|
||||
|
||||
if (!typebots)
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'No typebots found' })
|
||||
if (!typebot || (await isReadTypebotForbidden(typebot, user)))
|
||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'No typebot found' })
|
||||
|
||||
const linkedTypebotIds =
|
||||
typebotSchema.shape.groups
|
||||
.parse(typebot.groups)
|
||||
.flatMap((group) => group.blocks)
|
||||
.reduce<string[]>(
|
||||
(typebotIds, block) =>
|
||||
block.type === LogicBlockType.TYPEBOT_LINK &&
|
||||
isDefined(block.options.typebotId) &&
|
||||
!typebotIds.includes(block.options.typebotId)
|
||||
? [...typebotIds, block.options.typebotId]
|
||||
: typebotIds,
|
||||
[]
|
||||
) ?? []
|
||||
|
||||
if (!linkedTypebotIds.length) return { typebots: [] }
|
||||
|
||||
const typebots = (
|
||||
await prisma.typebot.findMany({
|
||||
where: {
|
||||
isArchived: { not: true },
|
||||
id: { in: linkedTypebotIds },
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
groups: true,
|
||||
variables: true,
|
||||
name: true,
|
||||
createdAt: true,
|
||||
workspaceId: true,
|
||||
collaborators: {
|
||||
select: {
|
||||
type: true,
|
||||
userId: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
.filter(async (typebot) => !(await isReadTypebotForbidden(typebot, user)))
|
||||
// To avoid the out of sort memory error, we sort the typebots manually
|
||||
.sort((a, b) => {
|
||||
return b.createdAt.getTime() - a.createdAt.getTime()
|
||||
})
|
||||
.map((typebot) => ({
|
||||
...typebot,
|
||||
groups: typebotSchema.shape.groups.parse(typebot.groups),
|
||||
variables: typebotSchema.shape.variables.parse(typebot.variables),
|
||||
}))
|
||||
|
||||
return {
|
||||
typebots: sortedTypebots,
|
||||
typebots,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -4,6 +4,7 @@ import { TypebotLinkOptions } from '@typebot.io/schemas'
|
||||
import { byId } from '@typebot.io/lib'
|
||||
import { GroupsDropdown } from './GroupsDropdown'
|
||||
import { TypebotsDropdown } from './TypebotsDropdown'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
type Props = {
|
||||
options: TypebotLinkOptions
|
||||
@@ -11,13 +12,22 @@ type Props = {
|
||||
}
|
||||
|
||||
export const TypebotLinkForm = ({ options, onOptionsChange }: Props) => {
|
||||
const { linkedTypebots, typebot } = useTypebot()
|
||||
const { linkedTypebots, typebot, save } = useTypebot()
|
||||
const [linkedTypebotId, setLinkedTypebotId] = useState(options.typebotId)
|
||||
|
||||
const handleTypebotIdChange = async (
|
||||
typebotId: string | 'current' | undefined
|
||||
) => onOptionsChange({ ...options, typebotId })
|
||||
|
||||
const handleTypebotIdChange = (typebotId: string | 'current' | undefined) =>
|
||||
onOptionsChange({ ...options, typebotId })
|
||||
const handleGroupIdChange = (groupId: string | undefined) =>
|
||||
onOptionsChange({ ...options, groupId })
|
||||
|
||||
useEffect(() => {
|
||||
if (linkedTypebotId === options.typebotId) return
|
||||
setLinkedTypebotId(options.typebotId)
|
||||
save().then()
|
||||
}, [linkedTypebotId, options.typebotId, save])
|
||||
|
||||
return (
|
||||
<Stack>
|
||||
{typebot && (
|
||||
|
||||
Reference in New Issue
Block a user