Feat: Remove HMAC from the webhook #10427
This commit is contained in:
parent
2a0f835ffe
commit
6eedd0a237
12 changed files with 140 additions and 82 deletions
|
|
@ -7,6 +7,7 @@ interface NumberInputProps {
|
||||||
onChange?: (value: number) => void;
|
onChange?: (value: number) => void;
|
||||||
height?: number | string;
|
height?: number | string;
|
||||||
min?: number;
|
min?: number;
|
||||||
|
max?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NumberInput: React.FC<NumberInputProps> = ({
|
const NumberInput: React.FC<NumberInputProps> = ({
|
||||||
|
|
@ -15,6 +16,7 @@ const NumberInput: React.FC<NumberInputProps> = ({
|
||||||
onChange,
|
onChange,
|
||||||
height,
|
height,
|
||||||
min = 0,
|
min = 0,
|
||||||
|
max = Infinity,
|
||||||
}) => {
|
}) => {
|
||||||
const [value, setValue] = useState<number>(() => {
|
const [value, setValue] = useState<number>(() => {
|
||||||
return initialValue ?? 0;
|
return initialValue ?? 0;
|
||||||
|
|
@ -34,6 +36,9 @@ const NumberInput: React.FC<NumberInputProps> = ({
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleIncrement = () => {
|
const handleIncrement = () => {
|
||||||
|
if (value > max - 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setValue(value + 1);
|
setValue(value + 1);
|
||||||
onChange?.(value + 1);
|
onChange?.(value + 1);
|
||||||
};
|
};
|
||||||
|
|
@ -41,6 +46,9 @@ const NumberInput: React.FC<NumberInputProps> = ({
|
||||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const newValue = Number(e.target.value);
|
const newValue = Number(e.target.value);
|
||||||
if (!isNaN(newValue)) {
|
if (!isNaN(newValue)) {
|
||||||
|
if (newValue > max) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
setValue(newValue);
|
setValue(newValue);
|
||||||
onChange?.(newValue);
|
onChange?.(newValue);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,11 @@ import {
|
||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { ReactNode, cloneElement, isValidElement } from 'react';
|
import { ReactNode, cloneElement, isValidElement } from 'react';
|
||||||
import { ControllerRenderProps, useFormContext } from 'react-hook-form';
|
import {
|
||||||
|
ControllerRenderProps,
|
||||||
|
UseControllerProps,
|
||||||
|
useFormContext,
|
||||||
|
} from 'react-hook-form';
|
||||||
|
|
||||||
type RAGFlowFormItemProps = {
|
type RAGFlowFormItemProps = {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
@ -18,7 +22,7 @@ type RAGFlowFormItemProps = {
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
labelClassName?: string;
|
labelClassName?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
};
|
} & Pick<UseControllerProps<any>, 'rules'>;
|
||||||
|
|
||||||
export function RAGFlowFormItem({
|
export function RAGFlowFormItem({
|
||||||
name,
|
name,
|
||||||
|
|
@ -29,11 +33,13 @@ export function RAGFlowFormItem({
|
||||||
required = false,
|
required = false,
|
||||||
labelClassName,
|
labelClassName,
|
||||||
className,
|
className,
|
||||||
|
rules,
|
||||||
}: RAGFlowFormItemProps) {
|
}: RAGFlowFormItemProps) {
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
return (
|
return (
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
|
rules={rules}
|
||||||
name={name}
|
name={name}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem
|
<FormItem
|
||||||
|
|
|
||||||
|
|
@ -195,7 +195,7 @@ export enum SwitchLogicOperator {
|
||||||
Or = 'or',
|
Or = 'or',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WebhookAlgorithmList = [
|
export const WebhookJWTAlgorithmList = [
|
||||||
'hs256',
|
'hs256',
|
||||||
'hs384',
|
'hs384',
|
||||||
'hs512',
|
'hs512',
|
||||||
|
|
|
||||||
|
|
@ -1024,12 +1024,18 @@ export enum WebhookSecurityAuthType {
|
||||||
Token = 'token',
|
Token = 'token',
|
||||||
Basic = 'basic',
|
Basic = 'basic',
|
||||||
Jwt = 'jwt',
|
Jwt = 'jwt',
|
||||||
Hmac = 'hmac',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const RateLimitPerList = ['minute', 'hour', 'day'];
|
export enum WebhookRateLimitPer {
|
||||||
|
Second = 'second',
|
||||||
|
Minute = 'minute',
|
||||||
|
Hour = 'hour',
|
||||||
|
Day = 'day',
|
||||||
|
}
|
||||||
|
|
||||||
export const WebhookMaxBodySize = ['10MB', '50MB', '100MB', '1000MB'];
|
export const RateLimitPerList = Object.values(WebhookRateLimitPer);
|
||||||
|
|
||||||
|
export const WebhookMaxBodySize = ['1MB', '5MB', '10MB'];
|
||||||
|
|
||||||
export enum WebhookRequestParameters {
|
export enum WebhookRequestParameters {
|
||||||
File = VariableType.File,
|
File = VariableType.File,
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ function BeginForm({ node }: INextOperatorForm) {
|
||||||
const form = useForm({
|
const form = useForm({
|
||||||
defaultValues: values,
|
defaultValues: values,
|
||||||
resolver: zodResolver(BeginFormSchema),
|
resolver: zodResolver(BeginFormSchema),
|
||||||
|
mode: 'onChange',
|
||||||
});
|
});
|
||||||
|
|
||||||
useWatchFormChange(node?.id, form);
|
useWatchFormChange(node?.id, form);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { WebhookAlgorithmList } from '@/constants/agent';
|
import { WebhookJWTAlgorithmList } from '@/constants/agent';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
export const BeginFormSchema = z.object({
|
export const BeginFormSchema = z.object({
|
||||||
|
|
@ -30,7 +30,14 @@ export const BeginFormSchema = z.object({
|
||||||
max_body_size: z.string(),
|
max_body_size: z.string(),
|
||||||
jwt: z
|
jwt: z
|
||||||
.object({
|
.object({
|
||||||
algorithm: z.string().default(WebhookAlgorithmList[0]).optional(),
|
algorithm: z.string().default(WebhookJWTAlgorithmList[0]).optional(),
|
||||||
|
required_claims: z.array(z.object({ value: z.string() })),
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
hmac: z
|
||||||
|
.object({
|
||||||
|
header: z.string().optional(),
|
||||||
|
secret: z.string().optional(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,11 @@ import { useCallback } from 'react';
|
||||||
import { UseFormReturn } from 'react-hook-form';
|
import { UseFormReturn } from 'react-hook-form';
|
||||||
import {
|
import {
|
||||||
AgentDialogueMode,
|
AgentDialogueMode,
|
||||||
RateLimitPerList,
|
|
||||||
WebhookContentType,
|
WebhookContentType,
|
||||||
WebhookExecutionMode,
|
WebhookExecutionMode,
|
||||||
WebhookMaxBodySize,
|
WebhookMaxBodySize,
|
||||||
WebhookMethod,
|
WebhookMethod,
|
||||||
|
WebhookRateLimitPer,
|
||||||
WebhookSecurityAuthType,
|
WebhookSecurityAuthType,
|
||||||
} from '../../constant';
|
} from '../../constant';
|
||||||
|
|
||||||
|
|
@ -14,7 +14,7 @@ const initialFormValuesMap = {
|
||||||
methods: [WebhookMethod.Get],
|
methods: [WebhookMethod.Get],
|
||||||
schema: {},
|
schema: {},
|
||||||
'security.auth_type': WebhookSecurityAuthType.Basic,
|
'security.auth_type': WebhookSecurityAuthType.Basic,
|
||||||
'security.rate_limit.per': RateLimitPerList[0],
|
'security.rate_limit.per': WebhookRateLimitPer.Second,
|
||||||
'security.rate_limit.limit': 10,
|
'security.rate_limit.limit': 10,
|
||||||
'security.max_body_size': WebhookMaxBodySize[0],
|
'security.max_body_size': WebhookMaxBodySize[0],
|
||||||
'response.status': 200,
|
'response.status': 200,
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
import { WebhookAlgorithmList } from '@/constants/agent';
|
import { WebhookJWTAlgorithmList } from '@/constants/agent';
|
||||||
import { WebhookSecurityAuthType } from '@/pages/agent/constant';
|
import { WebhookSecurityAuthType } from '@/pages/agent/constant';
|
||||||
import { buildOptions } from '@/utils/form';
|
import { buildOptions } from '@/utils/form';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useFormContext, useWatch } from 'react-hook-form';
|
import { useFormContext, useWatch } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { DynamicStringForm } from '../../components/dynamic-string-form';
|
||||||
|
|
||||||
const AlgorithmOptions = buildOptions(WebhookAlgorithmList);
|
const AlgorithmOptions = buildOptions(WebhookJWTAlgorithmList);
|
||||||
|
|
||||||
const RequiredClaimsOptions = buildOptions(['exp', 'sub']);
|
|
||||||
|
|
||||||
export function Auth() {
|
export function Auth() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
@ -88,38 +87,10 @@ export function Auth() {
|
||||||
>
|
>
|
||||||
<Input></Input>
|
<Input></Input>
|
||||||
</RAGFlowFormItem>
|
</RAGFlowFormItem>
|
||||||
<RAGFlowFormItem
|
<DynamicStringForm
|
||||||
name="security.jwt.required_claims"
|
name="security.jwt.required_claims"
|
||||||
label={t('flow.webhook.requiredClaims')}
|
label={t('flow.webhook.requiredClaims')}
|
||||||
>
|
></DynamicStringForm>
|
||||||
<SelectWithSearch options={RequiredClaimsOptions}></SelectWithSearch>
|
|
||||||
</RAGFlowFormItem>
|
|
||||||
</>
|
|
||||||
),
|
|
||||||
[t],
|
|
||||||
);
|
|
||||||
|
|
||||||
const renderHmacAuth = useCallback(
|
|
||||||
() => (
|
|
||||||
<>
|
|
||||||
<RAGFlowFormItem
|
|
||||||
name="security.hmac.header"
|
|
||||||
label={t('flow.webhook.header')}
|
|
||||||
>
|
|
||||||
<Input></Input>
|
|
||||||
</RAGFlowFormItem>
|
|
||||||
<RAGFlowFormItem
|
|
||||||
name="security.hmac.secret"
|
|
||||||
label={t('flow.webhook.secret')}
|
|
||||||
>
|
|
||||||
<Input></Input>
|
|
||||||
</RAGFlowFormItem>
|
|
||||||
<RAGFlowFormItem
|
|
||||||
name="security.hmac.algorithm"
|
|
||||||
label={t('flow.webhook.algorithm')}
|
|
||||||
>
|
|
||||||
<SelectWithSearch options={AlgorithmOptions}></SelectWithSearch>
|
|
||||||
</RAGFlowFormItem>
|
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
[t],
|
[t],
|
||||||
|
|
@ -129,11 +100,14 @@ export function Auth() {
|
||||||
[WebhookSecurityAuthType.Token]: renderTokenAuth,
|
[WebhookSecurityAuthType.Token]: renderTokenAuth,
|
||||||
[WebhookSecurityAuthType.Basic]: renderBasicAuth,
|
[WebhookSecurityAuthType.Basic]: renderBasicAuth,
|
||||||
[WebhookSecurityAuthType.Jwt]: renderJwtAuth,
|
[WebhookSecurityAuthType.Jwt]: renderJwtAuth,
|
||||||
[WebhookSecurityAuthType.Hmac]: renderHmacAuth,
|
|
||||||
[WebhookSecurityAuthType.None]: () => null,
|
[WebhookSecurityAuthType.None]: () => null,
|
||||||
};
|
};
|
||||||
|
|
||||||
return AuthMap[
|
return (
|
||||||
(authType ?? WebhookSecurityAuthType.None) as WebhookSecurityAuthType
|
<div key={`auth-${authType}`} className="space-y-5">
|
||||||
]();
|
{AuthMap[
|
||||||
|
(authType ?? WebhookSecurityAuthType.None) as WebhookSecurityAuthType
|
||||||
|
]()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,15 +6,10 @@ import { Separator } from '@/components/ui/separator';
|
||||||
import { Switch } from '@/components/ui/switch';
|
import { Switch } from '@/components/ui/switch';
|
||||||
import { buildOptions } from '@/utils/form';
|
import { buildOptions } from '@/utils/form';
|
||||||
import { loader } from '@monaco-editor/react';
|
import { loader } from '@monaco-editor/react';
|
||||||
import { omit } from 'lodash';
|
|
||||||
import { X } from 'lucide-react';
|
import { X } from 'lucide-react';
|
||||||
import { ReactNode } from 'react';
|
import { ReactNode } from 'react';
|
||||||
import { useFieldArray, useFormContext, useWatch } from 'react-hook-form';
|
import { useFieldArray, useFormContext } from 'react-hook-form';
|
||||||
import {
|
import { TypesWithArray, WebhookRequestParameters } from '../../../constant';
|
||||||
TypesWithArray,
|
|
||||||
WebhookContentType,
|
|
||||||
WebhookRequestParameters,
|
|
||||||
} from '../../../constant';
|
|
||||||
import { DynamicFormHeader } from '../../components/dynamic-fom-header';
|
import { DynamicFormHeader } from '../../components/dynamic-fom-header';
|
||||||
|
|
||||||
loader.config({ paths: { vs: '/vs' } });
|
loader.config({ paths: { vs: '/vs' } });
|
||||||
|
|
@ -28,16 +23,9 @@ type SelectKeysProps = {
|
||||||
requiredField?: string;
|
requiredField?: string;
|
||||||
nodeId?: string;
|
nodeId?: string;
|
||||||
isObject?: boolean;
|
isObject?: boolean;
|
||||||
|
operatorList: WebhookRequestParameters[];
|
||||||
};
|
};
|
||||||
|
|
||||||
function buildParametersOptions(isObject: boolean) {
|
|
||||||
const list = isObject
|
|
||||||
? WebhookRequestParameters
|
|
||||||
: omit(WebhookRequestParameters, ['File']);
|
|
||||||
|
|
||||||
return buildOptions(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DynamicRequest({
|
export function DynamicRequest({
|
||||||
name,
|
name,
|
||||||
label,
|
label,
|
||||||
|
|
@ -45,15 +33,9 @@ export function DynamicRequest({
|
||||||
keyField = 'key',
|
keyField = 'key',
|
||||||
operatorField = 'type',
|
operatorField = 'type',
|
||||||
requiredField = 'required',
|
requiredField = 'required',
|
||||||
isObject = false,
|
operatorList,
|
||||||
}: SelectKeysProps) {
|
}: SelectKeysProps) {
|
||||||
const form = useFormContext();
|
const form = useFormContext();
|
||||||
const contentType = useWatch({
|
|
||||||
name: 'content_types',
|
|
||||||
control: form.control,
|
|
||||||
});
|
|
||||||
const isFormDataContentType =
|
|
||||||
contentType === WebhookContentType.MultipartFormData;
|
|
||||||
|
|
||||||
const { fields, remove, append } = useFieldArray({
|
const { fields, remove, append } = useFieldArray({
|
||||||
name: name,
|
name: name,
|
||||||
|
|
@ -94,9 +76,7 @@ export function DynamicRequest({
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
field.onChange(val);
|
field.onChange(val);
|
||||||
}}
|
}}
|
||||||
options={buildParametersOptions(
|
options={buildOptions(operatorList)}
|
||||||
isObject && isFormDataContentType,
|
|
||||||
)}
|
|
||||||
></SelectWithSearch>
|
></SelectWithSearch>
|
||||||
)}
|
)}
|
||||||
</RAGFlowFormItem>
|
</RAGFlowFormItem>
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,20 @@
|
||||||
import { Collapse } from '@/components/collapse';
|
import { Collapse } from '@/components/collapse';
|
||||||
import CopyToClipboard from '@/components/copy-to-clipboard';
|
import CopyToClipboard from '@/components/copy-to-clipboard';
|
||||||
|
import NumberInput from '@/components/originui/number-input';
|
||||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import { MultiSelect } from '@/components/ui/multi-select';
|
import { MultiSelect } from '@/components/ui/multi-select';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { buildOptions } from '@/utils/form';
|
import { buildOptions } from '@/utils/form';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useFormContext, useWatch } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useParams } from 'umi';
|
import { useParams } from 'umi';
|
||||||
import {
|
import {
|
||||||
RateLimitPerList,
|
RateLimitPerList,
|
||||||
WebhookMaxBodySize,
|
WebhookMaxBodySize,
|
||||||
WebhookMethod,
|
WebhookMethod,
|
||||||
|
WebhookRateLimitPer,
|
||||||
WebhookSecurityAuthType,
|
WebhookSecurityAuthType,
|
||||||
} from '../../../constant';
|
} from '../../../constant';
|
||||||
import { DynamicStringForm } from '../../components/dynamic-string-form';
|
import { DynamicStringForm } from '../../components/dynamic-string-form';
|
||||||
|
|
@ -21,9 +24,26 @@ import { WebhookResponse } from './response';
|
||||||
|
|
||||||
const RateLimitPerOptions = buildOptions(RateLimitPerList);
|
const RateLimitPerOptions = buildOptions(RateLimitPerList);
|
||||||
|
|
||||||
|
const RequestLimitMap = {
|
||||||
|
[WebhookRateLimitPer.Second]: 100,
|
||||||
|
[WebhookRateLimitPer.Minute]: 1000,
|
||||||
|
[WebhookRateLimitPer.Hour]: 10000,
|
||||||
|
[WebhookRateLimitPer.Day]: 100000,
|
||||||
|
};
|
||||||
|
|
||||||
export function WebHook() {
|
export function WebHook() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
|
const form = useFormContext();
|
||||||
|
|
||||||
|
const rateLimitPer = useWatch({
|
||||||
|
name: 'security.rate_limit.per',
|
||||||
|
control: form.control,
|
||||||
|
});
|
||||||
|
|
||||||
|
const getLimitRateLimitPerMax = useCallback((rateLimitPer: string) => {
|
||||||
|
return RequestLimitMap[rateLimitPer as keyof typeof RequestLimitMap] ?? 100;
|
||||||
|
}, []);
|
||||||
|
|
||||||
const text = `${location.protocol}//${location.host}/api/v1/webhook/${id}`;
|
const text = `${location.protocol}//${location.host}/api/v1/webhook/${id}`;
|
||||||
|
|
||||||
|
|
@ -61,13 +81,28 @@ export function WebHook() {
|
||||||
name="security.rate_limit.limit"
|
name="security.rate_limit.limit"
|
||||||
label={t('flow.webhook.limit')}
|
label={t('flow.webhook.limit')}
|
||||||
>
|
>
|
||||||
<Input type="number"></Input>
|
<NumberInput
|
||||||
|
max={getLimitRateLimitPerMax(rateLimitPer)}
|
||||||
|
className="w-full"
|
||||||
|
></NumberInput>
|
||||||
</RAGFlowFormItem>
|
</RAGFlowFormItem>
|
||||||
<RAGFlowFormItem
|
<RAGFlowFormItem
|
||||||
name="security.rate_limit.per"
|
name="security.rate_limit.per"
|
||||||
label={t('flow.webhook.per')}
|
label={t('flow.webhook.per')}
|
||||||
>
|
>
|
||||||
<SelectWithSearch options={RateLimitPerOptions}></SelectWithSearch>
|
{(field) => (
|
||||||
|
<SelectWithSearch
|
||||||
|
options={RateLimitPerOptions}
|
||||||
|
value={field.value}
|
||||||
|
onChange={(val) => {
|
||||||
|
field.onChange(val);
|
||||||
|
form.setValue(
|
||||||
|
'security.rate_limit.limit',
|
||||||
|
getLimitRateLimitPerMax(val),
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
></SelectWithSearch>
|
||||||
|
)}
|
||||||
</RAGFlowFormItem>
|
</RAGFlowFormItem>
|
||||||
<RAGFlowFormItem
|
<RAGFlowFormItem
|
||||||
name="security.max_body_size"
|
name="security.max_body_size"
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,40 @@
|
||||||
import { Collapse } from '@/components/collapse';
|
import { Collapse } from '@/components/collapse';
|
||||||
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
import { SelectWithSearch } from '@/components/originui/select-with-search';
|
||||||
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
import { RAGFlowFormItem } from '@/components/ragflow-form';
|
||||||
import { WebhookContentType } from '@/pages/agent/constant';
|
import {
|
||||||
|
WebhookContentType,
|
||||||
|
WebhookRequestParameters,
|
||||||
|
} from '@/pages/agent/constant';
|
||||||
import { buildOptions } from '@/utils/form';
|
import { buildOptions } from '@/utils/form';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { useFormContext, useWatch } from 'react-hook-form';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { DynamicRequest } from './dynamic-request';
|
import { DynamicRequest } from './dynamic-request';
|
||||||
|
|
||||||
export function WebhookRequestSchema() {
|
export function WebhookRequestSchema() {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const form = useFormContext();
|
||||||
|
const contentType = useWatch({
|
||||||
|
name: 'content_types',
|
||||||
|
control: form.control,
|
||||||
|
});
|
||||||
|
const isFormDataContentType =
|
||||||
|
contentType === WebhookContentType.MultipartFormData;
|
||||||
|
|
||||||
|
const bodyOperatorList = useMemo(() => {
|
||||||
|
return isFormDataContentType
|
||||||
|
? [
|
||||||
|
WebhookRequestParameters.String,
|
||||||
|
WebhookRequestParameters.Number,
|
||||||
|
WebhookRequestParameters.Boolean,
|
||||||
|
WebhookRequestParameters.File,
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
WebhookRequestParameters.String,
|
||||||
|
WebhookRequestParameters.Number,
|
||||||
|
WebhookRequestParameters.Boolean,
|
||||||
|
];
|
||||||
|
}, [isFormDataContentType]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Collapse title={<div>{t('flow.webhook.schema')}</div>}>
|
<Collapse title={<div>{t('flow.webhook.schema')}</div>}>
|
||||||
|
|
@ -23,14 +50,20 @@ export function WebhookRequestSchema() {
|
||||||
<DynamicRequest
|
<DynamicRequest
|
||||||
name="schema.query"
|
name="schema.query"
|
||||||
label={t('flow.webhook.queryParameters')}
|
label={t('flow.webhook.queryParameters')}
|
||||||
|
operatorList={[
|
||||||
|
WebhookRequestParameters.String,
|
||||||
|
WebhookRequestParameters.Number,
|
||||||
|
WebhookRequestParameters.Boolean,
|
||||||
|
]}
|
||||||
></DynamicRequest>
|
></DynamicRequest>
|
||||||
<DynamicRequest
|
<DynamicRequest
|
||||||
name="schema.headers"
|
name="schema.headers"
|
||||||
label={t('flow.webhook.headerParameters')}
|
label={t('flow.webhook.headerParameters')}
|
||||||
|
operatorList={[WebhookRequestParameters.String]}
|
||||||
></DynamicRequest>
|
></DynamicRequest>
|
||||||
<DynamicRequest
|
<DynamicRequest
|
||||||
name="schema.body"
|
name="schema.body"
|
||||||
isObject
|
operatorList={bodyOperatorList}
|
||||||
label={t('flow.webhook.requestBodyParameters')}
|
label={t('flow.webhook.requestBodyParameters')}
|
||||||
></DynamicRequest>
|
></DynamicRequest>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import {
|
||||||
NodeHandleId,
|
NodeHandleId,
|
||||||
Operator,
|
Operator,
|
||||||
TypesWithArray,
|
TypesWithArray,
|
||||||
|
WebhookSecurityAuthType,
|
||||||
} from './constant';
|
} from './constant';
|
||||||
import { BeginFormSchemaType } from './form/begin-form/schema';
|
import { BeginFormSchemaType } from './form/begin-form/schema';
|
||||||
import { DataOperationsFormSchemaType } from './form/data-operations-form';
|
import { DataOperationsFormSchemaType } from './form/data-operations-form';
|
||||||
|
|
@ -348,13 +349,20 @@ function transformRequestSchemaToJsonschema(
|
||||||
|
|
||||||
function transformBeginParams(params: BeginFormSchemaType) {
|
function transformBeginParams(params: BeginFormSchemaType) {
|
||||||
if (params.mode === AgentDialogueMode.Webhook) {
|
if (params.mode === AgentDialogueMode.Webhook) {
|
||||||
|
const nextSecurity: Record<string, any> = {
|
||||||
|
...params.security,
|
||||||
|
ip_whitelist: params.security?.ip_whitelist.map((x) => x.value),
|
||||||
|
};
|
||||||
|
if (params.security?.auth_type === WebhookSecurityAuthType.Jwt) {
|
||||||
|
nextSecurity.jwt = {
|
||||||
|
...nextSecurity.jwt,
|
||||||
|
required_claims: nextSecurity.jwt?.required_claims.map((x) => x.value),
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
...params,
|
...params,
|
||||||
schema: transformRequestSchemaToJsonschema(params.schema),
|
schema: transformRequestSchemaToJsonschema(params.schema),
|
||||||
security: {
|
security: nextSecurity,
|
||||||
...params.security,
|
|
||||||
ip_whitelist: params.security?.ip_whitelist.map((x) => x.value),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue