fix: ✨ Allow webhook with basic auth
This commit is contained in:
@@ -14,7 +14,6 @@ import {
|
|||||||
IntegrationStepType,
|
IntegrationStepType,
|
||||||
LogicStepType,
|
LogicStepType,
|
||||||
Step,
|
Step,
|
||||||
StepIndices,
|
|
||||||
StepOptions,
|
StepOptions,
|
||||||
TextBubbleStep,
|
TextBubbleStep,
|
||||||
Webhook,
|
Webhook,
|
||||||
@@ -44,7 +43,6 @@ type Props = {
|
|||||||
onExpandClick: () => void
|
onExpandClick: () => void
|
||||||
onStepChange: (updates: Partial<Step>) => void
|
onStepChange: (updates: Partial<Step>) => void
|
||||||
onTestRequestClick: () => void
|
onTestRequestClick: () => void
|
||||||
indices: StepIndices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SettingsPopoverContent = ({ onExpandClick, ...props }: Props) => {
|
export const SettingsPopoverContent = ({ onExpandClick, ...props }: Props) => {
|
||||||
@@ -87,13 +85,11 @@ export const StepSettings = ({
|
|||||||
step,
|
step,
|
||||||
onStepChange,
|
onStepChange,
|
||||||
onTestRequestClick,
|
onTestRequestClick,
|
||||||
indices,
|
|
||||||
}: {
|
}: {
|
||||||
step: Step
|
step: Step
|
||||||
webhook?: Webhook
|
webhook?: Webhook
|
||||||
onStepChange: (step: Partial<Step>) => void
|
onStepChange: (step: Partial<Step>) => void
|
||||||
onTestRequestClick: () => void
|
onTestRequestClick: () => void
|
||||||
indices: StepIndices
|
|
||||||
}) => {
|
}) => {
|
||||||
const handleOptionsChange = (options: StepOptions) => {
|
const handleOptionsChange = (options: StepOptions) => {
|
||||||
onStepChange({ options } as Partial<Step>)
|
onStepChange({ options } as Partial<Step>)
|
||||||
@@ -210,7 +206,6 @@ export const StepSettings = ({
|
|||||||
onOptionsChange={handleOptionsChange}
|
onOptionsChange={handleOptionsChange}
|
||||||
onWebhookChange={handleWebhookChange}
|
onWebhookChange={handleWebhookChange}
|
||||||
onTestRequestClick={onTestRequestClick}
|
onTestRequestClick={onTestRequestClick}
|
||||||
indices={indices}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import {
|
|||||||
Webhook,
|
Webhook,
|
||||||
ResponseVariableMapping,
|
ResponseVariableMapping,
|
||||||
WebhookStep,
|
WebhookStep,
|
||||||
StepIndices,
|
|
||||||
} from 'models'
|
} from 'models'
|
||||||
import { DropdownList } from 'components/shared/DropdownList'
|
import { DropdownList } from 'components/shared/DropdownList'
|
||||||
import { TableList, TableListItemProps } from 'components/shared/TableList'
|
import { TableList, TableListItemProps } from 'components/shared/TableList'
|
||||||
@@ -39,15 +38,13 @@ type Props = {
|
|||||||
onOptionsChange: (options: WebhookOptions) => void
|
onOptionsChange: (options: WebhookOptions) => void
|
||||||
onWebhookChange: (updates: Partial<Webhook>) => void
|
onWebhookChange: (updates: Partial<Webhook>) => void
|
||||||
onTestRequestClick: () => void
|
onTestRequestClick: () => void
|
||||||
indices: StepIndices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WebhookSettings = ({
|
export const WebhookSettings = ({
|
||||||
step: { webhook, options },
|
step: { webhook, options, blockId, id: stepId },
|
||||||
onOptionsChange,
|
onOptionsChange,
|
||||||
onWebhookChange,
|
onWebhookChange,
|
||||||
onTestRequestClick,
|
onTestRequestClick,
|
||||||
indices,
|
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { typebot, save } = useTypebot()
|
const { typebot, save } = useTypebot()
|
||||||
const [isTestResponseLoading, setIsTestResponseLoading] = useState(false)
|
const [isTestResponseLoading, setIsTestResponseLoading] = useState(false)
|
||||||
@@ -85,12 +82,11 @@ export const WebhookSettings = ({
|
|||||||
await save()
|
await save()
|
||||||
const { data, error } = await executeWebhook(
|
const { data, error } = await executeWebhook(
|
||||||
typebot.id,
|
typebot.id,
|
||||||
webhook.id,
|
|
||||||
convertVariableForTestToVariables(
|
convertVariableForTestToVariables(
|
||||||
options.variablesForTest,
|
options.variablesForTest,
|
||||||
typebot.variables
|
typebot.variables
|
||||||
),
|
),
|
||||||
indices
|
{ blockId, stepId }
|
||||||
)
|
)
|
||||||
if (error) return toast({ title: error.name, description: error.message })
|
if (error) return toast({ title: error.name, description: error.message })
|
||||||
setTestResponse(JSON.stringify(data, undefined, 2))
|
setTestResponse(JSON.stringify(data, undefined, 2))
|
||||||
|
|||||||
@@ -216,7 +216,6 @@ export const StepNode = ({
|
|||||||
onExpandClick={handleExpandClick}
|
onExpandClick={handleExpandClick}
|
||||||
onStepChange={handleStepChange}
|
onStepChange={handleStepChange}
|
||||||
onTestRequestClick={updateOptions}
|
onTestRequestClick={updateOptions}
|
||||||
indices={indices}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isMediaBubbleStep(localStep) && (
|
{isMediaBubbleStep(localStep) && (
|
||||||
@@ -230,7 +229,6 @@ export const StepNode = ({
|
|||||||
step={localStep}
|
step={localStep}
|
||||||
onStepChange={handleStepChange}
|
onStepChange={handleStepChange}
|
||||||
onTestRequestClick={updateOptions}
|
onTestRequestClick={updateOptions}
|
||||||
indices={indices}
|
|
||||||
/>
|
/>
|
||||||
</SettingsModal>
|
</SettingsModal>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { WritableDraft } from 'immer/dist/types/types-external'
|
|||||||
import { generate } from 'short-uuid'
|
import { generate } from 'short-uuid'
|
||||||
import { SetTypebot } from '../TypebotContext'
|
import { SetTypebot } from '../TypebotContext'
|
||||||
import { produce } from 'immer'
|
import { produce } from 'immer'
|
||||||
import { byId, isDefined, isNotDefined } from 'utils'
|
import { byId, isDefined } from 'utils'
|
||||||
|
|
||||||
export type EdgesActions = {
|
export type EdgesActions = {
|
||||||
createEdge: (edge: Omit<Edge, 'id'>) => void
|
createEdge: (edge: Omit<Edge, 'id'>) => void
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { sendRequest } from 'utils'
|
|||||||
import { stringify } from 'qs'
|
import { stringify } from 'qs'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
import { fetcher } from './utils'
|
import { fetcher } from './utils'
|
||||||
import { StepIndices, Variable, VariableForTest, WebhookResponse } from 'models'
|
import { Variable, VariableForTest, WebhookResponse } from 'models'
|
||||||
|
|
||||||
export const getGoogleSheetsConsentScreenUrl = (
|
export const getGoogleSheetsConsentScreenUrl = (
|
||||||
redirectUrl: string,
|
redirectUrl: string,
|
||||||
@@ -68,12 +68,11 @@ export const useSheets = ({
|
|||||||
|
|
||||||
export const executeWebhook = (
|
export const executeWebhook = (
|
||||||
typebotId: string,
|
typebotId: string,
|
||||||
webhookId: string,
|
|
||||||
variables: Variable[],
|
variables: Variable[],
|
||||||
{ blockIndex, stepIndex }: StepIndices
|
{ blockId, stepId }: { blockId: string; stepId: string }
|
||||||
) =>
|
) =>
|
||||||
sendRequest<WebhookResponse>({
|
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',
|
method: 'POST',
|
||||||
body: {
|
body: {
|
||||||
variables,
|
variables,
|
||||||
|
|||||||
@@ -3,23 +3,26 @@ import { KeyValue, Typebot, Variable, Webhook, WebhookResponse } from 'models'
|
|||||||
import { parseVariables } from 'bot-engine'
|
import { parseVariables } from 'bot-engine'
|
||||||
import { NextApiRequest, NextApiResponse } from 'next'
|
import { NextApiRequest, NextApiResponse } from 'next'
|
||||||
import got, { Method, Headers, HTTPError } from 'got'
|
import got, { Method, Headers, HTTPError } from 'got'
|
||||||
import { methodNotAllowed } from 'utils'
|
import { byId, initMiddleware, methodNotAllowed } from 'utils'
|
||||||
import { stringify } from 'qs'
|
import { stringify } from 'qs'
|
||||||
import { withSentry } from '@sentry/nextjs'
|
import { withSentry } from '@sentry/nextjs'
|
||||||
|
import Cors from 'cors'
|
||||||
|
|
||||||
|
const cors = initMiddleware(Cors())
|
||||||
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
const handler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||||
|
await cors(req, res)
|
||||||
if (req.method === 'POST') {
|
if (req.method === 'POST') {
|
||||||
const typebotId = req.query.typebotId.toString()
|
const typebotId = req.query.typebotId.toString()
|
||||||
const blockIndex = Number(req.query.blockIndex)
|
const blockId = req.query.blockId.toString()
|
||||||
const stepIndex = Number(req.query.stepIndex)
|
const stepId = req.query.stepId.toString()
|
||||||
const variables = JSON.parse(req.body).variables as Variable[]
|
const variables = JSON.parse(req.body).variables as Variable[]
|
||||||
const typebot = await prisma.typebot.findUnique({
|
const typebot = await prisma.typebot.findUnique({
|
||||||
where: { id: typebotId },
|
where: { id: typebotId },
|
||||||
})
|
})
|
||||||
const step = (typebot as unknown as Typebot).blocks[blockIndex].steps[
|
const step = (typebot as unknown as Typebot).blocks
|
||||||
stepIndex
|
.find(byId(blockId))
|
||||||
]
|
?.steps.find(byId(stepId))
|
||||||
if (!('webhook' in step))
|
if (!step || !('webhook' in step))
|
||||||
return res
|
return res
|
||||||
.status(404)
|
.status(404)
|
||||||
.send({ statusCode: 404, data: { message: `Couldn't find webhook` } })
|
.send({ statusCode: 404, data: { message: `Couldn't find webhook` } })
|
||||||
@@ -38,6 +41,19 @@ const executeWebhook = async (
|
|||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
data: { message: `Webhook doesn't have url or method` },
|
data: { message: `Webhook doesn't have url or method` },
|
||||||
}
|
}
|
||||||
|
const basicAuth: { username?: string; password?: string } = {}
|
||||||
|
const basicAuthHeaderIdx = webhook.headers.findIndex(
|
||||||
|
(h) =>
|
||||||
|
h.key?.toLowerCase() === 'authorization' &&
|
||||||
|
h.value?.toLowerCase().includes('basic')
|
||||||
|
)
|
||||||
|
if (basicAuthHeaderIdx !== -1) {
|
||||||
|
const [username, password] =
|
||||||
|
webhook.headers[basicAuthHeaderIdx].value?.slice(6).split(':') ?? []
|
||||||
|
basicAuth.username = username
|
||||||
|
basicAuth.password = password
|
||||||
|
webhook.headers.splice(basicAuthHeaderIdx, 1)
|
||||||
|
}
|
||||||
const headers = convertKeyValueTableToObject(webhook.headers, variables) as
|
const headers = convertKeyValueTableToObject(webhook.headers, variables) as
|
||||||
| Headers
|
| Headers
|
||||||
| undefined
|
| undefined
|
||||||
@@ -51,6 +67,7 @@ const executeWebhook = async (
|
|||||||
{
|
{
|
||||||
method: webhook.method as Method,
|
method: webhook.method as Method,
|
||||||
headers,
|
headers,
|
||||||
|
...basicAuth,
|
||||||
json:
|
json:
|
||||||
contentType !== 'x-www-form-urlencoded' && webhook.body
|
contentType !== 'x-www-form-urlencoded' && webhook.body
|
||||||
? JSON.parse(parseVariables(variables)(webhook.body))
|
? JSON.parse(parseVariables(variables)(webhook.body))
|
||||||
@@ -19,7 +19,6 @@ import { parseRetryStep, stepCanBeRetried } from 'services/inputs'
|
|||||||
type ChatBlockProps = {
|
type ChatBlockProps = {
|
||||||
steps: PublicStep[]
|
steps: PublicStep[]
|
||||||
startStepIndex: number
|
startStepIndex: number
|
||||||
blockIndex: number
|
|
||||||
onScroll: () => void
|
onScroll: () => void
|
||||||
onBlockEnd: (edgeId?: string) => void
|
onBlockEnd: (edgeId?: string) => void
|
||||||
}
|
}
|
||||||
@@ -27,7 +26,6 @@ type ChatBlockProps = {
|
|||||||
export const ChatBlock = ({
|
export const ChatBlock = ({
|
||||||
steps,
|
steps,
|
||||||
startStepIndex,
|
startStepIndex,
|
||||||
blockIndex,
|
|
||||||
onScroll,
|
onScroll,
|
||||||
onBlockEnd,
|
onBlockEnd,
|
||||||
}: ChatBlockProps) => {
|
}: ChatBlockProps) => {
|
||||||
@@ -35,8 +33,6 @@ export const ChatBlock = ({
|
|||||||
useTypebot()
|
useTypebot()
|
||||||
const [displayedSteps, setDisplayedSteps] = useState<PublicStep[]>([])
|
const [displayedSteps, setDisplayedSteps] = useState<PublicStep[]>([])
|
||||||
|
|
||||||
const currentStepIndex = displayedSteps.length - 1
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const nextStep = steps[startStepIndex]
|
const nextStep = steps[startStepIndex]
|
||||||
if (nextStep) setDisplayedSteps([...displayedSteps, nextStep])
|
if (nextStep) setDisplayedSteps([...displayedSteps, nextStep])
|
||||||
@@ -65,8 +61,9 @@ export const ChatBlock = ({
|
|||||||
step: currentStep,
|
step: currentStep,
|
||||||
context: {
|
context: {
|
||||||
apiHost,
|
apiHost,
|
||||||
typebotId: typebot.id,
|
typebotId: typebot.typebotId,
|
||||||
indices: { blockIndex, stepIndex: currentStepIndex },
|
blockId: currentStep.blockId,
|
||||||
|
stepId: currentStep.id,
|
||||||
variables: typebot.variables,
|
variables: typebot.variables,
|
||||||
isPreview,
|
isPreview,
|
||||||
updateVariableValue,
|
updateVariableValue,
|
||||||
|
|||||||
@@ -82,7 +82,6 @@ export const ConversationContainer = ({
|
|||||||
key={displayedBlock.block.id + idx}
|
key={displayedBlock.block.id + idx}
|
||||||
steps={displayedBlock.block.steps}
|
steps={displayedBlock.block.steps}
|
||||||
startStepIndex={displayedBlock.startStepIndex}
|
startStepIndex={displayedBlock.startStepIndex}
|
||||||
blockIndex={idx}
|
|
||||||
onScroll={autoScrollToBottom}
|
onScroll={autoScrollToBottom}
|
||||||
onBlockEnd={displayNextBlock}
|
onBlockEnd={displayNextBlock}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -20,11 +20,11 @@ import { parseVariables, parseVariablesInObject } from './variable'
|
|||||||
|
|
||||||
const safeEval = eval
|
const safeEval = eval
|
||||||
|
|
||||||
type Indices = { blockIndex: number; stepIndex: number }
|
|
||||||
type IntegrationContext = {
|
type IntegrationContext = {
|
||||||
apiHost: string
|
apiHost: string
|
||||||
typebotId: string
|
typebotId: string
|
||||||
indices: Indices
|
blockId: string
|
||||||
|
stepId: string
|
||||||
isPreview: boolean
|
isPreview: boolean
|
||||||
variables: Variable[]
|
variables: Variable[]
|
||||||
updateVariableValue: (variableId: string, value: string) => void
|
updateVariableValue: (variableId: string, value: string) => void
|
||||||
@@ -153,7 +153,8 @@ const parseCellValues = (
|
|||||||
const executeWebhook = async (
|
const executeWebhook = async (
|
||||||
step: WebhookStep,
|
step: WebhookStep,
|
||||||
{
|
{
|
||||||
indices,
|
blockId,
|
||||||
|
stepId,
|
||||||
variables,
|
variables,
|
||||||
updateVariableValue,
|
updateVariableValue,
|
||||||
typebotId,
|
typebotId,
|
||||||
@@ -161,9 +162,8 @@ const executeWebhook = async (
|
|||||||
}: IntegrationContext
|
}: IntegrationContext
|
||||||
) => {
|
) => {
|
||||||
if (!step.webhook) return step.outgoingEdgeId
|
if (!step.webhook) return step.outgoingEdgeId
|
||||||
const { blockIndex, stepIndex } = indices
|
|
||||||
const { data, error } = await sendRequest({
|
const { data, error } = await sendRequest({
|
||||||
url: `${apiHost}/api/typebots/${typebotId}/blocks/${blockIndex}/steps/${stepIndex}/executeWebhook`,
|
url: `${apiHost}/api/typebots/${typebotId}/blocks/${blockId}/steps/${stepId}/executeWebhook`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: {
|
body: {
|
||||||
variables,
|
variables,
|
||||||
|
|||||||
Reference in New Issue
Block a user