From f317c5116b3ff8a9890669780558bbccb5ca3833 Mon Sep 17 00:00:00 2001 From: bill Date: Mon, 8 Dec 2025 19:20:46 +0800 Subject: [PATCH] Feat: Add DynamicStringForm --- web/src/pages/agent/constant/index.tsx | 8 + web/src/pages/agent/form/begin-form/index.tsx | 22 ++- .../agent/form/begin-form/webhook/auth.tsx | 152 ++++++++++++++++++ .../{webhook.tsx => webhook/index.tsx} | 44 ++++- .../form/components/dynamic-string-form.tsx | 46 ++++++ 5 files changed, 266 insertions(+), 6 deletions(-) create mode 100644 web/src/pages/agent/form/begin-form/webhook/auth.tsx rename web/src/pages/agent/form/begin-form/{webhook.tsx => webhook/index.tsx} (50%) create mode 100644 web/src/pages/agent/form/components/dynamic-string-form.tsx diff --git a/web/src/pages/agent/constant/index.tsx b/web/src/pages/agent/constant/index.tsx index 66082c30f..0ee8dec89 100644 --- a/web/src/pages/agent/constant/index.tsx +++ b/web/src/pages/agent/constant/index.tsx @@ -953,3 +953,11 @@ export enum WebhookExecutionMode { Immediately = 'Immediately', Streaming = 'Streaming', } + +export enum WebhookSecurityAuthType { + None = 'none', + Token = 'token', + Basic = 'basic', + Jwt = 'jwt', + Hmac = 'hmac', +} diff --git a/web/src/pages/agent/form/begin-form/index.tsx b/web/src/pages/agent/form/begin-form/index.tsx index 18e2b2454..7aa7a09ae 100644 --- a/web/src/pages/agent/form/begin-form/index.tsx +++ b/web/src/pages/agent/form/begin-form/index.tsx @@ -57,9 +57,27 @@ function BeginForm({ node }: INextOperatorForm) { .optional(), methods: z.string().optional(), content_types: z.string().optional(), - security: z.string().optional(), + security: z + .object({ + auth_type: z.string(), + ip_whitelist: z.array(z.object({ value: z.string() })), + rate_limit: z.object({ + limit: z.number(), + per: z.string().optional(), + }), + max_body_size: z.string(), + }) + .optional(), schema: z.string().optional(), - response: z.string().optional(), + response: z + .object({ + status: z.number(), + headers: z.array(z.object({ key: z.string(), value: z.string() })), + body_template: z.array( + z.object({ key: z.string(), value: z.string() }), + ), + }) + .optional(), execution_mode: z.string().optional(), }); diff --git a/web/src/pages/agent/form/begin-form/webhook/auth.tsx b/web/src/pages/agent/form/begin-form/webhook/auth.tsx new file mode 100644 index 000000000..3f7da9855 --- /dev/null +++ b/web/src/pages/agent/form/begin-form/webhook/auth.tsx @@ -0,0 +1,152 @@ +import { SelectWithSearch } from '@/components/originui/select-with-search'; +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { Input } from '@/components/ui/input'; +import { WebhookSecurityAuthType } from '@/pages/agent/constant'; +import { buildOptions } from '@/utils/form'; +import { useCallback } from 'react'; +import { useFormContext, useWatch } from 'react-hook-form'; +import { useTranslation } from 'react-i18next'; + +const AlgorithmOptions = buildOptions([ + 'hs256', + 'hs384', + 'hs512', + 'rs256', + 'rs384', + 'rs512', + 'es256', + 'es384', + 'es512', + 'ps256', + 'ps384', + 'ps512', + 'none', +]); + +const RequiredClaimsOptions = buildOptions(['exp', 'sub']); + +export function Auth() { + const { t } = useTranslation(); + const form = useFormContext(); + + const authType = useWatch({ + name: 'security.auth_type', + control: form.control, + }); + + const renderTokenAuth = useCallback( + () => ( + <> + + + + + + + + ), + [t], + ); + + const renderBasicAuth = useCallback( + () => ( + <> + + + + + + + + ), + [t], + ); + + const renderJwtAuth = useCallback( + () => ( + <> + + + + + + + + + + + + + + + + + ), + [t], + ); + + const renderHmacAuth = useCallback( + () => ( + <> + + + + + + + + + + + ), + [t], + ); + + const AuthMap = { + [WebhookSecurityAuthType.Token]: renderTokenAuth, + [WebhookSecurityAuthType.Basic]: renderBasicAuth, + [WebhookSecurityAuthType.Jwt]: renderJwtAuth, + [WebhookSecurityAuthType.Hmac]: renderHmacAuth, + [WebhookSecurityAuthType.None]: () => null, + }; + + return AuthMap[ + (authType ?? WebhookSecurityAuthType.None) as WebhookSecurityAuthType + ](); +} diff --git a/web/src/pages/agent/form/begin-form/webhook.tsx b/web/src/pages/agent/form/begin-form/webhook/index.tsx similarity index 50% rename from web/src/pages/agent/form/begin-form/webhook.tsx rename to web/src/pages/agent/form/begin-form/webhook/index.tsx index 5bb419b06..8c5d9fadf 100644 --- a/web/src/pages/agent/form/begin-form/webhook.tsx +++ b/web/src/pages/agent/form/begin-form/webhook/index.tsx @@ -1,5 +1,6 @@ import { SelectWithSearch } from '@/components/originui/select-with-search'; import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { Input } from '@/components/ui/input'; import { Textarea } from '@/components/ui/textarea'; import { buildOptions } from '@/utils/form'; import { useTranslation } from 'react-i18next'; @@ -7,7 +8,12 @@ import { WebhookContentType, WebhookExecutionMode, WebhookMethod, -} from '../../constant'; + WebhookSecurityAuthType, +} from '../../../constant'; +import { DynamicStringForm } from '../../components/dynamic-string-form'; +import { Auth } from './auth'; + +const RateLimitPerOptions = buildOptions(['minute', 'hour', 'day']); export function WebHook() { const { t } = useTranslation(); @@ -27,9 +33,39 @@ export function WebHook() { options={buildOptions(WebhookContentType)} > - - - +
+ + + + + + + + + + + + + + +
diff --git a/web/src/pages/agent/form/components/dynamic-string-form.tsx b/web/src/pages/agent/form/components/dynamic-string-form.tsx new file mode 100644 index 000000000..224e92310 --- /dev/null +++ b/web/src/pages/agent/form/components/dynamic-string-form.tsx @@ -0,0 +1,46 @@ +import { RAGFlowFormItem } from '@/components/ragflow-form'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Trash2 } from 'lucide-react'; +import { useFieldArray, useFormContext } from 'react-hook-form'; +import { DynamicFormHeader, FormListHeaderProps } from './dynamic-fom-header'; + +type DynamicStringFormProps = { name: string } & FormListHeaderProps; +export function DynamicStringForm({ name, label }: DynamicStringFormProps) { + const form = useFormContext(); + + const { fields, append, remove } = useFieldArray({ + name: name, + control: form.control, + }); + + return ( +
+ append({ value: '' })} + > +
+ {fields.map((field, index) => ( +
+ + + + +
+ ))} +
+
+ ); +}