fix: ✨ Allow webhook with basic auth
This commit is contained in:
@@ -14,7 +14,6 @@ import {
|
||||
IntegrationStepType,
|
||||
LogicStepType,
|
||||
Step,
|
||||
StepIndices,
|
||||
StepOptions,
|
||||
TextBubbleStep,
|
||||
Webhook,
|
||||
@@ -44,7 +43,6 @@ type Props = {
|
||||
onExpandClick: () => void
|
||||
onStepChange: (updates: Partial<Step>) => void
|
||||
onTestRequestClick: () => void
|
||||
indices: StepIndices
|
||||
}
|
||||
|
||||
export const SettingsPopoverContent = ({ onExpandClick, ...props }: Props) => {
|
||||
@@ -87,13 +85,11 @@ export const StepSettings = ({
|
||||
step,
|
||||
onStepChange,
|
||||
onTestRequestClick,
|
||||
indices,
|
||||
}: {
|
||||
step: Step
|
||||
webhook?: Webhook
|
||||
onStepChange: (step: Partial<Step>) => void
|
||||
onTestRequestClick: () => void
|
||||
indices: StepIndices
|
||||
}) => {
|
||||
const handleOptionsChange = (options: StepOptions) => {
|
||||
onStepChange({ options } as Partial<Step>)
|
||||
@@ -210,7 +206,6 @@ export const StepSettings = ({
|
||||
onOptionsChange={handleOptionsChange}
|
||||
onWebhookChange={handleWebhookChange}
|
||||
onTestRequestClick={onTestRequestClick}
|
||||
indices={indices}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
Webhook,
|
||||
ResponseVariableMapping,
|
||||
WebhookStep,
|
||||
StepIndices,
|
||||
} from 'models'
|
||||
import { DropdownList } from 'components/shared/DropdownList'
|
||||
import { TableList, TableListItemProps } from 'components/shared/TableList'
|
||||
@@ -39,15 +38,13 @@ type Props = {
|
||||
onOptionsChange: (options: WebhookOptions) => void
|
||||
onWebhookChange: (updates: Partial<Webhook>) => void
|
||||
onTestRequestClick: () => void
|
||||
indices: StepIndices
|
||||
}
|
||||
|
||||
export const WebhookSettings = ({
|
||||
step: { webhook, options },
|
||||
step: { webhook, options, blockId, id: stepId },
|
||||
onOptionsChange,
|
||||
onWebhookChange,
|
||||
onTestRequestClick,
|
||||
indices,
|
||||
}: Props) => {
|
||||
const { typebot, save } = useTypebot()
|
||||
const [isTestResponseLoading, setIsTestResponseLoading] = useState(false)
|
||||
@@ -85,12 +82,11 @@ export const WebhookSettings = ({
|
||||
await save()
|
||||
const { data, error } = await executeWebhook(
|
||||
typebot.id,
|
||||
webhook.id,
|
||||
convertVariableForTestToVariables(
|
||||
options.variablesForTest,
|
||||
typebot.variables
|
||||
),
|
||||
indices
|
||||
{ blockId, stepId }
|
||||
)
|
||||
if (error) return toast({ title: error.name, description: error.message })
|
||||
setTestResponse(JSON.stringify(data, undefined, 2))
|
||||
|
||||
@@ -216,7 +216,6 @@ export const StepNode = ({
|
||||
onExpandClick={handleExpandClick}
|
||||
onStepChange={handleStepChange}
|
||||
onTestRequestClick={updateOptions}
|
||||
indices={indices}
|
||||
/>
|
||||
)}
|
||||
{isMediaBubbleStep(localStep) && (
|
||||
@@ -230,7 +229,6 @@ export const StepNode = ({
|
||||
step={localStep}
|
||||
onStepChange={handleStepChange}
|
||||
onTestRequestClick={updateOptions}
|
||||
indices={indices}
|
||||
/>
|
||||
</SettingsModal>
|
||||
</Popover>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { WritableDraft } from 'immer/dist/types/types-external'
|
||||
import { generate } from 'short-uuid'
|
||||
import { SetTypebot } from '../TypebotContext'
|
||||
import { produce } from 'immer'
|
||||
import { byId, isDefined, isNotDefined } from 'utils'
|
||||
import { byId, isDefined } from 'utils'
|
||||
|
||||
export type EdgesActions = {
|
||||
createEdge: (edge: Omit<Edge, 'id'>) => void
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
import prisma from 'libs/prisma'
|
||||
import { KeyValue, Typebot, Variable, Webhook, WebhookResponse } from 'models'
|
||||
import { parseVariables } from 'bot-engine'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import got, { Method, Headers, HTTPError } from 'got'
|
||||
import { methodNotAllowed } from 'utils'
|
||||
import { stringify } from 'qs'
|
||||
import { withSentry } from '@sentry/nextjs'
|
||||
|
||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
if (req.method === 'POST') {
|
||||
const typebotId = req.query.typebotId.toString()
|
||||
const blockIndex = Number(req.query.blockIndex)
|
||||
const stepIndex = Number(req.query.stepIndex)
|
||||
const variables = JSON.parse(req.body).variables as Variable[]
|
||||
const typebot = await prisma.typebot.findUnique({
|
||||
where: { id: typebotId },
|
||||
})
|
||||
const step = (typebot as unknown as Typebot).blocks[blockIndex].steps[
|
||||
stepIndex
|
||||
]
|
||||
if (!('webhook' in step))
|
||||
return res
|
||||
.status(404)
|
||||
.send({ statusCode: 404, data: { message: `Couldn't find webhook` } })
|
||||
const result = await executeWebhook(step.webhook, variables)
|
||||
return res.status(200).send(result)
|
||||
}
|
||||
return methodNotAllowed(res)
|
||||
}
|
||||
|
||||
const executeWebhook = async (
|
||||
webhook: Webhook,
|
||||
variables: Variable[]
|
||||
): Promise<WebhookResponse> => {
|
||||
if (!webhook.url || !webhook.method)
|
||||
return {
|
||||
statusCode: 400,
|
||||
data: { message: `Webhook doesn't have url or method` },
|
||||
}
|
||||
const headers = convertKeyValueTableToObject(webhook.headers, variables) as
|
||||
| Headers
|
||||
| undefined
|
||||
const queryParams = stringify(
|
||||
convertKeyValueTableToObject(webhook.queryParams, variables)
|
||||
)
|
||||
const contentType = headers ? headers['Content-Type'] : undefined
|
||||
try {
|
||||
const response = await got(
|
||||
parseVariables(variables)(webhook.url + `?${queryParams}`),
|
||||
{
|
||||
method: webhook.method as Method,
|
||||
headers,
|
||||
json:
|
||||
contentType !== 'x-www-form-urlencoded' && webhook.body
|
||||
? JSON.parse(parseVariables(variables)(webhook.body))
|
||||
: undefined,
|
||||
form:
|
||||
contentType === 'x-www-form-urlencoded' && webhook.body
|
||||
? JSON.parse(parseVariables(variables)(webhook.body))
|
||||
: undefined,
|
||||
}
|
||||
)
|
||||
return {
|
||||
statusCode: response.statusCode,
|
||||
data: parseBody(response.body),
|
||||
}
|
||||
} catch (error) {
|
||||
if (error instanceof HTTPError) {
|
||||
return {
|
||||
statusCode: error.response.statusCode,
|
||||
data: parseBody(error.response.body as string),
|
||||
}
|
||||
}
|
||||
return {
|
||||
statusCode: 500,
|
||||
data: { message: `Error from Typebot server: ${error}` },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const parseBody = (body: string) => {
|
||||
try {
|
||||
return JSON.parse(body)
|
||||
} catch (err) {
|
||||
return body
|
||||
}
|
||||
}
|
||||
|
||||
const convertKeyValueTableToObject = (
|
||||
keyValues: KeyValue[] | undefined,
|
||||
variables: Variable[]
|
||||
) => {
|
||||
if (!keyValues) return
|
||||
return keyValues.reduce((object, item) => {
|
||||
if (!item.key) return {}
|
||||
return {
|
||||
...object,
|
||||
[item.key]: parseVariables(variables)(item.value ?? ''),
|
||||
}
|
||||
}, {})
|
||||
}
|
||||
|
||||
export default withSentry(handler)
|
||||
@@ -2,7 +2,7 @@ import { sendRequest } from 'utils'
|
||||
import { stringify } from 'qs'
|
||||
import useSWR from 'swr'
|
||||
import { fetcher } from './utils'
|
||||
import { StepIndices, Variable, VariableForTest, WebhookResponse } from 'models'
|
||||
import { Variable, VariableForTest, WebhookResponse } from 'models'
|
||||
|
||||
export const getGoogleSheetsConsentScreenUrl = (
|
||||
redirectUrl: string,
|
||||
@@ -68,12 +68,11 @@ export const useSheets = ({
|
||||
|
||||
export const executeWebhook = (
|
||||
typebotId: string,
|
||||
webhookId: string,
|
||||
variables: Variable[],
|
||||
{ blockIndex, stepIndex }: StepIndices
|
||||
{ blockId, stepId }: { blockId: string; stepId: string }
|
||||
) =>
|
||||
sendRequest<WebhookResponse>({
|
||||
url: `/api/typebots/${typebotId}/blocks/${blockIndex}/steps/${stepIndex}/executeWebhook`,
|
||||
url: `${process.env.NEXT_PUBLIC_VIEWER_HOST}/api/typebots/${typebotId}/blocks/${blockId}/steps/${stepId}/executeWebhook`,
|
||||
method: 'POST',
|
||||
body: {
|
||||
variables,
|
||||
|
||||
Reference in New Issue
Block a user