diff --git a/apps/builder/src/features/blocks/logic/typebotLink/api/getLinkedTypebots.ts b/apps/builder/src/features/blocks/logic/typebotLink/api/getLinkedTypebots.ts index 8126cf3b0..83ba1cfc7 100644 --- a/apps/builder/src/features/blocks/logic/typebotLink/api/getLinkedTypebots.ts +++ b/apps/builder/src/features/blocks/logic/typebotLink/api/getLinkedTypebots.ts @@ -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[] - - // 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( + (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, } }) diff --git a/apps/builder/src/features/blocks/logic/typebotLink/components/TypebotLinkForm.tsx b/apps/builder/src/features/blocks/logic/typebotLink/components/TypebotLinkForm.tsx index 4fa1ecdc0..bb062836c 100644 --- a/apps/builder/src/features/blocks/logic/typebotLink/components/TypebotLinkForm.tsx +++ b/apps/builder/src/features/blocks/logic/typebotLink/components/TypebotLinkForm.tsx @@ -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 ( {typebot && ( diff --git a/apps/builder/src/features/credentials/api/createCredentials.ts b/apps/builder/src/features/credentials/api/createCredentials.ts index 3152c1659..b489cba0d 100644 --- a/apps/builder/src/features/credentials/api/createCredentials.ts +++ b/apps/builder/src/features/credentials/api/createCredentials.ts @@ -7,6 +7,7 @@ import { openAICredentialsSchema } from '@typebot.io/schemas/features/blocks/int import { smtpCredentialsSchema } from '@typebot.io/schemas/features/blocks/integrations/sendEmail' import { encrypt } from '@typebot.io/lib/api/encryption' import { z } from 'zod' +import { isWriteWorkspaceForbidden } from '@/features/workspace/helpers/isWriteWorkspaceForbidden copy' const inputShape = { data: true, @@ -44,11 +45,10 @@ export const createCredentials = authenticatedProcedure const workspace = await prisma.workspace.findFirst({ where: { id: credentials.workspaceId, - members: { some: { userId: user.id } }, }, - select: { id: true }, + select: { id: true, members: true }, }) - if (!workspace) + if (!workspace || (await isWriteWorkspaceForbidden(workspace, user))) throw new TRPCError({ code: 'NOT_FOUND', message: 'Workspace not found' }) const { encryptedData, iv } = await encrypt(credentials.data) diff --git a/apps/builder/src/features/credentials/api/deleteCredentials.ts b/apps/builder/src/features/credentials/api/deleteCredentials.ts index c3e0fcded..88d689a14 100644 --- a/apps/builder/src/features/credentials/api/deleteCredentials.ts +++ b/apps/builder/src/features/credentials/api/deleteCredentials.ts @@ -2,6 +2,7 @@ import prisma from '@/lib/prisma' import { authenticatedProcedure } from '@/helpers/server/trpc' import { TRPCError } from '@trpc/server' import { z } from 'zod' +import { isWriteWorkspaceForbidden } from '@/features/workspace/helpers/isWriteWorkspaceForbidden copy' export const deleteCredentials = authenticatedProcedure .meta({ @@ -29,11 +30,10 @@ export const deleteCredentials = authenticatedProcedure const workspace = await prisma.workspace.findFirst({ where: { id: workspaceId, - members: { some: { userId: user.id } }, }, - select: { id: true }, + select: { id: true, members: true }, }) - if (!workspace) + if (!workspace || (await isWriteWorkspaceForbidden(workspace, user))) throw new TRPCError({ code: 'NOT_FOUND', message: 'Workspace not found', diff --git a/apps/builder/src/features/credentials/api/listCredentials.ts b/apps/builder/src/features/credentials/api/listCredentials.ts index 3a551577a..2b5d65f07 100644 --- a/apps/builder/src/features/credentials/api/listCredentials.ts +++ b/apps/builder/src/features/credentials/api/listCredentials.ts @@ -6,6 +6,7 @@ import { googleSheetsCredentialsSchema } from '@typebot.io/schemas/features/bloc import { openAICredentialsSchema } from '@typebot.io/schemas/features/blocks/integrations/openai' import { smtpCredentialsSchema } from '@typebot.io/schemas/features/blocks/integrations/sendEmail' import { z } from 'zod' +import { isReadWorkspaceFobidden } from '@/features/workspace/helpers/isReadWorkspaceFobidden' export const listCredentials = authenticatedProcedure .meta({ @@ -35,21 +36,23 @@ export const listCredentials = authenticatedProcedure const workspace = await prisma.workspace.findFirst({ where: { id: workspaceId, - members: { some: { userId: user.id } }, - }, - select: { id: true }, - }) - if (!workspace) - throw new TRPCError({ code: 'NOT_FOUND', message: 'Workspace not found' }) - const credentials = await prisma.credentials.findMany({ - where: { - type, - workspaceId, }, select: { id: true, - name: true, + members: true, + credentials: { + where: { + type, + }, + select: { + id: true, + name: true, + }, + }, }, }) - return { credentials } + if (!workspace || (await isReadWorkspaceFobidden(workspace, user))) + throw new TRPCError({ code: 'NOT_FOUND', message: 'Workspace not found' }) + + return { credentials: workspace.credentials } }) diff --git a/apps/builder/src/features/editor/providers/TypebotProvider.tsx b/apps/builder/src/features/editor/providers/TypebotProvider.tsx index f3a7ab164..42e25d3b0 100644 --- a/apps/builder/src/features/editor/providers/TypebotProvider.tsx +++ b/apps/builder/src/features/editor/providers/TypebotProvider.tsx @@ -131,27 +131,28 @@ export const TypebotProvider = ({ { redo, undo, flush, canRedo, canUndo, set: setLocalTypebot }, ] = useUndo(undefined) - const linkedTypebotIds = - localTypebot?.groups - .flatMap((b) => b.blocks) - .reduce( - (typebotIds, block) => - block.type === LogicBlockType.TYPEBOT_LINK && - isDefined(block.options.typebotId) && - !typebotIds.includes(block.options.typebotId) - ? [...typebotIds, block.options.typebotId] - : typebotIds, - [] - ) ?? [] + const linkedTypebotIds = useMemo( + () => + typebot?.groups + .flatMap((group) => group.blocks) + .reduce( + (typebotIds, block) => + block.type === LogicBlockType.TYPEBOT_LINK && + isDefined(block.options.typebotId) && + !typebotIds.includes(block.options.typebotId) + ? [...typebotIds, block.options.typebotId] + : typebotIds, + [] + ) ?? [], + [typebot?.groups] + ) const { data: linkedTypebotsData } = trpc.getLinkedTypebots.useQuery( { - workspaceId: localTypebot?.workspaceId as string, - typebotIds: linkedTypebotIds.join(','), + typebotId: typebot?.id as string, }, { - enabled: - isDefined(localTypebot?.workspaceId) && linkedTypebotIds.length > 0, + enabled: isDefined(typebot?.id) && linkedTypebotIds.length > 0, onError: (error) => showToast({ title: 'Error while fetching linkedTypebots', diff --git a/apps/builder/src/features/typebot/api/createTypebot.ts b/apps/builder/src/features/typebot/api/createTypebot.ts index 1913815bf..603d8d010 100644 --- a/apps/builder/src/features/typebot/api/createTypebot.ts +++ b/apps/builder/src/features/typebot/api/createTypebot.ts @@ -16,6 +16,7 @@ import { sanitizeSettings, } from '../helpers/sanitizers' import { createId } from '@paralleldrive/cuid2' +import { sendTelemetryEvents } from '@typebot.io/lib/telemetry/sendTelemetryEvent' export const createTypebot = authenticatedProcedure .meta({ @@ -106,7 +107,21 @@ export const createTypebot = authenticatedProcedure }, }) - return { typebot: typebotSchema.parse(newTypebot) } + const parsedNewTypebot = typebotSchema.parse(newTypebot) + + await sendTelemetryEvents([ + { + name: 'Typebot created', + workspaceId: parsedNewTypebot.workspaceId, + typebotId: parsedNewTypebot.id, + userId: user.id, + data: { + name: newTypebot.name, + }, + }, + ]) + + return { typebot: parsedNewTypebot } }) const defaultGroups = () => { diff --git a/apps/builder/src/features/typebot/api/publishTypebot.ts b/apps/builder/src/features/typebot/api/publishTypebot.ts index 2562891d5..eda90d07e 100644 --- a/apps/builder/src/features/typebot/api/publishTypebot.ts +++ b/apps/builder/src/features/typebot/api/publishTypebot.ts @@ -4,6 +4,7 @@ import { TRPCError } from '@trpc/server' import { typebotSchema } from '@typebot.io/schemas' import { z } from 'zod' import { isWriteTypebotForbidden } from '../helpers/isWriteTypebotForbidden' +import { sendTelemetryEvents } from '@typebot.io/lib/telemetry/sendTelemetryEvent' export const publishTypebot = authenticatedProcedure .meta({ @@ -41,7 +42,7 @@ export const publishTypebot = authenticatedProcedure ) throw new TRPCError({ code: 'NOT_FOUND', message: 'Typebot not found' }) - if (existingTypebot.publishedTypebot) { + if (existingTypebot.publishedTypebot) await prisma.publicTypebot.updateMany({ where: { id: existingTypebot.publishedTypebot.id, @@ -59,22 +60,35 @@ export const publishTypebot = authenticatedProcedure theme: typebotSchema.shape.theme.parse(existingTypebot.theme), }, }) - return { message: 'success' } - } + else + await prisma.publicTypebot.createMany({ + data: { + version: existingTypebot.version, + typebotId: existingTypebot.id, + edges: typebotSchema.shape.edges.parse(existingTypebot.edges), + groups: typebotSchema.shape.groups.parse(existingTypebot.groups), + settings: typebotSchema.shape.settings.parse( + existingTypebot.settings + ), + variables: typebotSchema.shape.variables.parse( + existingTypebot.variables + ), + theme: typebotSchema.shape.theme.parse(existingTypebot.theme), + }, + }) - await prisma.publicTypebot.createMany({ - data: { - version: existingTypebot.version, + await sendTelemetryEvents([ + { + name: 'Typebot published', + workspaceId: existingTypebot.workspaceId, typebotId: existingTypebot.id, - edges: typebotSchema.shape.edges.parse(existingTypebot.edges), - groups: typebotSchema.shape.groups.parse(existingTypebot.groups), - settings: typebotSchema.shape.settings.parse(existingTypebot.settings), - variables: typebotSchema.shape.variables.parse( - existingTypebot.variables - ), - theme: typebotSchema.shape.theme.parse(existingTypebot.theme), + userId: user.id, + data: { + name: existingTypebot.name, + isFirstPublish: existingTypebot.publishedTypebot ? undefined : true, + }, }, - }) + ]) return { message: 'success' } }) diff --git a/apps/builder/src/features/workspace/api/deleteWorkspace.ts b/apps/builder/src/features/workspace/api/deleteWorkspace.ts index fbd625b7b..616d84802 100644 --- a/apps/builder/src/features/workspace/api/deleteWorkspace.ts +++ b/apps/builder/src/features/workspace/api/deleteWorkspace.ts @@ -1,6 +1,8 @@ import prisma from '@/lib/prisma' import { authenticatedProcedure } from '@/helpers/server/trpc' import { z } from 'zod' +import { isAdminWriteWorkspaceForbidden } from '../helpers/isAdminWriteWorkspaceForbidden' +import { TRPCError } from '@trpc/server' export const deleteWorkspace = authenticatedProcedure .meta({ @@ -23,6 +25,14 @@ export const deleteWorkspace = authenticatedProcedure }) ) .mutation(async ({ input: { workspaceId }, ctx: { user } }) => { + const workspace = await prisma.workspace.findFirst({ + where: { id: workspaceId }, + include: { members: true }, + }) + + if (!workspace || (await isAdminWriteWorkspaceForbidden(workspace, user))) + throw new TRPCError({ code: 'NOT_FOUND', message: 'No workspaces found' }) + await prisma.workspace.deleteMany({ where: { members: { some: { userId: user.id } }, id: workspaceId }, }) diff --git a/apps/builder/src/features/workspace/api/getWorkspace.ts b/apps/builder/src/features/workspace/api/getWorkspace.ts index 6619a550f..3c385aea8 100644 --- a/apps/builder/src/features/workspace/api/getWorkspace.ts +++ b/apps/builder/src/features/workspace/api/getWorkspace.ts @@ -1,8 +1,9 @@ import prisma from '@/lib/prisma' import { authenticatedProcedure } from '@/helpers/server/trpc' import { TRPCError } from '@trpc/server' -import { Workspace, workspaceSchema } from '@typebot.io/schemas' +import { workspaceSchema } from '@typebot.io/schemas' import { z } from 'zod' +import { isReadWorkspaceFobidden } from '../helpers/isReadWorkspaceFobidden' export const getWorkspace = authenticatedProcedure .meta({ @@ -25,11 +26,12 @@ export const getWorkspace = authenticatedProcedure }) ) .query(async ({ input: { workspaceId }, ctx: { user } }) => { - const workspace = (await prisma.workspace.findFirst({ - where: { members: { some: { userId: user.id } }, id: workspaceId }, - })) as Workspace | null + const workspace = await prisma.workspace.findFirst({ + where: { id: workspaceId }, + include: { members: true }, + }) - if (!workspace) + if (!workspace || (await isReadWorkspaceFobidden(workspace, user))) throw new TRPCError({ code: 'NOT_FOUND', message: 'No workspaces found' }) return { diff --git a/apps/builder/src/features/workspace/api/listInvitationsInWorkspace.ts b/apps/builder/src/features/workspace/api/listInvitationsInWorkspace.ts index 77d02e260..cdfe08b65 100644 --- a/apps/builder/src/features/workspace/api/listInvitationsInWorkspace.ts +++ b/apps/builder/src/features/workspace/api/listInvitationsInWorkspace.ts @@ -1,11 +1,9 @@ import prisma from '@/lib/prisma' import { authenticatedProcedure } from '@/helpers/server/trpc' import { TRPCError } from '@trpc/server' -import { - WorkspaceInvitation, - workspaceInvitationSchema, -} from '@typebot.io/schemas' +import { workspaceInvitationSchema } from '@typebot.io/schemas' import { z } from 'zod' +import { isReadWorkspaceFobidden } from '../helpers/isReadWorkspaceFobidden' export const listInvitationsInWorkspace = authenticatedProcedure .meta({ @@ -28,19 +26,13 @@ export const listInvitationsInWorkspace = authenticatedProcedure }) ) .query(async ({ input: { workspaceId }, ctx: { user } }) => { - const invitations = (await prisma.workspaceInvitation.findMany({ - where: { - workspaceId, - workspace: { members: { some: { userId: user.id } } }, - }, - select: { createdAt: true, email: true, type: true }, - })) as WorkspaceInvitation[] + const workspace = await prisma.workspace.findFirst({ + where: { id: workspaceId }, + include: { members: true, invitations: true }, + }) - if (!invitations) - throw new TRPCError({ - code: 'NOT_FOUND', - message: 'No invitations found', - }) + if (!workspace || (await isReadWorkspaceFobidden(workspace, user))) + throw new TRPCError({ code: 'NOT_FOUND', message: 'No workspaces found' }) - return { invitations } + return { invitations: workspace.invitations } }) diff --git a/apps/builder/src/features/workspace/api/listMembersInWorkspace.ts b/apps/builder/src/features/workspace/api/listMembersInWorkspace.ts index a104d727b..a8758330d 100644 --- a/apps/builder/src/features/workspace/api/listMembersInWorkspace.ts +++ b/apps/builder/src/features/workspace/api/listMembersInWorkspace.ts @@ -1,8 +1,9 @@ import prisma from '@/lib/prisma' import { authenticatedProcedure } from '@/helpers/server/trpc' import { TRPCError } from '@trpc/server' -import { WorkspaceMember, workspaceMemberSchema } from '@typebot.io/schemas' +import { workspaceMemberSchema } from '@typebot.io/schemas' import { z } from 'zod' +import { isReadWorkspaceFobidden } from '../helpers/isReadWorkspaceFobidden' export const listMembersInWorkspace = authenticatedProcedure .meta({ @@ -25,13 +26,25 @@ export const listMembersInWorkspace = authenticatedProcedure }) ) .query(async ({ input: { workspaceId }, ctx: { user } }) => { - const members = (await prisma.memberInWorkspace.findMany({ - where: { userId: user.id, workspaceId }, - include: { user: { select: { name: true, email: true, image: true } } }, - })) as WorkspaceMember[] + const workspace = await prisma.workspace.findFirst({ + where: { id: workspaceId }, + include: { + members: { + include: { + user: true, + }, + }, + }, + }) - if (!members) - throw new TRPCError({ code: 'NOT_FOUND', message: 'No members found' }) + if (!workspace || (await isReadWorkspaceFobidden(workspace, user))) + throw new TRPCError({ code: 'NOT_FOUND', message: 'No workspaces found' }) - return { members } + return { + members: workspace.members.map((member) => ({ + role: member.role, + user: member.user, + workspaceId, + })), + } }) diff --git a/apps/builder/src/features/workspace/api/listWorkspaces.ts b/apps/builder/src/features/workspace/api/listWorkspaces.ts index e2e3421c2..213e28504 100644 --- a/apps/builder/src/features/workspace/api/listWorkspaces.ts +++ b/apps/builder/src/features/workspace/api/listWorkspaces.ts @@ -1,7 +1,7 @@ import prisma from '@/lib/prisma' import { authenticatedProcedure } from '@/helpers/server/trpc' import { TRPCError } from '@trpc/server' -import { Workspace, workspaceSchema } from '@typebot.io/schemas' +import { workspaceSchema } from '@typebot.io/schemas' import { z } from 'zod' export const listWorkspaces = authenticatedProcedure @@ -23,10 +23,10 @@ export const listWorkspaces = authenticatedProcedure }) ) .query(async ({ ctx: { user } }) => { - const workspaces = (await prisma.workspace.findMany({ + const workspaces = await prisma.workspace.findMany({ where: { members: { some: { userId: user.id } } }, select: { name: true, id: true, icon: true, plan: true }, - })) as Pick[] + }) if (!workspaces) throw new TRPCError({ code: 'NOT_FOUND', message: 'No workspaces found' }) diff --git a/apps/builder/src/features/workspace/api/updateWorkspace.ts b/apps/builder/src/features/workspace/api/updateWorkspace.ts index 5673196a3..7de949741 100644 --- a/apps/builder/src/features/workspace/api/updateWorkspace.ts +++ b/apps/builder/src/features/workspace/api/updateWorkspace.ts @@ -1,8 +1,9 @@ import prisma from '@/lib/prisma' import { authenticatedProcedure } from '@/helpers/server/trpc' import { TRPCError } from '@trpc/server' -import { Workspace, workspaceSchema } from '@typebot.io/schemas' +import { workspaceSchema } from '@typebot.io/schemas' import { z } from 'zod' +import { isAdminWriteWorkspaceForbidden } from '../helpers/isAdminWriteWorkspaceForbidden' export const updateWorkspace = authenticatedProcedure .meta({ @@ -32,13 +33,20 @@ export const updateWorkspace = authenticatedProcedure data: updates, }) - const workspace = (await prisma.workspace.findFirst({ + const workspace = await prisma.workspace.findFirst({ where: { members: { some: { userId: user.id } }, id: workspaceId }, - })) as Workspace | null + include: { members: true }, + }) if (!workspace) throw new TRPCError({ code: 'NOT_FOUND', message: 'Workspace not found' }) + if (await isAdminWriteWorkspaceForbidden(workspace, user)) + throw new TRPCError({ + code: 'FORBIDDEN', + message: 'You are not allowed to update this workspace', + }) + return { workspace, } diff --git a/apps/builder/src/features/workspace/helpers/isAdminWriteWorkspaceForbidden.ts b/apps/builder/src/features/workspace/helpers/isAdminWriteWorkspaceForbidden.ts new file mode 100644 index 000000000..2203020a6 --- /dev/null +++ b/apps/builder/src/features/workspace/helpers/isAdminWriteWorkspaceForbidden.ts @@ -0,0 +1,13 @@ +import { MemberInWorkspace, User } from '@typebot.io/prisma' + +export const isAdminWriteWorkspaceForbidden = async ( + workspace: { + members: MemberInWorkspace[] + }, + user: Pick +) => { + const userRole = workspace.members.find( + (member) => member.userId === user.id + )?.role + return !userRole || userRole !== 'ADMIN' +} diff --git a/apps/builder/src/features/workspace/helpers/isReadWorkspaceFobidden.ts b/apps/builder/src/features/workspace/helpers/isReadWorkspaceFobidden.ts new file mode 100644 index 000000000..735cc5861 --- /dev/null +++ b/apps/builder/src/features/workspace/helpers/isReadWorkspaceFobidden.ts @@ -0,0 +1,15 @@ +import { MemberInWorkspace, User } from '@typebot.io/prisma' + +export const isReadWorkspaceFobidden = async ( + workspace: { + members: MemberInWorkspace[] + }, + user: Pick +) => { + if ( + process.env.ADMIN_EMAIL === user.email || + workspace.members.find((member) => member.userId === user.id) + ) + return false + return true +} diff --git a/apps/builder/src/features/workspace/helpers/isWriteWorkspaceForbidden copy.ts b/apps/builder/src/features/workspace/helpers/isWriteWorkspaceForbidden copy.ts new file mode 100644 index 000000000..b279ed6c9 --- /dev/null +++ b/apps/builder/src/features/workspace/helpers/isWriteWorkspaceForbidden copy.ts @@ -0,0 +1,13 @@ +import { MemberInWorkspace, User } from '@typebot.io/prisma' + +export const isWriteWorkspaceForbidden = async ( + workspace: { + members: MemberInWorkspace[] + }, + user: Pick +) => { + const userRole = workspace.members.find( + (member) => member.userId === user.id + )?.role + return !userRole || userRole === 'GUEST' +} diff --git a/package.json b/package.json index 06e4c6297..6930ad71f 100644 --- a/package.json +++ b/package.json @@ -23,12 +23,12 @@ "cz-emoji": "1.3.2-canary.2", "husky": "^8.0.3", "prettier": "2.8.8", - "turbo": "1.10.7" + "turbo": "1.10.12" }, "config": { "commitizen": { "path": "node_modules/cz-emoji" } }, - "packageManager": "pnpm@8.6.7" + "packageManager": "pnpm@8.6.12" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1ba9a0d1..6b79e8cc2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,8 +21,8 @@ importers: specifier: 2.8.8 version: 2.8.8 turbo: - specifier: 1.10.7 - version: 1.10.7 + specifier: 1.10.12 + version: 1.10.12 apps/builder: dependencies: @@ -23117,65 +23117,65 @@ packages: fsevents: 2.3.2 dev: true - /turbo-darwin-64@1.10.7: - resolution: {integrity: sha512-N2MNuhwrl6g7vGuz4y3fFG2aR1oCs0UZ5HKl8KSTn/VC2y2YIuLGedQ3OVbo0TfEvygAlF3QGAAKKtOCmGPNKA==} + /turbo-darwin-64@1.10.12: + resolution: {integrity: sha512-vmDfGVPl5/aFenAbOj3eOx3ePNcWVUyZwYr7taRl0ZBbmv2TzjRiFotO4vrKCiTVnbqjQqAFQWY2ugbqCI1kOQ==} cpu: [x64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-darwin-arm64@1.10.7: - resolution: {integrity: sha512-WbJkvjU+6qkngp7K4EsswOriO3xrNQag7YEGRtfLoDdMTk4O4QTeU6sfg2dKfDsBpTidTvEDwgIYJhYVGzrz9Q==} + /turbo-darwin-arm64@1.10.12: + resolution: {integrity: sha512-3JliEESLNX2s7g54SOBqqkqJ7UhcOGkS0ywMr5SNuvF6kWVTbuUq7uBU/sVbGq8RwvK1ONlhPvJne5MUqBCTCQ==} cpu: [arm64] os: [darwin] requiresBuild: true dev: true optional: true - /turbo-linux-64@1.10.7: - resolution: {integrity: sha512-x1CF2CDP1pDz/J8/B2T0hnmmOQI2+y11JGIzNP0KtwxDM7rmeg3DDTtDM/9PwGqfPotN9iVGgMiMvBuMFbsLhg==} + /turbo-linux-64@1.10.12: + resolution: {integrity: sha512-siYhgeX0DidIfHSgCR95b8xPee9enKSOjCzx7EjTLmPqPaCiVebRYvbOIYdQWRqiaKh9yfhUtFmtMOMScUf1gg==} cpu: [x64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-linux-arm64@1.10.7: - resolution: {integrity: sha512-JtnBmaBSYbs7peJPkXzXxsRGSGBmBEIb6/kC8RRmyvPAMyqF8wIex0pttsI+9plghREiGPtRWv/lfQEPRlXnNQ==} + /turbo-linux-arm64@1.10.12: + resolution: {integrity: sha512-K/ZhvD9l4SslclaMkTiIrnfcACgos79YcAo4kwc8bnMQaKuUeRpM15sxLpZp3xDjDg8EY93vsKyjaOhdFG2UbA==} cpu: [arm64] os: [linux] requiresBuild: true dev: true optional: true - /turbo-windows-64@1.10.7: - resolution: {integrity: sha512-7A/4CByoHdolWS8dg3DPm99owfu1aY/W0V0+KxFd0o2JQMTQtoBgIMSvZesXaWM57z3OLsietFivDLQPuzE75w==} + /turbo-windows-64@1.10.12: + resolution: {integrity: sha512-7FSgSwvktWDNOqV65l9AbZwcoueAILeE4L7JvjauNASAjjbuzXGCEq5uN8AQU3U5BOFj4TdXrVmO2dX+lLu8Zg==} cpu: [x64] os: [win32] requiresBuild: true dev: true optional: true - /turbo-windows-arm64@1.10.7: - resolution: {integrity: sha512-D36K/3b6+hqm9IBAymnuVgyePktwQ+F0lSXr2B9JfAdFPBktSqGmp50JNC7pahxhnuCLj0Vdpe9RqfnJw5zATA==} + /turbo-windows-arm64@1.10.12: + resolution: {integrity: sha512-gCNXF52dwom1HLY9ry/cneBPOKTBHhzpqhMylcyvJP0vp9zeMQQkt6yjYv+6QdnmELC92CtKNp2FsNZo+z0pyw==} cpu: [arm64] os: [win32] requiresBuild: true dev: true optional: true - /turbo@1.10.7: - resolution: {integrity: sha512-xm0MPM28TWx1e6TNC3wokfE5eaDqlfi0G24kmeHupDUZt5Wd0OzHFENEHMPqEaNKJ0I+AMObL6nbSZonZBV2HA==} + /turbo@1.10.12: + resolution: {integrity: sha512-WM3+jTfQWnB9W208pmP4oeehZcC6JQNlydb/ZHMRrhmQa+htGhWLCzd6Q9rLe0MwZLPpSPFV2/bN5egCLyoKjQ==} hasBin: true requiresBuild: true optionalDependencies: - turbo-darwin-64: 1.10.7 - turbo-darwin-arm64: 1.10.7 - turbo-linux-64: 1.10.7 - turbo-linux-arm64: 1.10.7 - turbo-windows-64: 1.10.7 - turbo-windows-arm64: 1.10.7 + turbo-darwin-64: 1.10.12 + turbo-darwin-arm64: 1.10.12 + turbo-linux-64: 1.10.12 + turbo-linux-arm64: 1.10.12 + turbo-windows-64: 1.10.12 + turbo-windows-arm64: 1.10.12 dev: true /type-check@0.3.2: