Feat: Add DynamicStringForm
This commit is contained in:
parent
bae5e86ad6
commit
f317c5116b
5 changed files with 266 additions and 6 deletions
|
|
@ -953,3 +953,11 @@ export enum WebhookExecutionMode {
|
|||
Immediately = 'Immediately',
|
||||
Streaming = 'Streaming',
|
||||
}
|
||||
|
||||
export enum WebhookSecurityAuthType {
|
||||
None = 'none',
|
||||
Token = 'token',
|
||||
Basic = 'basic',
|
||||
Jwt = 'jwt',
|
||||
Hmac = 'hmac',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
});
|
||||
|
||||
|
|
|
|||
152
web/src/pages/agent/form/begin-form/webhook/auth.tsx
Normal file
152
web/src/pages/agent/form/begin-form/webhook/auth.tsx
Normal file
|
|
@ -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(
|
||||
() => (
|
||||
<>
|
||||
<RAGFlowFormItem
|
||||
name="security.token.token_header"
|
||||
label={t('flow.webhook.tokenHeader')}
|
||||
>
|
||||
<Input></Input>
|
||||
</RAGFlowFormItem>
|
||||
<RAGFlowFormItem
|
||||
name="security.token.token_value"
|
||||
label={t('flow.webhook.tokenValue')}
|
||||
>
|
||||
<Input></Input>
|
||||
</RAGFlowFormItem>
|
||||
</>
|
||||
),
|
||||
[t],
|
||||
);
|
||||
|
||||
const renderBasicAuth = useCallback(
|
||||
() => (
|
||||
<>
|
||||
<RAGFlowFormItem
|
||||
name="security.basic_auth.username"
|
||||
label={t('flow.webhook.username')}
|
||||
>
|
||||
<Input></Input>
|
||||
</RAGFlowFormItem>
|
||||
<RAGFlowFormItem
|
||||
name="security.basic_auth.password"
|
||||
label={t('flow.webhook.password')}
|
||||
>
|
||||
<Input></Input>
|
||||
</RAGFlowFormItem>
|
||||
</>
|
||||
),
|
||||
[t],
|
||||
);
|
||||
|
||||
const renderJwtAuth = useCallback(
|
||||
() => (
|
||||
<>
|
||||
<RAGFlowFormItem
|
||||
name="security.jwt.algorithm"
|
||||
label={t('flow.webhook.algorithm')}
|
||||
>
|
||||
<SelectWithSearch options={AlgorithmOptions}></SelectWithSearch>
|
||||
</RAGFlowFormItem>
|
||||
<RAGFlowFormItem
|
||||
name="security.jwt.secret"
|
||||
label={t('flow.webhook.secret')}
|
||||
>
|
||||
<Input></Input>
|
||||
</RAGFlowFormItem>
|
||||
<RAGFlowFormItem
|
||||
name="security.jwt.issuer"
|
||||
label={t('flow.webhook.issuer')}
|
||||
>
|
||||
<Input></Input>
|
||||
</RAGFlowFormItem>
|
||||
<RAGFlowFormItem
|
||||
name="security.jwt.audience"
|
||||
label={t('flow.webhook.audience')}
|
||||
>
|
||||
<Input></Input>
|
||||
</RAGFlowFormItem>
|
||||
<RAGFlowFormItem
|
||||
name="security.jwt.required_claims"
|
||||
label={t('flow.webhook.requiredClaims')}
|
||||
>
|
||||
<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],
|
||||
);
|
||||
|
||||
const AuthMap = {
|
||||
[WebhookSecurityAuthType.Token]: renderTokenAuth,
|
||||
[WebhookSecurityAuthType.Basic]: renderBasicAuth,
|
||||
[WebhookSecurityAuthType.Jwt]: renderJwtAuth,
|
||||
[WebhookSecurityAuthType.Hmac]: renderHmacAuth,
|
||||
[WebhookSecurityAuthType.None]: () => null,
|
||||
};
|
||||
|
||||
return AuthMap[
|
||||
(authType ?? WebhookSecurityAuthType.None) as WebhookSecurityAuthType
|
||||
]();
|
||||
}
|
||||
|
|
@ -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)}
|
||||
></SelectWithSearch>
|
||||
</RAGFlowFormItem>
|
||||
<RAGFlowFormItem name="security" label={t('flow.webhook.security')}>
|
||||
<Textarea></Textarea>
|
||||
</RAGFlowFormItem>
|
||||
<section className="space-y-5 bg-bg-card p-2 rounded">
|
||||
<RAGFlowFormItem
|
||||
name="security.auth_type"
|
||||
label={t('flow.webhook.authType')}
|
||||
>
|
||||
<SelectWithSearch
|
||||
options={buildOptions(WebhookSecurityAuthType)}
|
||||
></SelectWithSearch>
|
||||
</RAGFlowFormItem>
|
||||
<Auth></Auth>
|
||||
<RAGFlowFormItem
|
||||
name="security.rate_limit.limit"
|
||||
label={t('flow.webhook.limit')}
|
||||
>
|
||||
<Input type="number"></Input>
|
||||
</RAGFlowFormItem>
|
||||
<RAGFlowFormItem
|
||||
name="security.rate_limit.per"
|
||||
label={t('flow.webhook.per')}
|
||||
>
|
||||
<SelectWithSearch options={RateLimitPerOptions}></SelectWithSearch>
|
||||
</RAGFlowFormItem>
|
||||
<RAGFlowFormItem
|
||||
name="security.max_body_size"
|
||||
label={t('flow.webhook.maxBodySize')}
|
||||
>
|
||||
<Input></Input>
|
||||
</RAGFlowFormItem>
|
||||
<DynamicStringForm
|
||||
name="security.ip_whitelist"
|
||||
label={t('flow.webhook.ipWhitelist')}
|
||||
></DynamicStringForm>
|
||||
</section>
|
||||
<RAGFlowFormItem name="schema" label={t('flow.webhook.schema')}>
|
||||
<Textarea></Textarea>
|
||||
</RAGFlowFormItem>
|
||||
46
web/src/pages/agent/form/components/dynamic-string-form.tsx
Normal file
46
web/src/pages/agent/form/components/dynamic-string-form.tsx
Normal file
|
|
@ -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 (
|
||||
<section>
|
||||
<DynamicFormHeader
|
||||
label={label}
|
||||
onClick={() => append({ value: '' })}
|
||||
></DynamicFormHeader>
|
||||
<div className="space-y-4">
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.id} className="flex items-center gap-2">
|
||||
<RAGFlowFormItem
|
||||
name={`${name}.${index}.value`}
|
||||
label="delimiter"
|
||||
labelClassName="!hidden"
|
||||
className="flex-1 !m-0"
|
||||
>
|
||||
<Input className="!m-0"></Input>
|
||||
</RAGFlowFormItem>
|
||||
<Button
|
||||
type="button"
|
||||
variant={'ghost'}
|
||||
onClick={() => remove(index)}
|
||||
>
|
||||
<Trash2 />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue