@@ -32,7 +32,7 @@ export const ImageUploadContent = ({
|
|||||||
onClose,
|
onClose,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const [currentTab, setCurrentTab] = useState<Tabs>(
|
const [currentTab, setCurrentTab] = useState<Tabs>(
|
||||||
isEmojiEnabled ? 'emoji' : 'upload'
|
isEmojiEnabled ? 'emoji' : 'link'
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleSubmit = (url: string) => {
|
const handleSubmit = (url: string) => {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { authenticatedProcedure } from '@/helpers/server/trpc'
|
|||||||
import { TRPCError } from '@trpc/server'
|
import { TRPCError } from '@trpc/server'
|
||||||
import { Typebot, Webhook } from '@typebot.io/schemas'
|
import { Typebot, Webhook } from '@typebot.io/schemas'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import { getLinkedTypebots } from '@/features/blocks/logic/typebotLink/helpers/getLinkedTypebots'
|
import { fetchLinkedTypebots } from '@/features/blocks/logic/typebotLink/helpers/fetchLinkedTypebots'
|
||||||
import { parseResultExample } from '../helpers/parseResultExample'
|
import { parseResultExample } from '../helpers/parseResultExample'
|
||||||
|
|
||||||
export const getResultExample = authenticatedProcedure
|
export const getResultExample = authenticatedProcedure
|
||||||
@@ -63,7 +63,7 @@ export const getResultExample = authenticatedProcedure
|
|||||||
if (!block)
|
if (!block)
|
||||||
throw new TRPCError({ code: 'NOT_FOUND', message: 'Block not found' })
|
throw new TRPCError({ code: 'NOT_FOUND', message: 'Block not found' })
|
||||||
|
|
||||||
const linkedTypebots = await getLinkedTypebots(typebot, user)
|
const linkedTypebots = await fetchLinkedTypebots(typebot, user)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
resultExample: await parseResultExample(
|
resultExample: await parseResultExample(
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import prisma from '@/lib/prisma'
|
||||||
|
import { authenticatedProcedure } from '@/helpers/server/trpc'
|
||||||
|
import { TRPCError } from '@trpc/server'
|
||||||
|
import { Typebot, typebotSchema } from '@typebot.io/schemas'
|
||||||
|
import { z } from 'zod'
|
||||||
|
import { getUserRoleInWorkspace } from '@/features/workspace/helpers/getUserRoleInWorkspace'
|
||||||
|
|
||||||
|
export const getLinkedTypebots = authenticatedProcedure
|
||||||
|
.meta({
|
||||||
|
openapi: {
|
||||||
|
method: 'GET',
|
||||||
|
path: '/linkedTypebots',
|
||||||
|
protect: true,
|
||||||
|
summary: 'Get linked typebots',
|
||||||
|
tags: ['Typebot'],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.input(
|
||||||
|
z.object({
|
||||||
|
workspaceId: z.string(),
|
||||||
|
typebotIds: z.string().describe('Comma separated list of typebot ids'),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.output(
|
||||||
|
z.object({
|
||||||
|
typebots: z.array(
|
||||||
|
typebotSchema.pick({
|
||||||
|
id: true,
|
||||||
|
groups: true,
|
||||||
|
variables: true,
|
||||||
|
name: true,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.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({
|
||||||
|
where: {
|
||||||
|
isArchived: { not: true },
|
||||||
|
id: { in: typebotIdsArray },
|
||||||
|
workspaceId,
|
||||||
|
},
|
||||||
|
orderBy: { createdAt: 'desc' },
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
groups: true,
|
||||||
|
variables: true,
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
})) as Pick<Typebot, 'id' | 'groups' | 'variables' | 'name'>[]
|
||||||
|
|
||||||
|
if (!typebots)
|
||||||
|
throw new TRPCError({ code: 'NOT_FOUND', message: 'No typebots found' })
|
||||||
|
|
||||||
|
return {
|
||||||
|
typebots,
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,27 +1,22 @@
|
|||||||
import prisma from '@/lib/prisma'
|
import prisma from '@/lib/prisma'
|
||||||
import { canReadTypebots } from '@/helpers/databaseRules'
|
import { canReadTypebots } from '@/helpers/databaseRules'
|
||||||
import { User } from '@typebot.io/prisma'
|
import { User } from '@typebot.io/prisma'
|
||||||
import {
|
import { LogicBlockType, PublicTypebot, Typebot } from '@typebot.io/schemas'
|
||||||
LogicBlockType,
|
|
||||||
PublicTypebot,
|
|
||||||
Typebot,
|
|
||||||
TypebotLinkBlock,
|
|
||||||
} from '@typebot.io/schemas'
|
|
||||||
import { isDefined } from '@typebot.io/lib'
|
|
||||||
|
|
||||||
export const getLinkedTypebots = async (
|
export const fetchLinkedTypebots = async (
|
||||||
typebot: Pick<PublicTypebot, 'groups'>,
|
typebot: Pick<PublicTypebot, 'groups'>,
|
||||||
user?: User
|
user?: User
|
||||||
): Promise<(Typebot | PublicTypebot)[]> => {
|
): Promise<(Typebot | PublicTypebot)[]> => {
|
||||||
const linkedTypebotIds = (
|
const linkedTypebotIds = typebot.groups
|
||||||
typebot.groups
|
.flatMap((group) => group.blocks)
|
||||||
.flatMap((g) => g.blocks)
|
.reduce<string[]>((typebotIds, block) => {
|
||||||
.filter(
|
if (block.type !== LogicBlockType.TYPEBOT_LINK) return typebotIds
|
||||||
(s) =>
|
const typebotId = block.options.typebotId
|
||||||
s.type === LogicBlockType.TYPEBOT_LINK &&
|
if (!typebotId) return typebotIds
|
||||||
isDefined(s.options.typebotId)
|
return typebotIds.includes(typebotId)
|
||||||
) as TypebotLinkBlock[]
|
? typebotIds
|
||||||
).map((s) => s.options.typebotId as string)
|
: [...typebotIds, typebotId]
|
||||||
|
}, [])
|
||||||
if (linkedTypebotIds.length === 0) return []
|
if (linkedTypebotIds.length === 0) return []
|
||||||
const typebots = (await ('typebotId' in typebot
|
const typebots = (await ('typebotId' in typebot
|
||||||
? prisma.publicTypebot.findMany({
|
? prisma.publicTypebot.findMany({
|
||||||
@@ -36,6 +31,6 @@ export const getLinkedTypebots = async (
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
: { id: { in: linkedTypebotIds } },
|
: { id: { in: linkedTypebotIds } },
|
||||||
}))) as unknown as (Typebot | PublicTypebot)[]
|
}))) as (Typebot | PublicTypebot)[]
|
||||||
return typebots
|
return typebots
|
||||||
}
|
}
|
||||||
@@ -57,7 +57,7 @@ test('should be configurable', async ({ page }) => {
|
|||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
await page.getByTestId('selected-item-label').first().click({ force: true })
|
await page.getByTestId('selected-item-label').first().click({ force: true })
|
||||||
await page.click('button >> text=Current typebot')
|
await page.click('button >> text=Current typebot')
|
||||||
await page.getByPlaceholder('Select a block').click()
|
await page.getByRole('textbox').nth(1).click()
|
||||||
await page.click('button >> text=Hello')
|
await page.click('button >> text=Hello')
|
||||||
|
|
||||||
await page.click('text=Preview')
|
await page.click('text=Preview')
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import { dequal } from 'dequal'
|
|||||||
import { useToast } from '@/hooks/useToast'
|
import { useToast } from '@/hooks/useToast'
|
||||||
import { useTypebotQuery } from '@/hooks/useTypebotQuery'
|
import { useTypebotQuery } from '@/hooks/useTypebotQuery'
|
||||||
import { useUndo } from '../hooks/useUndo'
|
import { useUndo } from '../hooks/useUndo'
|
||||||
import { useLinkedTypebots } from '@/hooks/useLinkedTypebots'
|
|
||||||
import { updateTypebotQuery } from '../queries/updateTypebotQuery'
|
import { updateTypebotQuery } from '../queries/updateTypebotQuery'
|
||||||
import { updateWebhookQuery } from '@/features/blocks/integrations/webhook/queries/updateWebhookQuery'
|
import { updateWebhookQuery } from '@/features/blocks/integrations/webhook/queries/updateWebhookQuery'
|
||||||
import { useAutoSave } from '@/hooks/useAutoSave'
|
import { useAutoSave } from '@/hooks/useAutoSave'
|
||||||
@@ -39,6 +38,7 @@ import { areTypebotsEqual } from '@/features/publish/helpers/areTypebotsEqual'
|
|||||||
import { isPublished as isPublishedHelper } from '@/features/publish/helpers/isPublished'
|
import { isPublished as isPublishedHelper } from '@/features/publish/helpers/isPublished'
|
||||||
import { convertTypebotToPublicTypebot } from '@/features/publish/helpers/convertTypebotToPublicTypebot'
|
import { convertTypebotToPublicTypebot } from '@/features/publish/helpers/convertTypebotToPublicTypebot'
|
||||||
import { convertPublicTypebotToTypebot } from '@/features/publish/helpers/convertPublicTypebotToTypebot'
|
import { convertPublicTypebotToTypebot } from '@/features/publish/helpers/convertPublicTypebotToTypebot'
|
||||||
|
import { trpc } from '@/lib/trpc'
|
||||||
|
|
||||||
const autoSaveTimeout = 10000
|
const autoSaveTimeout = 10000
|
||||||
|
|
||||||
@@ -65,7 +65,7 @@ const typebotContext = createContext<
|
|||||||
{
|
{
|
||||||
typebot?: Typebot
|
typebot?: Typebot
|
||||||
publishedTypebot?: PublicTypebot
|
publishedTypebot?: PublicTypebot
|
||||||
linkedTypebots?: Typebot[]
|
linkedTypebots?: Pick<Typebot, 'id' | 'groups' | 'variables' | 'name'>[]
|
||||||
webhooks: Webhook[]
|
webhooks: Webhook[]
|
||||||
isReadOnly?: boolean
|
isReadOnly?: boolean
|
||||||
isPublished: boolean
|
isPublished: boolean
|
||||||
@@ -132,16 +132,21 @@ export const TypebotProvider = ({
|
|||||||
[]
|
[]
|
||||||
) ?? []
|
) ?? []
|
||||||
|
|
||||||
const { typebots: linkedTypebots } = useLinkedTypebots({
|
const { data: linkedTypebotsData } = trpc.getLinkedTypebots.useQuery(
|
||||||
workspaceId: localTypebot?.workspaceId ?? undefined,
|
{
|
||||||
typebotId,
|
workspaceId: localTypebot?.workspaceId as string,
|
||||||
typebotIds: linkedTypebotIds,
|
typebotIds: linkedTypebotIds.join(','),
|
||||||
onError: (error) =>
|
},
|
||||||
showToast({
|
{
|
||||||
title: 'Error while fetching linkedTypebots',
|
enabled:
|
||||||
description: error.message,
|
isDefined(localTypebot?.workspaceId) && linkedTypebotIds.length > 0,
|
||||||
}),
|
onError: (error) =>
|
||||||
})
|
showToast({
|
||||||
|
title: 'Error while fetching linkedTypebots',
|
||||||
|
description: error.message,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!typebot && isDefined(localTypebot)) setLocalTypebot(undefined)
|
if (!typebot && isDefined(localTypebot)) setLocalTypebot(undefined)
|
||||||
@@ -385,7 +390,7 @@ export const TypebotProvider = ({
|
|||||||
value={{
|
value={{
|
||||||
typebot: localTypebot,
|
typebot: localTypebot,
|
||||||
publishedTypebot,
|
publishedTypebot,
|
||||||
linkedTypebots,
|
linkedTypebots: linkedTypebotsData?.typebots ?? [],
|
||||||
webhooks: webhooks ?? [],
|
webhooks: webhooks ?? [],
|
||||||
isReadOnly,
|
isReadOnly,
|
||||||
isSavingLoading,
|
isSavingLoading,
|
||||||
|
|||||||
@@ -223,6 +223,7 @@ test.describe.parallel('Theme page', () => {
|
|||||||
})
|
})
|
||||||
await page.goto(`/typebots/${typebotId}/theme`)
|
await page.goto(`/typebots/${typebotId}/theme`)
|
||||||
await expect(page.locator('button >> text="Go"')).toBeVisible()
|
await expect(page.locator('button >> text="Go"')).toBeVisible()
|
||||||
|
await page.getByRole('button', { name: 'Templates New!' }).click()
|
||||||
await page.getByRole('button', { name: 'Save current theme' }).click()
|
await page.getByRole('button', { name: 'Save current theme' }).click()
|
||||||
await page.getByPlaceholder('My template').fill('My awesome theme')
|
await page.getByPlaceholder('My template').fill('My awesome theme')
|
||||||
await page.getByRole('button', { name: 'Save' }).click()
|
await page.getByRole('button', { name: 'Save' }).click()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { WorkspaceRole } from '@typebot.io/prisma'
|
|||||||
import { PublicTypebot, Typebot, typebotSchema } from '@typebot.io/schemas'
|
import { PublicTypebot, Typebot, typebotSchema } from '@typebot.io/schemas'
|
||||||
import { omit } from '@typebot.io/lib'
|
import { omit } from '@typebot.io/lib'
|
||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
import { getUserRoleInWorkspace } from '@/features/workspace/helpers/getUserRoleInWorkspace'
|
||||||
|
|
||||||
export const listTypebots = authenticatedProcedure
|
export const listTypebots = authenticatedProcedure
|
||||||
.meta({
|
.meta({
|
||||||
@@ -31,33 +32,27 @@ export const listTypebots = authenticatedProcedure
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
.query(async ({ input: { workspaceId, folderId }, ctx: { user } }) => {
|
.query(async ({ input: { workspaceId, folderId }, ctx: { user } }) => {
|
||||||
|
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({
|
const typebots = (await prisma.typebot.findMany({
|
||||||
where: {
|
where: {
|
||||||
OR: [
|
isArchived: { not: true },
|
||||||
{
|
folderId:
|
||||||
isArchived: { not: true },
|
userRole === WorkspaceRole.GUEST
|
||||||
folderId: folderId === 'root' ? null : folderId,
|
? undefined
|
||||||
workspace: {
|
: folderId === 'root'
|
||||||
id: workspaceId,
|
? null
|
||||||
members: {
|
: folderId,
|
||||||
some: {
|
workspaceId,
|
||||||
userId: user.id,
|
collaborators:
|
||||||
role: { not: WorkspaceRole.GUEST },
|
userRole === WorkspaceRole.GUEST
|
||||||
},
|
? { some: { userId: user.id } }
|
||||||
},
|
: undefined,
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
isArchived: { not: true },
|
|
||||||
workspace: {
|
|
||||||
id: workspaceId,
|
|
||||||
members: {
|
|
||||||
some: { userId: user.id, role: WorkspaceRole.GUEST },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
collaborators: { some: { userId: user.id } },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
orderBy: { createdAt: 'desc' },
|
orderBy: { createdAt: 'desc' },
|
||||||
select: {
|
select: {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { billingRouter } from '@/features/billing/api/router'
|
import { billingRouter } from '@/features/billing/api/router'
|
||||||
import { webhookRouter } from '@/features/blocks/integrations/webhook/api/router'
|
import { webhookRouter } from '@/features/blocks/integrations/webhook/api/router'
|
||||||
|
import { getLinkedTypebots } from '@/features/blocks/logic/typebotLink/api/getLinkedTypebots'
|
||||||
import { credentialsRouter } from '@/features/credentials/api/router'
|
import { credentialsRouter } from '@/features/credentials/api/router'
|
||||||
import { getAppVersionProcedure } from '@/features/dashboard/api/getAppVersionProcedure'
|
import { getAppVersionProcedure } from '@/features/dashboard/api/getAppVersionProcedure'
|
||||||
import { resultsRouter } from '@/features/results/api/router'
|
import { resultsRouter } from '@/features/results/api/router'
|
||||||
@@ -12,6 +13,7 @@ import { router } from '../../trpc'
|
|||||||
export const trpcRouter = router({
|
export const trpcRouter = router({
|
||||||
getAppVersionProcedure,
|
getAppVersionProcedure,
|
||||||
processTelemetryEvent,
|
processTelemetryEvent,
|
||||||
|
getLinkedTypebots,
|
||||||
workspace: workspaceRouter,
|
workspace: workspaceRouter,
|
||||||
typebot: typebotRouter,
|
typebot: typebotRouter,
|
||||||
webhook: webhookRouter,
|
webhook: webhookRouter,
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import { fetcher } from '@/helpers/fetcher'
|
|
||||||
import { Typebot } from '@typebot.io/schemas'
|
|
||||||
import { stringify } from 'qs'
|
|
||||||
import useSWR from 'swr'
|
|
||||||
|
|
||||||
export const useLinkedTypebots = ({
|
|
||||||
workspaceId,
|
|
||||||
typebotId,
|
|
||||||
typebotIds,
|
|
||||||
onError,
|
|
||||||
}: {
|
|
||||||
workspaceId?: string
|
|
||||||
typebotId?: string
|
|
||||||
typebotIds: string[]
|
|
||||||
onError: (error: Error) => void
|
|
||||||
}) => {
|
|
||||||
const params = stringify({ typebotIds, workspaceId }, { indices: false })
|
|
||||||
const { data, error, mutate } = useSWR<
|
|
||||||
{
|
|
||||||
typebots: Typebot[]
|
|
||||||
},
|
|
||||||
Error
|
|
||||||
>(
|
|
||||||
workspaceId && typebotIds.length > 0
|
|
||||||
? typebotIds.every((id) => typebotId === id)
|
|
||||||
? undefined
|
|
||||||
: `/api/typebots?${params}`
|
|
||||||
: null,
|
|
||||||
fetcher
|
|
||||||
)
|
|
||||||
if (error) onError(error)
|
|
||||||
return {
|
|
||||||
typebots: data?.typebots,
|
|
||||||
isLoading: !error && !data,
|
|
||||||
mutate,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
|
||||||
import {
|
|
||||||
badRequest,
|
|
||||||
forbidden,
|
|
||||||
methodNotAllowed,
|
|
||||||
notAuthenticated,
|
|
||||||
} from '@typebot.io/lib/api'
|
|
||||||
import Stripe from 'stripe'
|
|
||||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
|
||||||
import prisma from '@/lib/prisma'
|
|
||||||
import { WorkspaceRole } from '@typebot.io/prisma'
|
|
||||||
|
|
||||||
// TO-DO: Delete
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|
||||||
const user = await getAuthenticatedUser(req)
|
|
||||||
if (!user) return notAuthenticated(res)
|
|
||||||
if (req.method === 'GET') {
|
|
||||||
const stripeId = req.query.stripeId as string | undefined
|
|
||||||
if (!stripeId) return badRequest(res)
|
|
||||||
if (!process.env.STRIPE_SECRET_KEY)
|
|
||||||
throw Error('STRIPE_SECRET_KEY var is missing')
|
|
||||||
const workspace = await prisma.workspace.findFirst({
|
|
||||||
where: {
|
|
||||||
stripeId,
|
|
||||||
members: { some: { userId: user.id, role: WorkspaceRole.ADMIN } },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (!workspace?.stripeId) return forbidden(res)
|
|
||||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
|
||||||
apiVersion: '2022-11-15',
|
|
||||||
})
|
|
||||||
const session = await stripe.billingPortal.sessions.create({
|
|
||||||
customer: workspace.stripeId,
|
|
||||||
return_url: req.headers.referer,
|
|
||||||
})
|
|
||||||
res.redirect(session.url)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return methodNotAllowed(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default handler
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
|
||||||
import {
|
|
||||||
badRequest,
|
|
||||||
forbidden,
|
|
||||||
methodNotAllowed,
|
|
||||||
notAuthenticated,
|
|
||||||
} from '@typebot.io/lib/api'
|
|
||||||
import Stripe from 'stripe'
|
|
||||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
|
||||||
import prisma from '@/lib/prisma'
|
|
||||||
import { WorkspaceRole } from '@typebot.io/prisma'
|
|
||||||
|
|
||||||
// TODO: Delete
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|
||||||
const user = await getAuthenticatedUser(req)
|
|
||||||
if (!user) return notAuthenticated(res)
|
|
||||||
if (req.method === 'GET') {
|
|
||||||
const stripeId = req.query.stripeId as string | undefined
|
|
||||||
if (!stripeId) return badRequest(res)
|
|
||||||
if (!process.env.STRIPE_SECRET_KEY)
|
|
||||||
throw Error('STRIPE_SECRET_KEY var is missing')
|
|
||||||
const workspace = await prisma.workspace.findFirst({
|
|
||||||
where: {
|
|
||||||
stripeId,
|
|
||||||
members: { some: { userId: user.id, role: WorkspaceRole.ADMIN } },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (!workspace?.stripeId) return forbidden(res)
|
|
||||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
|
||||||
apiVersion: '2022-11-15',
|
|
||||||
})
|
|
||||||
const invoices = await stripe.invoices.list({
|
|
||||||
customer: workspace.stripeId,
|
|
||||||
})
|
|
||||||
res.send({
|
|
||||||
invoices: invoices.data.map((i) => ({
|
|
||||||
id: i.number,
|
|
||||||
url: i.invoice_pdf,
|
|
||||||
amount: i.subtotal,
|
|
||||||
currency: i.currency,
|
|
||||||
date: i.status_transitions.paid_at,
|
|
||||||
})),
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return methodNotAllowed(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default handler
|
|
||||||
@@ -1,261 +0,0 @@
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
|
||||||
import { isDefined } from '@typebot.io/lib'
|
|
||||||
import {
|
|
||||||
badRequest,
|
|
||||||
forbidden,
|
|
||||||
methodNotAllowed,
|
|
||||||
notAuthenticated,
|
|
||||||
} from '@typebot.io/lib/api'
|
|
||||||
import Stripe from 'stripe'
|
|
||||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
|
||||||
import prisma from '@/lib/prisma'
|
|
||||||
import { Plan, WorkspaceRole } from '@typebot.io/prisma'
|
|
||||||
|
|
||||||
// TODO: Delete
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|
||||||
const user = await getAuthenticatedUser(req)
|
|
||||||
if (!user) return notAuthenticated(res)
|
|
||||||
if (req.method === 'GET')
|
|
||||||
return res.send(await getSubscriptionDetails(req, res)(user.id))
|
|
||||||
if (req.method === 'POST') {
|
|
||||||
const session = await createCheckoutSession(req)
|
|
||||||
return res.send({ sessionId: session.id })
|
|
||||||
}
|
|
||||||
if (req.method === 'PUT') {
|
|
||||||
await updateSubscription(req)
|
|
||||||
return res.send({ message: 'success' })
|
|
||||||
}
|
|
||||||
if (req.method === 'DELETE') {
|
|
||||||
await cancelSubscription(req, res)(user.id)
|
|
||||||
return res.send({ message: 'success' })
|
|
||||||
}
|
|
||||||
return methodNotAllowed(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
const getSubscriptionDetails =
|
|
||||||
(req: NextApiRequest, res: NextApiResponse) => async (userId: string) => {
|
|
||||||
const stripeId = req.query.stripeId as string | undefined
|
|
||||||
if (!stripeId) return badRequest(res)
|
|
||||||
if (!process.env.STRIPE_SECRET_KEY)
|
|
||||||
throw Error('STRIPE_SECRET_KEY var is missing')
|
|
||||||
const workspace = await prisma.workspace.findFirst({
|
|
||||||
where: {
|
|
||||||
stripeId,
|
|
||||||
members: { some: { userId, role: WorkspaceRole.ADMIN } },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (!workspace?.stripeId) return forbidden(res)
|
|
||||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
|
||||||
apiVersion: '2022-11-15',
|
|
||||||
})
|
|
||||||
const subscriptions = await stripe.subscriptions.list({
|
|
||||||
customer: workspace.stripeId,
|
|
||||||
limit: 1,
|
|
||||||
})
|
|
||||||
return {
|
|
||||||
additionalChatsIndex:
|
|
||||||
subscriptions.data[0]?.items.data.find(
|
|
||||||
(item) =>
|
|
||||||
item.price.id === process.env.STRIPE_ADDITIONAL_CHATS_PRICE_ID
|
|
||||||
)?.quantity ?? 0,
|
|
||||||
additionalStorageIndex:
|
|
||||||
subscriptions.data[0]?.items.data.find(
|
|
||||||
(item) =>
|
|
||||||
item.price.id === process.env.STRIPE_ADDITIONAL_STORAGE_PRICE_ID
|
|
||||||
)?.quantity ?? 0,
|
|
||||||
currency: subscriptions.data[0]?.currency,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const createCheckoutSession = (req: NextApiRequest) => {
|
|
||||||
if (!process.env.STRIPE_SECRET_KEY)
|
|
||||||
throw Error('STRIPE_SECRET_KEY var is missing')
|
|
||||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
|
||||||
apiVersion: '2022-11-15',
|
|
||||||
})
|
|
||||||
const {
|
|
||||||
email,
|
|
||||||
currency,
|
|
||||||
plan,
|
|
||||||
workspaceId,
|
|
||||||
href,
|
|
||||||
additionalChats,
|
|
||||||
additionalStorage,
|
|
||||||
} = typeof req.body === 'string' ? JSON.parse(req.body) : req.body
|
|
||||||
|
|
||||||
return stripe.checkout.sessions.create({
|
|
||||||
success_url: `${href}?stripe=${plan}&success=true`,
|
|
||||||
cancel_url: `${href}?stripe=cancel`,
|
|
||||||
allow_promotion_codes: true,
|
|
||||||
customer_email: email,
|
|
||||||
mode: 'subscription',
|
|
||||||
metadata: { workspaceId, plan, additionalChats, additionalStorage },
|
|
||||||
currency,
|
|
||||||
automatic_tax: { enabled: true },
|
|
||||||
line_items: parseSubscriptionItems(
|
|
||||||
plan,
|
|
||||||
additionalChats,
|
|
||||||
additionalStorage
|
|
||||||
),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateSubscription = async (req: NextApiRequest) => {
|
|
||||||
const {
|
|
||||||
stripeId,
|
|
||||||
plan,
|
|
||||||
workspaceId,
|
|
||||||
additionalChats,
|
|
||||||
additionalStorage,
|
|
||||||
currency,
|
|
||||||
} = (typeof req.body === 'string' ? JSON.parse(req.body) : req.body) as {
|
|
||||||
stripeId: string
|
|
||||||
workspaceId: string
|
|
||||||
additionalChats: number
|
|
||||||
additionalStorage: number
|
|
||||||
plan: 'STARTER' | 'PRO'
|
|
||||||
currency: 'eur' | 'usd'
|
|
||||||
}
|
|
||||||
if (!process.env.STRIPE_SECRET_KEY)
|
|
||||||
throw Error('STRIPE_SECRET_KEY var is missing')
|
|
||||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
|
||||||
apiVersion: '2022-11-15',
|
|
||||||
})
|
|
||||||
const { data } = await stripe.subscriptions.list({
|
|
||||||
customer: stripeId,
|
|
||||||
})
|
|
||||||
const subscription = data[0] as Stripe.Subscription | undefined
|
|
||||||
const currentStarterPlanItemId = subscription?.items.data.find(
|
|
||||||
(item) => item.price.id === process.env.STRIPE_STARTER_PRICE_ID
|
|
||||||
)?.id
|
|
||||||
const currentProPlanItemId = subscription?.items.data.find(
|
|
||||||
(item) => item.price.id === process.env.STRIPE_PRO_PRICE_ID
|
|
||||||
)?.id
|
|
||||||
const currentAdditionalChatsItemId = subscription?.items.data.find(
|
|
||||||
(item) => item.price.id === process.env.STRIPE_ADDITIONAL_CHATS_PRICE_ID
|
|
||||||
)?.id
|
|
||||||
const currentAdditionalStorageItemId = subscription?.items.data.find(
|
|
||||||
(item) => item.price.id === process.env.STRIPE_ADDITIONAL_STORAGE_PRICE_ID
|
|
||||||
)?.id
|
|
||||||
const items = [
|
|
||||||
{
|
|
||||||
id: currentStarterPlanItemId ?? currentProPlanItemId,
|
|
||||||
price:
|
|
||||||
plan === Plan.STARTER
|
|
||||||
? process.env.STRIPE_STARTER_PRICE_ID
|
|
||||||
: process.env.STRIPE_PRO_PRICE_ID,
|
|
||||||
quantity: 1,
|
|
||||||
},
|
|
||||||
additionalChats === 0 && !currentAdditionalChatsItemId
|
|
||||||
? undefined
|
|
||||||
: {
|
|
||||||
id: currentAdditionalChatsItemId,
|
|
||||||
price: process.env.STRIPE_ADDITIONAL_CHATS_PRICE_ID,
|
|
||||||
quantity: additionalChats,
|
|
||||||
deleted: subscription ? additionalChats === 0 : undefined,
|
|
||||||
},
|
|
||||||
additionalStorage === 0 && !currentAdditionalStorageItemId
|
|
||||||
? undefined
|
|
||||||
: {
|
|
||||||
id: currentAdditionalStorageItemId,
|
|
||||||
price: process.env.STRIPE_ADDITIONAL_STORAGE_PRICE_ID,
|
|
||||||
quantity: additionalStorage,
|
|
||||||
deleted: subscription ? additionalStorage === 0 : undefined,
|
|
||||||
},
|
|
||||||
].filter(isDefined)
|
|
||||||
|
|
||||||
if (subscription) {
|
|
||||||
await stripe.subscriptions.update(subscription.id, {
|
|
||||||
items,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
await stripe.subscriptions.create({
|
|
||||||
customer: stripeId,
|
|
||||||
items,
|
|
||||||
currency,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
await prisma.workspace.update({
|
|
||||||
where: { id: workspaceId },
|
|
||||||
data: {
|
|
||||||
plan,
|
|
||||||
additionalChatsIndex: additionalChats,
|
|
||||||
additionalStorageIndex: additionalStorage,
|
|
||||||
chatsLimitFirstEmailSentAt: null,
|
|
||||||
chatsLimitSecondEmailSentAt: null,
|
|
||||||
storageLimitFirstEmailSentAt: null,
|
|
||||||
storageLimitSecondEmailSentAt: null,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancelSubscription =
|
|
||||||
(req: NextApiRequest, res: NextApiResponse) => async (userId: string) => {
|
|
||||||
const stripeId = req.query.stripeId as string | undefined
|
|
||||||
if (!stripeId) return badRequest(res)
|
|
||||||
if (!process.env.STRIPE_SECRET_KEY)
|
|
||||||
throw Error('STRIPE_SECRET_KEY var is missing')
|
|
||||||
const workspace = await prisma.workspace.findFirst({
|
|
||||||
where: {
|
|
||||||
stripeId,
|
|
||||||
members: { some: { userId, role: WorkspaceRole.ADMIN } },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (!workspace?.stripeId) return forbidden(res)
|
|
||||||
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, {
|
|
||||||
apiVersion: '2022-11-15',
|
|
||||||
})
|
|
||||||
const existingSubscription = await stripe.subscriptions.list({
|
|
||||||
customer: workspace.stripeId,
|
|
||||||
})
|
|
||||||
const currentSubscriptionId = existingSubscription.data[0]?.id
|
|
||||||
if (currentSubscriptionId)
|
|
||||||
await stripe.subscriptions.del(currentSubscriptionId)
|
|
||||||
|
|
||||||
await prisma.workspace.update({
|
|
||||||
where: { id: workspace.id },
|
|
||||||
data: {
|
|
||||||
plan: Plan.FREE,
|
|
||||||
additionalChatsIndex: 0,
|
|
||||||
additionalStorageIndex: 0,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseSubscriptionItems = (
|
|
||||||
plan: Plan,
|
|
||||||
additionalChats: number,
|
|
||||||
additionalStorage: number
|
|
||||||
) =>
|
|
||||||
[
|
|
||||||
{
|
|
||||||
price:
|
|
||||||
plan === Plan.STARTER
|
|
||||||
? process.env.STRIPE_STARTER_PRICE_ID
|
|
||||||
: process.env.STRIPE_PRO_PRICE_ID,
|
|
||||||
quantity: 1,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
.concat(
|
|
||||||
additionalChats > 0
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
price: process.env.STRIPE_ADDITIONAL_CHATS_PRICE_ID,
|
|
||||||
quantity: additionalChats,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []
|
|
||||||
)
|
|
||||||
.concat(
|
|
||||||
additionalStorage > 0
|
|
||||||
? [
|
|
||||||
{
|
|
||||||
price: process.env.STRIPE_ADDITIONAL_STORAGE_PRICE_ID,
|
|
||||||
quantity: additionalStorage,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
: []
|
|
||||||
)
|
|
||||||
|
|
||||||
export default handler
|
|
||||||
@@ -2,7 +2,6 @@ import { Plan } from '@typebot.io/prisma'
|
|||||||
import prisma from '@/lib/prisma'
|
import prisma from '@/lib/prisma'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import {
|
import {
|
||||||
badRequest,
|
|
||||||
methodNotAllowed,
|
methodNotAllowed,
|
||||||
notAuthenticated,
|
notAuthenticated,
|
||||||
notFound,
|
notFound,
|
||||||
@@ -19,37 +18,6 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
const user = await getAuthenticatedUser(req)
|
const user = await getAuthenticatedUser(req)
|
||||||
if (!user) return notAuthenticated(res)
|
if (!user) return notAuthenticated(res)
|
||||||
try {
|
try {
|
||||||
if (req.method === 'GET') {
|
|
||||||
const workspaceId = req.query.workspaceId as string | undefined
|
|
||||||
if (!workspaceId) return badRequest(res)
|
|
||||||
const typebotIds = req.query.typebotIds as string[]
|
|
||||||
const typebots = await prisma.typebot.findMany({
|
|
||||||
where:
|
|
||||||
process.env.ADMIN_EMAIL === user.email
|
|
||||||
? undefined
|
|
||||||
: {
|
|
||||||
OR: [
|
|
||||||
{
|
|
||||||
workspace: { members: { some: { userId: user.id } } },
|
|
||||||
id: { in: typebotIds },
|
|
||||||
isArchived: { not: true },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: { in: typebotIds },
|
|
||||||
collaborators: {
|
|
||||||
some: {
|
|
||||||
userId: user.id,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
isArchived: { not: true },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
orderBy: { createdAt: 'desc' },
|
|
||||||
select: { name: true, id: true, groups: true, variables: true },
|
|
||||||
})
|
|
||||||
return res.send({ typebots })
|
|
||||||
}
|
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
const workspace = await prisma.workspace.findFirst({
|
const workspace = await prisma.workspace.findFirst({
|
||||||
where: { id: req.body.workspaceId },
|
where: { id: req.body.workspaceId },
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
// TODO: Remove when all clients are up to date
|
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
|
||||||
|
|
||||||
const handler = async (_req: NextApiRequest, res: NextApiResponse) => {
|
|
||||||
return res.send({ commitSha: process.env.VERCEL_GIT_COMMIT_SHA })
|
|
||||||
}
|
|
||||||
|
|
||||||
export default handler
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
import prisma from '@/lib/prisma'
|
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
|
||||||
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
|
|
||||||
import { methodNotAllowed, notAuthenticated } from '@typebot.io/lib/api'
|
|
||||||
|
|
||||||
// TODO: Delete
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|
||||||
const user = await getAuthenticatedUser(req)
|
|
||||||
if (!user) return notAuthenticated(res)
|
|
||||||
if (req.method === 'GET') {
|
|
||||||
const workspaceId = req.query.workspaceId as string
|
|
||||||
const now = new Date()
|
|
||||||
const firstDayOfMonth = new Date(now.getFullYear(), now.getMonth(), 1)
|
|
||||||
const firstDayOfNextMonth = new Date(
|
|
||||||
now.getFullYear(),
|
|
||||||
now.getMonth() + 1,
|
|
||||||
1
|
|
||||||
)
|
|
||||||
const [
|
|
||||||
totalChatsUsed,
|
|
||||||
{
|
|
||||||
_sum: { storageUsed: totalStorageUsed },
|
|
||||||
},
|
|
||||||
] = await prisma.$transaction(async (tx) => {
|
|
||||||
const typebots = await tx.typebot.findMany({
|
|
||||||
where: {
|
|
||||||
workspace: {
|
|
||||||
id: workspaceId,
|
|
||||||
members: { some: { userId: user.id } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return Promise.all([
|
|
||||||
prisma.result.count({
|
|
||||||
where: {
|
|
||||||
typebotId: { in: typebots.map((typebot) => typebot.id) },
|
|
||||||
hasStarted: true,
|
|
||||||
createdAt: {
|
|
||||||
gte: firstDayOfMonth,
|
|
||||||
lt: firstDayOfNextMonth,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
prisma.answer.aggregate({
|
|
||||||
where: {
|
|
||||||
storageUsed: { gt: 0 },
|
|
||||||
result: {
|
|
||||||
typebotId: { in: typebots.map((typebot) => typebot.id) },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
_sum: { storageUsed: true },
|
|
||||||
}),
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
return res.send({
|
|
||||||
totalChatsUsed,
|
|
||||||
totalStorageUsed,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
methodNotAllowed(res)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default handler
|
|
||||||
@@ -27,7 +27,7 @@ Fork the repository
|
|||||||
5. Change the build command to:
|
5. Change the build command to:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
cd ../.. && pnpm prisma generate && pnpm build:builder && pnpm db:migrate
|
cd ../.. && pnpm build:builder && pnpm db:migrate
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Add the required environment variables ([Check out the configuration guide](/self-hosting/configuration))
|
6. Add the required environment variables ([Check out the configuration guide](/self-hosting/configuration))
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,34 @@
|
|||||||
|
import { canReadTypebots } from '@/helpers/api/dbRules'
|
||||||
|
import prisma from '@/lib/prisma'
|
||||||
|
import { User } from '@typebot.io/prisma'
|
||||||
|
import { PublicTypebot, Typebot } from '@typebot.io/schemas'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
isPreview?: boolean
|
||||||
|
typebotIds: string[]
|
||||||
|
user?: User
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchLinkedTypebots = async ({
|
||||||
|
user,
|
||||||
|
isPreview,
|
||||||
|
typebotIds,
|
||||||
|
}: Props) => {
|
||||||
|
const linkedTypebots = (
|
||||||
|
isPreview
|
||||||
|
? await prisma.typebot.findMany({
|
||||||
|
where: user
|
||||||
|
? {
|
||||||
|
AND: [
|
||||||
|
{ id: { in: typebotIds } },
|
||||||
|
canReadTypebots(typebotIds, user as User),
|
||||||
|
],
|
||||||
|
}
|
||||||
|
: { id: { in: typebotIds } },
|
||||||
|
})
|
||||||
|
: await prisma.publicTypebot.findMany({
|
||||||
|
where: { id: { in: typebotIds } },
|
||||||
|
})
|
||||||
|
) as (Typebot | PublicTypebot)[]
|
||||||
|
return linkedTypebots
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
import prisma from '@/lib/prisma'
|
|
||||||
import { PublicTypebot, Typebot } from '@typebot.io/schemas'
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
isPreview: boolean
|
|
||||||
typebotIds: string[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getLinkedTypebots = async ({ isPreview, typebotIds }: Props) => {
|
|
||||||
const linkedTypebots = (
|
|
||||||
isPreview
|
|
||||||
? await prisma.typebot.findMany({
|
|
||||||
where: { id: { in: typebotIds } },
|
|
||||||
})
|
|
||||||
: await prisma.publicTypebot.findMany({
|
|
||||||
where: { id: { in: typebotIds } },
|
|
||||||
})
|
|
||||||
) as (Typebot | PublicTypebot)[]
|
|
||||||
return linkedTypebots
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import prisma from '@/lib/prisma'
|
|
||||||
import { canReadTypebots } from '@/helpers/api/dbRules'
|
|
||||||
import { User } from '@typebot.io/prisma'
|
import { User } from '@typebot.io/prisma'
|
||||||
import {
|
import {
|
||||||
LogicBlockType,
|
LogicBlockType,
|
||||||
@@ -8,6 +6,7 @@ import {
|
|||||||
TypebotLinkBlock,
|
TypebotLinkBlock,
|
||||||
} from '@typebot.io/schemas'
|
} from '@typebot.io/schemas'
|
||||||
import { isDefined } from '@typebot.io/lib'
|
import { isDefined } from '@typebot.io/lib'
|
||||||
|
import { fetchLinkedTypebots } from './fetchLinkedTypebots'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
typebots: Pick<PublicTypebot, 'groups'>[]
|
typebots: Pick<PublicTypebot, 'groups'>[]
|
||||||
@@ -15,7 +14,7 @@ type Props = {
|
|||||||
isPreview?: boolean
|
isPreview?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getLinkedTypebotsChildren =
|
export const getPreviouslyLinkedTypebots =
|
||||||
({ typebots, user, isPreview }: Props) =>
|
({ typebots, user, isPreview }: Props) =>
|
||||||
async (
|
async (
|
||||||
capturedLinkedBots: (Typebot | PublicTypebot)[]
|
capturedLinkedBots: (Typebot | PublicTypebot)[]
|
||||||
@@ -39,23 +38,12 @@ export const getLinkedTypebotsChildren =
|
|||||||
)
|
)
|
||||||
.filter(isDefined)
|
.filter(isDefined)
|
||||||
if (linkedTypebotIds.length === 0) return capturedLinkedBots
|
if (linkedTypebotIds.length === 0) return capturedLinkedBots
|
||||||
const linkedTypebots = (
|
const linkedTypebots = (await fetchLinkedTypebots({
|
||||||
isPreview
|
user,
|
||||||
? await prisma.typebot.findMany({
|
typebotIds: linkedTypebotIds,
|
||||||
where: user
|
isPreview,
|
||||||
? {
|
})) as (Typebot | PublicTypebot)[]
|
||||||
AND: [
|
return getPreviouslyLinkedTypebots({
|
||||||
{ id: { in: linkedTypebotIds } },
|
|
||||||
canReadTypebots(linkedTypebotIds, user as User),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
: { id: { in: linkedTypebotIds } },
|
|
||||||
})
|
|
||||||
: await prisma.publicTypebot.findMany({
|
|
||||||
where: { id: { in: linkedTypebotIds } },
|
|
||||||
})
|
|
||||||
) as (Typebot | PublicTypebot)[]
|
|
||||||
return getLinkedTypebotsChildren({
|
|
||||||
typebots: linkedTypebots,
|
typebots: linkedTypebots,
|
||||||
user,
|
user,
|
||||||
isPreview,
|
isPreview,
|
||||||
@@ -21,8 +21,8 @@ import Cors from 'cors'
|
|||||||
import prisma from '@/lib/prisma'
|
import prisma from '@/lib/prisma'
|
||||||
import { parseVariables } from '@/features/variables/parseVariables'
|
import { parseVariables } from '@/features/variables/parseVariables'
|
||||||
import { parseSampleResult } from '@/features/blocks/integrations/webhook/parseSampleResult'
|
import { parseSampleResult } from '@/features/blocks/integrations/webhook/parseSampleResult'
|
||||||
import { getLinkedTypebots } from '@/features/blocks/logic/typebotLink/getLinkedTypebots'
|
import { fetchLinkedTypebots } from '@/features/blocks/logic/typebotLink/fetchLinkedTypebots'
|
||||||
import { getLinkedTypebotsChildren } from '@/features/blocks/logic/typebotLink/getLinkedTypebotsChildren'
|
import { getPreviouslyLinkedTypebots } from '@/features/blocks/logic/typebotLink/getPreviouslyLinkedTypebots'
|
||||||
import { saveErrorLog } from '@/features/logs/saveErrorLog'
|
import { saveErrorLog } from '@/features/logs/saveErrorLog'
|
||||||
import { saveSuccessLog } from '@/features/logs/saveSuccessLog'
|
import { saveSuccessLog } from '@/features/logs/saveSuccessLog'
|
||||||
|
|
||||||
@@ -127,11 +127,11 @@ export const executeWebhook =
|
|||||||
convertKeyValueTableToObject(webhook.queryParams, variables)
|
convertKeyValueTableToObject(webhook.queryParams, variables)
|
||||||
)
|
)
|
||||||
const contentType = headers ? headers['Content-Type'] : undefined
|
const contentType = headers ? headers['Content-Type'] : undefined
|
||||||
const linkedTypebotsParents = await getLinkedTypebots({
|
const linkedTypebotsParents = await fetchLinkedTypebots({
|
||||||
isPreview: !('typebotId' in typebot),
|
isPreview: !('typebotId' in typebot),
|
||||||
typebotIds: parentTypebotIds,
|
typebotIds: parentTypebotIds,
|
||||||
})
|
})
|
||||||
const linkedTypebotsChildren = await getLinkedTypebotsChildren({
|
const linkedTypebotsChildren = await getPreviouslyLinkedTypebots({
|
||||||
isPreview: !('typebotId' in typebot),
|
isPreview: !('typebotId' in typebot),
|
||||||
typebots: [typebot],
|
typebots: [typebot],
|
||||||
})([])
|
})([])
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import prisma from '@/lib/prisma'
|
|||||||
import { Typebot } from '@typebot.io/schemas'
|
import { Typebot } from '@typebot.io/schemas'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { methodNotAllowed } from '@typebot.io/lib/api'
|
import { methodNotAllowed } from '@typebot.io/lib/api'
|
||||||
import { getLinkedTypebotsChildren } from '@/features/blocks/logic/typebotLink/getLinkedTypebotsChildren'
|
import { getPreviouslyLinkedTypebots } from '@/features/blocks/logic/typebotLink/getPreviouslyLinkedTypebots'
|
||||||
import { parseSampleResult } from '@/features/blocks/integrations/webhook/parseSampleResult'
|
import { parseSampleResult } from '@/features/blocks/integrations/webhook/parseSampleResult'
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
@@ -23,7 +23,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
.flatMap((g) => g.blocks)
|
.flatMap((g) => g.blocks)
|
||||||
.find((s) => s.id === blockId)
|
.find((s) => s.id === blockId)
|
||||||
if (!block) return res.status(404).send({ message: 'Group not found' })
|
if (!block) return res.status(404).send({ message: 'Group not found' })
|
||||||
const linkedTypebots = await getLinkedTypebotsChildren({
|
const linkedTypebots = await getPreviouslyLinkedTypebots({
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
typebots: [typebot],
|
typebots: [typebot],
|
||||||
user,
|
user,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { Typebot } from '@typebot.io/schemas'
|
|||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import { methodNotAllowed } from '@typebot.io/lib/api'
|
import { methodNotAllowed } from '@typebot.io/lib/api'
|
||||||
import { parseSampleResult } from '@/features/blocks/integrations/webhook/parseSampleResult'
|
import { parseSampleResult } from '@/features/blocks/integrations/webhook/parseSampleResult'
|
||||||
import { getLinkedTypebotsChildren } from '@/features/blocks/logic/typebotLink/getLinkedTypebotsChildren'
|
import { getPreviouslyLinkedTypebots } from '@/features/blocks/logic/typebotLink/getPreviouslyLinkedTypebots'
|
||||||
import { authenticateUser } from '@/helpers/authenticateUser'
|
import { authenticateUser } from '@/helpers/authenticateUser'
|
||||||
|
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
@@ -19,7 +19,7 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
|||||||
},
|
},
|
||||||
})) as unknown as Typebot | undefined
|
})) as unknown as Typebot | undefined
|
||||||
if (!typebot) return res.status(400).send({ message: 'Typebot not found' })
|
if (!typebot) return res.status(400).send({ message: 'Typebot not found' })
|
||||||
const linkedTypebots = await getLinkedTypebotsChildren({
|
const linkedTypebots = await getPreviouslyLinkedTypebots({
|
||||||
isPreview: true,
|
isPreview: true,
|
||||||
typebots: [typebot],
|
typebots: [typebot],
|
||||||
user,
|
user,
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import Mail from 'nodemailer/lib/mailer'
|
|||||||
import { DefaultBotNotificationEmail } from '@typebot.io/emails'
|
import { DefaultBotNotificationEmail } from '@typebot.io/emails'
|
||||||
import { render } from '@faire/mjml-react/utils/render'
|
import { render } from '@faire/mjml-react/utils/render'
|
||||||
import prisma from '@/lib/prisma'
|
import prisma from '@/lib/prisma'
|
||||||
import { getLinkedTypebotsChildren } from '@/features/blocks/logic/typebotLink/getLinkedTypebotsChildren'
|
import { getPreviouslyLinkedTypebots } from '@/features/blocks/logic/typebotLink/getPreviouslyLinkedTypebots'
|
||||||
import { saveErrorLog } from '@/features/logs/saveErrorLog'
|
import { saveErrorLog } from '@/features/logs/saveErrorLog'
|
||||||
import { saveSuccessLog } from '@/features/logs/saveSuccessLog'
|
import { saveSuccessLog } from '@/features/logs/saveSuccessLog'
|
||||||
|
|
||||||
@@ -194,7 +194,7 @@ const getEmailBody = async ({
|
|||||||
where: { typebotId },
|
where: { typebotId },
|
||||||
})) as unknown as PublicTypebot
|
})) as unknown as PublicTypebot
|
||||||
if (!typebot) return
|
if (!typebot) return
|
||||||
const linkedTypebots = await getLinkedTypebotsChildren({
|
const linkedTypebots = await getPreviouslyLinkedTypebots({
|
||||||
typebots: [typebot],
|
typebots: [typebot],
|
||||||
})([])
|
})([])
|
||||||
const answers = parseAnswers(typebot, linkedTypebots)(resultValues)
|
const answers = parseAnswers(typebot, linkedTypebots)(resultValues)
|
||||||
|
|||||||
Reference in New Issue
Block a user