🚸 Improve parsing preprocessing on typebots

This commit is contained in:
Baptiste Arnaud
2023-08-23 10:57:38 +02:00
parent fe54888350
commit 0acede92ef
24 changed files with 132 additions and 584 deletions

View File

@@ -1,62 +0,0 @@
import { CustomDomain } from '@typebot.io/prisma'
import { got, HTTPError } from 'got'
import prisma from '@/lib/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
import {
badRequest,
forbidden,
methodNotAllowed,
notAuthenticated,
} from '@typebot.io/lib/api'
// TODO: delete
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getAuthenticatedUser(req, res)
if (!user) return notAuthenticated(res)
const workspaceId = req.query.workspaceId as string | undefined
if (!workspaceId) return badRequest(res)
if (req.method === 'GET') {
const customDomains = await prisma.customDomain.findMany({
where: {
workspace: { id: workspaceId, members: { some: { userId: user.id } } },
},
})
return res.send({ customDomains })
}
if (req.method === 'POST') {
const workspace = await prisma.workspace.findFirst({
where: { id: workspaceId, members: { some: { userId: user.id } } },
select: { id: true },
})
if (!workspace) return forbidden(res)
const data = (
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
) as CustomDomain
try {
await createDomainOnVercel(data.name)
} catch (err) {
console.log(err)
if (err instanceof HTTPError && err.response.statusCode !== 409)
return res.status(err.response.statusCode).send(err.response.body)
}
const customDomains = await prisma.customDomain.create({
data: {
...data,
workspaceId,
},
})
return res.send({ customDomains })
}
return methodNotAllowed(res)
}
const createDomainOnVercel = (name: string) =>
got.post({
url: `https://api.vercel.com/v8/projects/${process.env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME}/domains?teamId=${process.env.VERCEL_TEAM_ID}`,
headers: { Authorization: `Bearer ${process.env.VERCEL_TOKEN}` },
json: { name },
})
export default handler

View File

@@ -1,72 +0,0 @@
import { Plan } from '@typebot.io/prisma'
import prisma from '@/lib/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import {
methodNotAllowed,
notAuthenticated,
notFound,
} from '@typebot.io/lib/api'
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
import {
NewTypebotProps,
parseNewTypebot,
} from '@/features/dashboard/api/parseNewTypebot'
import { omit } from '@typebot.io/lib'
import { sendTelemetryEvents } from '@typebot.io/lib/telemetry/sendTelemetryEvent'
// TODO: Delete
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getAuthenticatedUser(req, res)
if (!user) return notAuthenticated(res)
try {
if (req.method === 'POST') {
const workspace = await prisma.workspace.findFirst({
where: { id: req.body.workspaceId },
select: { plan: true },
})
if (!workspace) return notFound(res, "Couldn't find workspace")
const data =
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
const formattedData = removeOldProperties(data) as
| NewTypebotProps
| Omit<NewTypebotProps, 'groups'>
const typebot = await prisma.typebot.create({
data:
'groups' in formattedData
? formattedData
: parseNewTypebot({
isBrandingEnabled: workspace.plan === Plan.FREE,
...data,
}),
})
await sendTelemetryEvents([
{
name: 'Typebot created',
userId: user.id,
workspaceId: typebot.workspaceId,
typebotId: typebot.id,
data: {
name: typebot.name,
},
},
])
return res.send(typebot)
}
return methodNotAllowed(res)
} catch (err) {
console.error(err)
if (err instanceof Error) {
return res.status(500).send({ title: err.name, message: err.message })
}
return res.status(500).send({ message: 'An error occured', error: err })
}
}
const removeOldProperties = (data: unknown) => {
if (data && typeof data === 'object' && 'publishedTypebotId' in data) {
return omit(data, 'publishedTypebotId')
}
return data
}
export default handler

View File

@@ -1,180 +0,0 @@
import { CollaborationType, Prisma } from '@typebot.io/prisma'
import prisma from '@/lib/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { methodNotAllowed, notAuthenticated } from '@typebot.io/lib/api'
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
import { Group, Typebot } from '@typebot.io/schemas'
import { omit } from '@typebot.io/lib'
import { isReadTypebotForbidden } from '@/features/typebot/helpers/isReadTypebotForbidden'
import { archiveResults } from '@typebot.io/lib/api/helpers/archiveResults'
import { migrateTypebotFromV3ToV4 } from '@typebot.io/lib/migrations/migrateTypebotFromV3ToV4'
import { isWriteTypebotForbidden } from '@/features/typebot/helpers/isWriteTypebotForbidden'
// TODO: delete
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getAuthenticatedUser(req, res)
if (!user) return notAuthenticated(res)
const typebotId = req.query.typebotId as string
if (req.method === 'GET') {
const fullTypebot = await prisma.typebot.findFirst({
where: {
id: typebotId,
isArchived: { not: true },
},
include: {
publishedTypebot: true,
collaborators: { select: { userId: true, type: true } },
webhooks: true,
},
})
if (!fullTypebot || (await isReadTypebotForbidden(fullTypebot, user)))
return res.status(404).send({ typebot: null })
const { publishedTypebot, collaborators, webhooks, ...typebot } =
fullTypebot
const isReadOnly =
collaborators.find((c) => c.userId === user.id)?.type ===
CollaborationType.READ
return res.send({
typebot: await migrateTypebot(typebot as Typebot),
publishedTypebot,
isReadOnly,
webhooks,
})
}
if (req.method === 'DELETE') {
const typebot = await prisma.typebot.findUnique({
where: {
id: typebotId,
},
select: {
id: true,
workspaceId: true,
groups: true,
collaborators: {
select: {
userId: true,
type: true,
},
},
},
})
if (!typebot || (await isWriteTypebotForbidden(typebot, user)))
return res.status(404).send({ typebot: null })
const { success } = await archiveResults(prisma)({
typebot: {
groups: typebot.groups as Group[],
},
resultsFilter: { typebotId },
})
if (!success) return res.status(500).send({ success: false, error: '' })
await prisma.publicTypebot.deleteMany({
where: { typebotId },
})
const typebots = await prisma.typebot.updateMany({
where: { id: typebotId },
data: { isArchived: true, publicId: null, customDomain: null },
})
return res.send({ typebots })
}
if (req.method === 'PUT') {
const data = (
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
) as Typebot
const typebot = await prisma.typebot.findUnique({
where: {
id: typebotId,
},
select: {
id: true,
workspaceId: true,
groups: true,
updatedAt: true,
collaborators: {
select: {
userId: true,
type: true,
},
},
},
})
if (!typebot || (await isWriteTypebotForbidden(typebot, user)))
return res.status(404).send({ message: 'Typebot not found' })
if (
(typebot.updatedAt as Date).getTime() > new Date(data.updatedAt).getTime()
)
return res.send({
message: 'Found newer version of the typebot in database',
})
const updates = {
...omit(data, 'id', 'createdAt', 'updatedAt'),
theme: data.theme ?? undefined,
settings: data.settings ?? undefined,
resultsTablePreferences: data.resultsTablePreferences ?? undefined,
groups: data.groups ?? [],
variables: data.variables ?? [],
edges: data.edges ?? [],
} satisfies Prisma.TypebotUpdateInput
try {
const updatedTypebot = await prisma.typebot.update({
where: { id: typebotId },
data: updates,
})
return res.send({ typebot: updatedTypebot })
} catch (err) {
if (err instanceof Prisma.PrismaClientKnownRequestError) {
if (err.code === 'P2002') {
return res.status(409).send({
message:
err.meta && 'target' in err.meta && Array.isArray(err.meta.target)
? `${err.meta.target[0]} already exists`
: 'Duplicate conflict',
})
}
return res.status(500).send({ message: err.message })
}
}
}
if (req.method === 'PATCH') {
const typebot = await prisma.typebot.findUnique({
where: {
id: typebotId,
},
select: {
id: true,
workspaceId: true,
groups: true,
collaborators: {
select: {
userId: true,
type: true,
},
},
},
})
if (!typebot || (await isWriteTypebotForbidden(typebot, user)))
return res.status(404).send({ message: 'Typebot not found' })
const data = typeof req.body === 'string' ? JSON.parse(req.body) : req.body
const updatedTypebot = await prisma.typebot.update({
where: { id: typebotId },
data,
})
return res.send({ typebot: updatedTypebot })
}
return methodNotAllowed(res)
}
const migrateTypebot = async (typebot: Typebot): Promise<Typebot> => {
if (typebot.version === '4') return typebot
return migrateTypebotFromV3ToV4(prisma)(typebot)
}
export default handler

View File

@@ -1,38 +0,0 @@
import { PublicTypebot } from '@typebot.io/schemas'
import prisma from '@/lib/prisma'
import { NextApiRequest, NextApiResponse } from 'next'
import { methodNotAllowed, notAuthenticated } from '@typebot.io/lib/api'
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
import { canReadTypebots } from '@/helpers/databaseRules'
// TODO: Delete (deprecated)
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
const user = await getAuthenticatedUser(req, res)
if (!user) return notAuthenticated(res)
if (req.method === 'GET') {
const typebotId = req.query.typebotId as string
const typebot = await prisma.typebot.findFirst({
where: canReadTypebots(typebotId, user),
select: { publishedTypebot: true },
})
const publishedTypebot =
typebot?.publishedTypebot as unknown as PublicTypebot
if (!publishedTypebot) return res.status(404).send({ answersCounts: [] })
const answersCounts = await prisma.answer.groupBy({
by: ['groupId'],
where: {
groupId: { in: publishedTypebot.groups.map((g) => g.id) },
},
_count: { _all: true },
})
return res.status(200).send({
answersCounts: answersCounts.map((answer) => ({
groupId: answer.groupId,
totalAnswers: answer._count._all,
})),
})
}
return methodNotAllowed(res)
}
export default handler