2
0

♻️ Migrate from got to ky (#1416)

Closes #1415
This commit is contained in:
Baptiste Arnaud
2024-04-05 09:01:16 +02:00
committed by GitHub
parent ccc7101dd3
commit d96f384e02
59 changed files with 990 additions and 628 deletions

View File

@@ -21,7 +21,7 @@ const injectViewerUrlIfVercelPreview = (val) => {
process.env.VERCEL_BUILDER_PROJECT_NAME,
process.env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME
)
if (process.env.NEXT_PUBLIC_CHAT_API_URL.includes('{{pr_id}}'))
if (process.env.NEXT_PUBLIC_CHAT_API_URL?.includes('{{pr_id}}'))
process.env.NEXT_PUBLIC_CHAT_API_URL =
process.env.NEXT_PUBLIC_CHAT_API_URL.replace(
'{{pr_id}}',
@@ -56,12 +56,14 @@ const nextConfig = {
if (nextRuntime === 'edge') {
config.resolve.alias['minio'] = false
config.resolve.alias['got'] = false
config.resolve.alias['qrcode'] = false
return config
}
// These packages are imports from the integrations definition files that can be ignored for the client.
config.resolve.alias['minio'] = false
config.resolve.alias['got'] = false
config.resolve.alias['openai'] = false
config.resolve.alias['qrcode'] = false
return config
},
headers: async () => {

View File

@@ -67,7 +67,7 @@
"framer-motion": "10.3.0",
"google-auth-library": "8.9.0",
"google-spreadsheet": "4.1.1",
"got": "12.6.0",
"ky": "1.2.3",
"immer": "10.0.2",
"jsonwebtoken": "9.0.1",
"libphonenumber-js": "1.10.37",

View File

@@ -32,7 +32,6 @@ export const getResultExample = authenticatedProcedure
})
)
.query(async ({ input: { typebotId, blockId }, ctx: { user } }) => {
console.log('user', user)
const typebot = (await prisma.typebot.findFirst({
where: canReadTypebots(typebotId, user),
select: {

View File

@@ -17,7 +17,7 @@ export const convertVariablesForTestToVariables = (
) as Variable
return { ...variable, value: parseVariableValue(variableForTest.value) }
}, {}),
]
].filter((v) => v.value)
}
const parseVariableValue = (value: string | undefined): string | string[] => {

View File

@@ -1,6 +1,5 @@
import { Variable, HttpResponse } from '@typebot.io/schemas'
import { sendRequest } from '@typebot.io/lib'
import { env } from '@typebot.io/env'
export const executeWebhook = (
typebotId: string,
@@ -8,7 +7,7 @@ export const executeWebhook = (
{ blockId }: { blockId: string }
) =>
sendRequest<HttpResponse>({
url: `${env.NEXT_PUBLIC_VIEWER_URL[0]}/api/typebots/${typebotId}/blocks/${blockId}/executeWebhook`,
url: `/api/typebots/${typebotId}/blocks/${blockId}/testWebhook`,
method: 'POST',
body: {
variables,

View File

@@ -5,7 +5,7 @@ import { z } from 'zod'
import { isReadWorkspaceFobidden } from '@/features/workspace/helpers/isReadWorkspaceFobidden'
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
import { ZemanticAiCredentials } from '@typebot.io/schemas/features/blocks/integrations/zemanticAi'
import got from 'got'
import ky from 'ky'
export const listProjects = authenticatedProcedure
.input(
@@ -58,7 +58,7 @@ export const listProjects = authenticatedProcedure
const url = 'https://api.zemantic.ai/v1/projects'
try {
const response = await got
const response = await ky
.get(url, {
headers: {
Authorization: `Bearer ${data.apiKey}`,

View File

@@ -3,7 +3,7 @@ import { authenticatedProcedure } from '@/helpers/server/trpc'
import { TRPCError } from '@trpc/server'
import { z } from 'zod'
import { customDomainSchema } from '@typebot.io/schemas/features/customDomains'
import got, { HTTPError } from 'got'
import ky, { HTTPError } from 'ky'
import { env } from '@typebot.io/env'
import { isWriteWorkspaceForbidden } from '@/features/workspace/helpers/isWriteWorkspaceForbidden'
import { trackEvents } from '@typebot.io/telemetry/trackEvents'
@@ -61,12 +61,12 @@ export const createCustomDomain = authenticatedProcedure
try {
await createDomainOnVercel(name)
} catch (err) {
console.log(err)
if (err instanceof HTTPError && err.response.statusCode !== 409)
if (err instanceof HTTPError && err.response.status !== 409) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Failed to create custom domain on Vercel',
})
}
}
const customDomain = await prisma.customDomain.create({
@@ -91,8 +91,12 @@ export const createCustomDomain = authenticatedProcedure
})
const createDomainOnVercel = (name: string) =>
got.post({
url: `https://api.vercel.com/v10/projects/${env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME}/domains?teamId=${env.VERCEL_TEAM_ID}`,
headers: { Authorization: `Bearer ${env.VERCEL_TOKEN}` },
json: { name },
})
ky.post(
`https://api.vercel.com/v10/projects/${env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME}/domains?teamId=${env.VERCEL_TEAM_ID}`,
{
headers: {
authorization: `Bearer ${env.VERCEL_TOKEN}`,
},
json: { name },
}
)

View File

@@ -2,7 +2,7 @@ import prisma from '@typebot.io/lib/prisma'
import { authenticatedProcedure } from '@/helpers/server/trpc'
import { TRPCError } from '@trpc/server'
import { z } from 'zod'
import got from 'got'
import ky from 'ky'
import { env } from '@typebot.io/env'
import { isWriteWorkspaceForbidden } from '@/features/workspace/helpers/isWriteWorkspaceForbidden'
@@ -63,7 +63,9 @@ export const deleteCustomDomain = authenticatedProcedure
})
const deleteDomainOnVercel = (name: string) =>
got.delete({
url: `https://api.vercel.com/v9/projects/${env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME}/domains/${name}?teamId=${env.VERCEL_TEAM_ID}`,
headers: { Authorization: `Bearer ${env.VERCEL_TOKEN}` },
})
ky.delete(
`https://api.vercel.com/v9/projects/${env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME}/domains/${name}?teamId=${env.VERCEL_TEAM_ID}`,
{
headers: { Authorization: `Bearer ${env.VERCEL_TOKEN}` },
}
)

View File

@@ -377,6 +377,7 @@ const SystemUserToken = ({
<ListItem>Copy and paste the generated token:</ListItem>
<TextInput
isRequired
type="password"
label="System User Token"
defaultValue={initialToken}
onChange={(val) => setToken(val.trim())}

View File

@@ -1,6 +1,6 @@
import { authenticatedProcedure } from '@/helpers/server/trpc'
import { z } from 'zod'
import got from 'got'
import ky from 'ky'
import prisma from '@typebot.io/lib/prisma'
import { decrypt } from '@typebot.io/lib/api/encryption/decrypt'
import { TRPCError } from '@trpc/server'
@@ -22,16 +22,13 @@ export const getPhoneNumber = authenticatedProcedure
code: 'NOT_FOUND',
message: 'Credentials not found',
})
const { display_phone_number } = (await got(
`${env.WHATSAPP_CLOUD_API_URL}/v17.0/${credentials.phoneNumberId}`,
{
const { display_phone_number } = await ky
.get(`${env.WHATSAPP_CLOUD_API_URL}/v17.0/${credentials.phoneNumberId}`, {
headers: {
Authorization: `Bearer ${credentials.systemUserAccessToken}`,
},
}
).json()) as {
display_phone_number: string
}
})
.json<{ display_phone_number: string }>()
const formattedPhoneNumber = `${
display_phone_number.startsWith('+') ? '' : '+'

View File

@@ -1,6 +1,6 @@
import { authenticatedProcedure } from '@/helpers/server/trpc'
import { z } from 'zod'
import got from 'got'
import ky from 'ky'
import { TRPCError } from '@trpc/server'
import { WhatsAppCredentials } from '@typebot.io/schemas/features/whatsapp'
import prisma from '@typebot.io/lib/prisma'
@@ -28,21 +28,23 @@ export const getSystemTokenInfo = authenticatedProcedure
})
const {
data: { expires_at, scopes, app_id, application },
} = (await got(
`${env.WHATSAPP_CLOUD_API_URL}/v17.0/debug_token?input_token=${credentials.systemUserAccessToken}`,
{
headers: {
Authorization: `Bearer ${credentials.systemUserAccessToken}`,
},
}
).json()) as {
data: {
app_id: string
application: string
expires_at: number
scopes: string[]
}
}
} = await ky
.get(
`${env.WHATSAPP_CLOUD_API_URL}/v17.0/debug_token?input_token=${credentials.systemUserAccessToken}`,
{
headers: {
Authorization: `Bearer ${credentials.systemUserAccessToken}`,
},
}
)
.json<{
data: {
app_id: string
application: string
expires_at: number
scopes: string[]
}
}>()
return {
appId: app_id,

View File

@@ -3,7 +3,7 @@ import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { startSession } from '@typebot.io/bot-engine/startSession'
import { env } from '@typebot.io/env'
import { HTTPError } from 'got'
import { HTTPError } from 'ky'
import prisma from '@typebot.io/lib/prisma'
import { saveStateToDatabase } from '@typebot.io/bot-engine/saveStateToDatabase'
import { restartSession } from '@typebot.io/bot-engine/queries/restartSession'
@@ -169,7 +169,7 @@ export const startWhatsAppPreview = authenticatedProcedure
},
})
} catch (err) {
if (err instanceof HTTPError) console.log(err.response.body)
if (err instanceof HTTPError) console.log(await err.response.text())
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Request to Meta to send preview message failed',

View File

@@ -16,7 +16,7 @@ import { getNewUserInvitations } from '@/features/auth/helpers/getNewUserInvitat
import { sendVerificationRequest } from '@/features/auth/helpers/sendVerificationRequest'
import { Ratelimit } from '@upstash/ratelimit'
import { Redis } from '@upstash/redis/nodejs'
import got from 'got'
import ky from 'ky'
import { env } from '@typebot.io/env'
import * as Sentry from '@sentry/nextjs'
import { getIp } from '@typebot.io/lib/getIp'
@@ -164,10 +164,12 @@ export const getAuthOptions = ({
if (!account) return false
const isNewUser = !('createdAt' in user && isDefined(user.createdAt))
if (isNewUser && user.email) {
const { body } = await got.get(
'https://raw.githubusercontent.com/disposable-email-domains/disposable-email-domains/master/disposable_email_blocklist.conf'
)
const disposableEmailDomains = body.split('\n')
const data = await ky
.get(
'https://raw.githubusercontent.com/disposable-email-domains/disposable-email-domains/master/disposable_email_blocklist.conf'
)
.text()
const disposableEmailDomains = data.split('\n')
if (disposableEmailDomains.includes(user.email.split('@')[1]))
return false
}

View File

@@ -5,7 +5,7 @@ import {
methodNotAllowed,
notAuthenticated,
} from '@typebot.io/lib/api'
import { got } from 'got'
import ky from 'ky'
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
import { env } from '@typebot.io/env'
@@ -31,9 +31,11 @@ const handler = async (req: NextApiRequest, res: NextApiResponse) => {
}
const deleteDomainOnVercel = (name: string) =>
got.delete({
url: `https://api.vercel.com/v8/projects/${env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME}/domains/${name}?teamId=${env.VERCEL_TEAM_ID}`,
headers: { Authorization: `Bearer ${env.VERCEL_TOKEN}` },
})
ky.delete(
`https://api.vercel.com/v8/projects/${env.NEXT_PUBLIC_VERCEL_VIEWER_PROJECT_NAME}/domains/${name}?teamId=${env.VERCEL_TEAM_ID}`,
{
headers: { Authorization: `Bearer ${env.VERCEL_TOKEN}` },
}
)
export default handler

View File

@@ -0,0 +1,114 @@
import {
Typebot,
Variable,
HttpRequest,
Block,
AnswerInSessionState,
} from '@typebot.io/schemas'
import { NextApiRequest, NextApiResponse } from 'next'
import { byId } from '@typebot.io/lib'
import { isWebhookBlock } from '@typebot.io/schemas/helpers'
import { methodNotAllowed, notFound } from '@typebot.io/lib/api'
import prisma from '@typebot.io/lib/prisma'
import { getBlockById } from '@typebot.io/schemas/helpers'
import {
executeWebhook,
parseWebhookAttributes,
} from '@typebot.io/bot-engine/blocks/integrations/webhook/executeWebhookBlock'
import { fetchLinkedChildTypebots } from '@typebot.io/bot-engine/blocks/logic/typebotLink/fetchLinkedChildTypebots'
import { parseSampleResult } from '@typebot.io/bot-engine/blocks/integrations/webhook/parseSampleResult'
import { saveLog } from '@typebot.io/bot-engine/logs/saveLog'
import { getAuthenticatedUser } from '@/features/auth/helpers/getAuthenticatedUser'
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
if (req.method === 'POST') {
const user = await getAuthenticatedUser(req, res)
const typebotId = req.query.typebotId as string
const blockId = req.query.blockId as string
const resultId = req.query.resultId as string | undefined
const { variables } = (
typeof req.body === 'string' ? JSON.parse(req.body) : req.body
) as {
variables: Variable[]
}
const typebot = (await prisma.typebot.findUnique({
where: { id: typebotId },
include: { webhooks: true },
})) as unknown as (Typebot & { webhooks: HttpRequest[] }) | null
if (!typebot) return notFound(res)
const block = typebot.groups
.flatMap<Block>((g) => g.blocks)
.find(byId(blockId))
if (!block || !isWebhookBlock(block))
return notFound(res, 'Webhook block not found')
const webhookId = 'webhookId' in block ? block.webhookId : undefined
const webhook =
block.options?.webhook ??
typebot.webhooks.find((w) => {
if ('id' in w) return w.id === webhookId
return false
})
if (!webhook)
return res
.status(404)
.send({ statusCode: 404, data: { message: `Couldn't find webhook` } })
const { group } = getBlockById(blockId, typebot.groups)
const linkedTypebots = await fetchLinkedChildTypebots({
isPreview: !('typebotId' in typebot),
typebots: [typebot],
userId: user?.id,
})([])
const answers = arrayify(
await parseSampleResult(typebot, linkedTypebots)(group.id, variables)
)
const parsedWebhook = await parseWebhookAttributes({
webhook,
isCustomBody: block.options?.isCustomBody,
typebot: {
...typebot,
variables: typebot.variables.map((v) => {
const matchingVariable = variables.find(byId(v.id))
if (!matchingVariable) return v
return { ...v, value: matchingVariable.value }
}),
},
answers,
})
if (!parsedWebhook)
return res.status(500).send({
statusCode: 500,
data: { message: `Couldn't parse webhook attributes` },
})
const { response, logs } = await executeWebhook(parsedWebhook, {
timeout: block.options?.timeout,
})
if (resultId)
await Promise.all(
logs?.map((log) =>
saveLog({
message: log.description,
details: log.details,
status: log.status as 'error' | 'success' | 'info',
resultId,
})
) ?? []
)
return res.status(200).send(response)
}
return methodNotAllowed(res)
}
const arrayify = (
obj: Record<string, string | boolean | undefined>
): AnswerInSessionState[] =>
Object.entries(obj)
.map(([key, value]) => ({ key, value: value?.toString() }))
.filter((a) => a.value) as AnswerInSessionState[]
export default handler