@@ -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 () => {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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[] => {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}`,
|
||||
|
||||
@@ -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 },
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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}` },
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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())}
|
||||
|
||||
@@ -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('+') ? '' : '+'
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user