import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { Box, Flex, Button, ModalBody, Input, Textarea, TableContainer, Table, Thead, Th, Tbody, Tr, Td, ModalFooter } from '@chakra-ui/react'; import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { useForm } from 'react-hook-form'; import { useToast } from '@fastgpt/web/hooks/useToast'; import { useRequest } from '@fastgpt/web/hooks/useRequest'; import Avatar from '@fastgpt/web/components/common/Avatar'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import { useTranslation } from 'next-i18next'; import { HttpPluginImgUrl } from '@fastgpt/global/common/file/image/constants'; import { postCreateHttpPlugin, putUpdateHttpPlugin, getApiSchemaByUrl } from '@/web/core/app/api/plugin'; import { str2OpenApiSchema } from '@fastgpt/global/core/app/httpPlugin/utils'; import MyIcon from '@fastgpt/web/components/common/Icon'; import MyModal from '@fastgpt/web/components/common/MyModal'; import HttpInput from '@fastgpt/web/components/common/Input/HttpInput'; import { OpenApiJsonSchema } from '@fastgpt/global/core/app/httpPlugin/type'; import { AppSchema } from '@fastgpt/global/core/app/type'; import { useContextSelector } from 'use-context-selector'; import { AppListContext } from './context'; export type EditHttpPluginProps = { id?: string; avatar: string; name: string; intro?: string; pluginData?: AppSchema['pluginData']; }; export const defaultHttpPlugin: EditHttpPluginProps = { avatar: HttpPluginImgUrl, name: '', intro: '', pluginData: { apiSchemaStr: '', customHeaders: '{"Authorization":"Bearer"}' } }; const HttpPluginEditModal = ({ defaultPlugin = defaultHttpPlugin, onClose }: { defaultPlugin?: EditHttpPluginProps; onClose: () => void; }) => { const { t } = useTranslation(); const { toast } = useToast(); const isEdit = !!defaultPlugin.id; const { parentId, loadMyApps } = useContextSelector(AppListContext, (v) => v); const [schemaUrl, setSchemaUrl] = useState(''); const [customHeaders, setCustomHeaders] = useState<{ key: string; value: string }[]>(() => { const keyValue = JSON.parse(defaultPlugin.pluginData?.customHeaders || '{}'); return Object.keys(keyValue).map((key) => ({ key, value: keyValue[key] })); }); const [updateTrigger, setUpdateTrigger] = useState(false); const { register, setValue, handleSubmit, watch } = useForm({ defaultValues: defaultPlugin }); const avatar = watch('avatar'); const apiSchemaStr = watch('pluginData.apiSchemaStr'); const [apiData, setApiData] = useState({ pathData: [], serverPath: '' }); const { mutate: onCreate, isLoading: isCreating } = useRequest({ mutationFn: async (data: EditHttpPluginProps) => { return postCreateHttpPlugin({ parentId, name: data.name, intro: data.intro, avatar: data.avatar, pluginData: { apiSchemaStr: data.pluginData?.apiSchemaStr, customHeaders: data.pluginData?.customHeaders } }); }, onSuccess() { loadMyApps(); onClose(); }, successToast: t('common:create_success'), errorToast: t('common:create_failed') }); const { mutate: updatePlugins, isLoading: isUpdating } = useRequest({ mutationFn: async (data: EditHttpPluginProps) => { if (!data.id || !data.pluginData) return Promise.resolve(''); return putUpdateHttpPlugin({ appId: data.id, name: data.name, intro: data.intro, avatar: data.avatar, pluginData: data.pluginData }); }, onSuccess() { loadMyApps(); onClose(); }, successToast: t('common:update_success'), errorToast: t('common:update_failed') }); const { File, onOpen: onOpenSelectFile, onSelectImage } = useSelectFile({ fileType: 'image/*', multiple: false }); /* load api from url */ const { mutate: onClickUrlLoadApi, isLoading: isLoadingUrlApi } = useRequest({ mutationFn: async () => { if (!schemaUrl || (!schemaUrl.startsWith('https://') && !schemaUrl.startsWith('http://'))) { return toast({ title: t('common:plugin.Invalid URL'), status: 'warning' }); } const schema = await getApiSchemaByUrl(schemaUrl); setValue('pluginData.apiSchemaStr', JSON.stringify(schema, null, 2)); }, errorToast: t('common:plugin.Invalid Schema') }); useEffect(() => { (async () => { if (!apiSchemaStr) { return setApiData({ pathData: [], serverPath: '' }); } try { setApiData(await str2OpenApiSchema(apiSchemaStr)); } catch (err) { toast({ status: 'warning', title: t('common:plugin.Invalid Schema') }); setApiData({ pathData: [], serverPath: '' }); } })(); }, [apiSchemaStr, t, toast]); return ( <> <> {t('common:input_name')} <> {t('common:plugin.Intro')}