mirror of
https://github.com/langgenius/dify.git
synced 2026-08-03 11:46:37 +08:00
Remove label, description, and icon fields from SandboxProvider type as they are no longer returned by the backend API. Use i18n translations to display provider labels instead of relying on API response data.
175 lines
6.2 KiB
TypeScript
175 lines
6.2 KiB
TypeScript
'use client'
|
|
|
|
import type { FormRefObject, FormSchema } from '@/app/components/base/form/types'
|
|
import type { SandboxProvider } from '@/types/sandbox-provider'
|
|
import { RiExternalLinkLine, RiLock2Fill } from '@remixicon/react'
|
|
import { memo, useCallback, useMemo, useRef } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import Button from '@/app/components/base/button'
|
|
import { BaseForm } from '@/app/components/base/form/components/base'
|
|
import { FormTypeEnum } from '@/app/components/base/form/types'
|
|
import Modal from '@/app/components/base/modal'
|
|
import { useToastContext } from '@/app/components/base/toast'
|
|
import {
|
|
useDeleteSandboxProviderConfig,
|
|
useSaveSandboxProviderConfig,
|
|
} from '@/service/use-sandbox-provider'
|
|
import { PROVIDER_DOC_LINKS, PROVIDER_LABEL_KEYS, SANDBOX_FIELD_CONFIGS } from './constants'
|
|
import ProviderIcon from './provider-icon'
|
|
|
|
type ConfigModalProps = {
|
|
provider: SandboxProvider
|
|
onClose: () => void
|
|
}
|
|
|
|
const ConfigModal = ({
|
|
provider,
|
|
onClose,
|
|
}: ConfigModalProps) => {
|
|
const { t } = useTranslation()
|
|
const { notify } = useToastContext()
|
|
const formRef = useRef<FormRefObject>(null)
|
|
|
|
const { mutateAsync: saveConfig, isPending: isSaving } = useSaveSandboxProviderConfig()
|
|
const { mutateAsync: deleteConfig, isPending: isDeleting } = useDeleteSandboxProviderConfig()
|
|
|
|
const formSchemas: FormSchema[] = useMemo(() => {
|
|
return provider.config_schema.map((schema) => {
|
|
const fieldConfig = SANDBOX_FIELD_CONFIGS[schema.name as keyof typeof SANDBOX_FIELD_CONFIGS]
|
|
const fallbackType = schema.type === 'secret' ? FormTypeEnum.secretInput : FormTypeEnum.textInput
|
|
|
|
return {
|
|
name: schema.name,
|
|
label: fieldConfig ? t(fieldConfig.labelKey, { ns: 'common' }) : schema.name,
|
|
placeholder: fieldConfig ? t(fieldConfig.placeholderKey, { ns: 'common' }) : '',
|
|
type: fieldConfig?.type ?? fallbackType,
|
|
required: schema.name === 'api_key',
|
|
default: provider.config[schema.name] || '',
|
|
}
|
|
})
|
|
}, [provider.config_schema, provider.config, t])
|
|
|
|
const handleSave = useCallback(async () => {
|
|
const formValues = formRef.current?.getFormValues({
|
|
needTransformWhenSecretFieldIsPristine: true,
|
|
})
|
|
|
|
if (!formValues?.isCheckValidated)
|
|
return
|
|
|
|
try {
|
|
await saveConfig({
|
|
providerType: provider.provider_type,
|
|
config: formValues.values,
|
|
})
|
|
notify({ type: 'success', message: t('api.saved', { ns: 'common' }) })
|
|
onClose()
|
|
}
|
|
catch {
|
|
// Error toast is handled by fetch layer
|
|
}
|
|
}, [saveConfig, provider.provider_type, notify, t, onClose])
|
|
|
|
const handleRevoke = useCallback(async () => {
|
|
try {
|
|
await deleteConfig(provider.provider_type)
|
|
notify({ type: 'success', message: t('api.remove', { ns: 'common' }) })
|
|
onClose()
|
|
}
|
|
catch {
|
|
// Error toast is handled by fetch layer
|
|
}
|
|
}, [deleteConfig, provider.provider_type, notify, t, onClose])
|
|
|
|
const isConfigured = provider.is_tenant_configured
|
|
const docLink = PROVIDER_DOC_LINKS[provider.provider_type]
|
|
|
|
return (
|
|
<Modal
|
|
isShow
|
|
onClose={onClose}
|
|
closable
|
|
className="w-[480px]"
|
|
>
|
|
{/* Custom Header: Title + Subtitle with 8px gap */}
|
|
<div className="mb-4 flex flex-col gap-2">
|
|
<h3 className="title-2xl-semi-bold text-text-primary">
|
|
{t('sandboxProvider.configModal.title', { ns: 'common' })}
|
|
</h3>
|
|
<div className="flex items-center gap-2">
|
|
<ProviderIcon providerType={provider.provider_type} size="sm" withBorder />
|
|
<span className="system-md-regular text-text-secondary">
|
|
{t(PROVIDER_LABEL_KEYS[provider.provider_type as keyof typeof PROVIDER_LABEL_KEYS] ?? 'sandboxProvider.e2b.label', { ns: 'common' })}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<BaseForm
|
|
formSchemas={formSchemas}
|
|
ref={formRef}
|
|
labelClassName="system-sm-medium mb-1 flex items-center gap-1 text-text-secondary"
|
|
formClassName="space-y-4"
|
|
/>
|
|
|
|
{/* Footer Actions */}
|
|
<div className="mt-6 flex items-center justify-between">
|
|
<div>
|
|
{docLink && (
|
|
<a
|
|
href={docLink}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="system-xs-regular inline-flex items-center gap-1 text-text-accent hover:underline"
|
|
>
|
|
{t('sandboxProvider.configModal.readDocLink', { ns: 'common', provider: t(PROVIDER_LABEL_KEYS[provider.provider_type as keyof typeof PROVIDER_LABEL_KEYS] ?? 'sandboxProvider.e2b.label', { ns: 'common' }) })}
|
|
<RiExternalLinkLine className="h-3 w-3" />
|
|
</a>
|
|
)}
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
{isConfigured && (
|
|
<Button
|
|
variant="warning"
|
|
size="medium"
|
|
onClick={handleRevoke}
|
|
disabled={isDeleting || isSaving}
|
|
>
|
|
{t('sandboxProvider.configModal.revoke', { ns: 'common' })}
|
|
</Button>
|
|
)}
|
|
<Button
|
|
variant="secondary"
|
|
size="medium"
|
|
onClick={onClose}
|
|
disabled={isSaving || isDeleting}
|
|
>
|
|
{t('sandboxProvider.configModal.cancel', { ns: 'common' })}
|
|
</Button>
|
|
<Button
|
|
variant="primary"
|
|
size="medium"
|
|
onClick={handleSave}
|
|
disabled={isSaving || isDeleting}
|
|
>
|
|
{t('sandboxProvider.configModal.save', { ns: 'common' })}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Security tip */}
|
|
<div className="-mx-6 -mb-6 mt-4 flex items-start justify-center gap-1 rounded-b-2xl border-t border-divider-subtle bg-background-soft px-2 py-3">
|
|
<RiLock2Fill className="h-3 w-3 shrink-0 text-text-primary" />
|
|
<p className="system-xs-regular text-text-tertiary">
|
|
{t('sandboxProvider.configModal.securityTip', { ns: 'common' })}
|
|
{' '}
|
|
<span className="text-text-accent">PKCS1_OAEP</span>
|
|
{' '}
|
|
{t('sandboxProvider.configModal.securityTipTechnology', { ns: 'common' })}
|
|
</p>
|
|
</div>
|
|
</Modal>
|
|
)
|
|
}
|
|
|
|
export default memo(ConfigModal)
|