diff --git a/README.md b/README.md index a689745a4..68091e665 100644 --- a/README.md +++ b/README.md @@ -27,14 +27,6 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开 | ![Demo](./.github/imgs/intro1.png) | ![Demo](./.github/imgs/intro2.png) | | ![Demo](./.github/imgs/intro3.png) | ![Demo](./.github/imgs/intro4.png) | -## ⚡快速部署 - -> Sealos 的服务器在国外,不需要额外处理网络问题,无需服务器、无需魔法、无需域名,支持高并发 & 动态伸缩。点击以下按钮即可一键部署 👇 - -[![](https://cdn.jsdelivr.us/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt) - -由于需要部署数据库,部署完后需要等待 2~4 分钟才能正常访问。默认用了最低配置,首次访问时会有些慢。 - ## 💡 功能 1. 强大的可视化编排,轻松构建 AI 应用 @@ -76,12 +68,21 @@ FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开 项目技术栈: NextJs + TS + ChakraUI + Mongo + Postgres(Vector 插件) -- [快开始本地开发](https://doc.fastgpt.run/docs/develop/dev) -- [部署 FastGPT](https://doc.fastgpt.run/docs/category/deploy) -- [系统配置文件说明](https://doc.fastgpt.run/docs/category/data-config) -- [多模型配置](https://doc.fastgpt.run/docs/develop/data_config/chat_models) -- [V3 升级 V4 初始化](https://doc.fastgpt.run/docs/develop/deploy/v4init) -- [API 文档](https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh?pre_pathname=%2Fdrive%2Fhome%2F) +- **⚡ 快速部署** + + > Sealos 的服务器在国外,不需要额外处理网络问题,无需服务器、无需魔法、无需域名,支持高并发 & 动态伸缩。点击以下按钮即可一键部署 👇 + + [![](https://cdn.jsdelivr.us/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt) + + 由于需要部署数据库,部署完后需要等待 2~4 分钟才能正常访问。默认用了最低配置,首次访问时会有些慢。 + +* [快开始本地开发](https://doc.fastgpt.run/docs/develop/dev) +* [部署 FastGPT](https://doc.fastgpt.run/docs/category/deploy) +* [系统配置文件说明](https://doc.fastgpt.run/docs/category/data-config) +* [多模型配置](https://doc.fastgpt.run/docs/develop/data_config/chat_models) +* [V3 升级 V4 初始化](https://doc.fastgpt.run/docs/develop/deploy/v4init) +* [升级 v4.1 初始化](https://doc.fastgpt.run/docs/develop/deploy/initv4.1) +* [API 文档](https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh?pre_pathname=%2Fdrive%2Fhome%2F) ## 🏘️ 社区交流群 diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json index f9061f459..45a4449ea 100644 --- a/client/public/locales/en/common.json +++ b/client/public/locales/en/common.json @@ -11,6 +11,7 @@ "Confirm Save App Tip": "The application may be in advanced orchestration mode, and the advanced orchestration configuration will be overwritten after saving, please confirm!", "Connection is invalid": "Connecting is invalid", "Connection type is different": "Connection type is different", + "Copy Module Config": "Copy config", "Export Config Successful": "The configuration has been copied. Please check for important data", "Export Configs": "Export Configs", "Import Config": "Import Config", diff --git a/client/public/locales/zh/common.json b/client/public/locales/zh/common.json index ff42aba65..ed27797b9 100644 --- a/client/public/locales/zh/common.json +++ b/client/public/locales/zh/common.json @@ -11,6 +11,7 @@ "Confirm Save App Tip": "该应用可能为高级编排模式,保存后将会覆盖高级编排配置,请确认!", "Connection is invalid": "连接无效", "Connection type is different": "连接的类型不一致", + "Copy Module Config": "复制配置", "Export Config Successful": "已复制配置,请注意检查是否有重要数据", "Export Configs": "导出配置", "Import Config": "导入配置", diff --git a/client/src/api/chat.ts b/client/src/api/chat.ts index 9fc5e125e..8406b56f6 100644 --- a/client/src/api/chat.ts +++ b/client/src/api/chat.ts @@ -27,20 +27,10 @@ export const delChatHistoryById = (chatId: string) => DELETE(`/chat/removeHistor */ export const clearChatHistoryByAppId = (appId: string) => DELETE(`/chat/removeHistory`, { appId }); -/** - * update history quote status - */ -export const updateHistoryQuote = (params: { - chatId: string; - contentId: string; - quoteId: string; - sourceText: string; -}) => PUT(`/chat/history/updateHistoryQuote`, params); - /** * 删除一句对话 */ -export const delChatRecordByIndex = (data: { chatId: string; contentId: string }) => +export const delChatRecordById = (data: { chatId: string; contentId: string }) => DELETE(`/chat/delChatRecordByContentId`, data); /** diff --git a/client/src/components/ChatBox/index.tsx b/client/src/components/ChatBox/index.tsx index 204510d6c..64fcee4f6 100644 --- a/client/src/components/ChatBox/index.tsx +++ b/client/src/components/ChatBox/index.tsx @@ -40,6 +40,7 @@ import { useRouter } from 'next/router'; import { useGlobalStore } from '@/store/global'; import { TaskResponseKeyEnum, getDefaultChatVariables } from '@/constants/chat'; import { useTranslation } from 'react-i18next'; +import { customAlphabet } from 'nanoid'; import MyIcon from '@/components/Icon'; import Avatar from '@/components/Avatar'; @@ -51,6 +52,8 @@ const ResponseTags = dynamic(() => import('./ResponseTags')); import styles from './index.module.scss'; +const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24); + const textareaMinH = '22px'; type generatingMessageProps = { text?: string; name?: string; status?: 'running' | 'finish' }; export type StartChatFnProps = { @@ -282,13 +285,13 @@ const ChatBox = ( const newChatList: ChatSiteItemType[] = [ ...chatHistory, { - _id: String(new Types.ObjectId()), + dataId: nanoid(), obj: 'Human', value: val, status: 'finish' }, { - _id: String(new Types.ObjectId()), + dataId: nanoid(), obj: 'AI', value: '', status: 'loading' @@ -552,7 +555,7 @@ const ChatBox = ( {chatHistory.map((item, index) => ( { setChatHistory((state) => - state.filter((chat) => chat._id !== item._id) + state.filter((chat) => chat.dataId !== item.dataId) ); onDelMessage({ - contentId: item._id, + contentId: item.dataId, index }); }} @@ -630,10 +633,10 @@ const ChatBox = ( _hover={{ color: 'red.600' }} onClick={() => { setChatHistory((state) => - state.filter((chat) => chat._id !== item._id) + state.filter((chat) => chat.dataId !== item.dataId) ); onDelMessage({ - contentId: item._id, + contentId: item.dataId, index }); }} @@ -682,7 +685,7 @@ const ChatBox = ( /> diff --git a/client/src/pages/api/admin/initChat.ts b/client/src/pages/api/admin/initChat.ts index 1b8143487..6b2be11c5 100644 --- a/client/src/pages/api/admin/initChat.ts +++ b/client/src/pages/api/admin/initChat.ts @@ -45,9 +45,7 @@ async function init(limit: number, skip: number) { chatId: { $exists: false } }, '_id' - ) - .limit(limit) - .skip(skip); + ).limit(limit); await Promise.all( chats.map((chat) => diff --git a/client/src/pages/api/admin/initChatItem.ts b/client/src/pages/api/admin/initChatItem.ts new file mode 100644 index 000000000..a52694a61 --- /dev/null +++ b/client/src/pages/api/admin/initChatItem.ts @@ -0,0 +1,98 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import type { NextApiRequest, NextApiResponse } from 'next'; +import { jsonRes } from '@/service/response'; +import { authUser } from '@/service/utils/auth'; +import { connectToDatabase, Chat, ChatItem } from '@/service/mongo'; +import { customAlphabet } from 'nanoid'; +const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24); + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + await authUser({ req, authRoot: true }); + await connectToDatabase(); + + const { limit = 100 } = req.body as { limit: number }; + let skip = 0; + + const total = await Chat.countDocuments({ + content: { $exists: true, $not: { $size: 0 } }, + isInit: { $ne: true } + }); + const totalChat = await Chat.aggregate([ + { + $project: { + contentLength: { $size: '$content' } + } + }, + { + $group: { + _id: null, + totalLength: { $sum: '$contentLength' } + } + } + ]); + + console.log('chatLen:', total, totalChat); + + let promise = Promise.resolve(); + + for (let i = 0; i < total; i += limit) { + const skipVal = skip; + skip += limit; + promise = promise + .then(() => init(limit)) + .then(() => { + console.log(skipVal); + }); + } + + await promise; + + jsonRes(res, {}); + } catch (error) { + jsonRes(res, { + code: 500, + error + }); + } +} + +async function init(limit: number) { + // 遍历 app + const chats = await Chat.find( + { + content: { $exists: true, $not: { $size: 0 } }, + isInit: { $ne: true } + }, + '_id userId appId chatId content' + ) + .sort({ updateTime: -1 }) + .limit(limit); + + await Promise.all( + chats.map(async (chat) => { + const inserts = chat.content + .map((item) => ({ + dataId: nanoid(), + chatId: chat.chatId, + userId: chat.userId, + appId: chat.appId, + obj: item.obj, + value: item.value, + responseData: item.responseData + })) + .filter((item) => item.chatId && item.userId && item.appId && item.obj && item.value); + + try { + await Promise.all(inserts.map((item) => ChatItem.create(item))); + await Chat.findByIdAndUpdate(chat._id, { + isInit: true + }); + } catch (error) { + console.log(error); + + await ChatItem.deleteMany({ chatId: chat.chatId }); + } + }) + ); +} diff --git a/client/src/pages/api/admin/initv4.ts b/client/src/pages/api/admin/initv4.ts index 284f103c7..a4cdd155f 100644 --- a/client/src/pages/api/admin/initv4.ts +++ b/client/src/pages/api/admin/initv4.ts @@ -408,9 +408,7 @@ async function init(limit: number, skip: number) { // userId: '63f9a14228d2a688d8dc9e1b' }, '_id chat' - ) - .limit(limit) - .skip(skip); + ).limit(limit); return Promise.all( apps.map(async (app) => { diff --git a/client/src/pages/api/chat/delChatRecordByContentId.ts b/client/src/pages/api/chat/delChatRecordByContentId.ts index 29c9c00f4..5d79a0d6e 100644 --- a/client/src/pages/api/chat/delChatRecordByContentId.ts +++ b/client/src/pages/api/chat/delChatRecordByContentId.ts @@ -1,35 +1,23 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; -import { connectToDatabase, Chat } from '@/service/mongo'; +import { connectToDatabase, ChatItem } from '@/service/mongo'; import { authUser } from '@/service/utils/auth'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { const { chatId, contentId } = req.query as { chatId: string; contentId: string }; - if (!chatId || !contentId) { - throw new Error('缺少参数'); - } - await connectToDatabase(); // 凭证校验 const { userId } = await authUser({ req, authToken: true }); - const chatRecord = await Chat.findOne({ chatId }); - - if (!chatRecord) { - throw new Error('找不到对话'); - } - // 删除一条数据库记录 - await Chat.updateOne( - { - chatId, - userId - }, - { $pull: { content: { _id: contentId } } } - ); + await ChatItem.deleteOne({ + dataId: contentId, + chatId, + userId + }); jsonRes(res); } catch (err) { diff --git a/client/src/pages/api/chat/history/updateHistoryQuote.ts b/client/src/pages/api/chat/history/updateHistoryQuote.ts deleted file mode 100644 index 973d79c19..000000000 --- a/client/src/pages/api/chat/history/updateHistoryQuote.ts +++ /dev/null @@ -1,57 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { jsonRes } from '@/service/response'; -import { connectToDatabase, Chat } from '@/service/mongo'; -import { authUser } from '@/service/utils/auth'; -import { Types } from 'mongoose'; - -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - try { - let { - chatId, - contentId, - quoteId, - sourceText = '' - } = req.body as { - chatId: string; - contentId: string; - quoteId: string; - sourceText: string; - }; - await connectToDatabase(); - - const { userId } = await authUser({ req, authToken: true }); - - if (!contentId || !chatId || !quoteId) { - throw new Error('params is error'); - } - - await Chat.updateOne( - { - chatId, - userId: new Types.ObjectId(userId), - 'content._id': new Types.ObjectId(contentId) - }, - { - $set: { - 'content.$.rawSearch.$[quoteElem].source': sourceText - } - }, - { - arrayFilters: [ - { - 'quoteElem.id': quoteId - } - ] - } - ); - - jsonRes(res, { - data: '' - }); - } catch (err) { - jsonRes(res, { - code: 500, - error: err - }); - } -} diff --git a/client/src/pages/api/chat/init.ts b/client/src/pages/api/chat/init.ts index 0c8b91bbd..25963982a 100644 --- a/client/src/pages/api/chat/init.ts +++ b/client/src/pages/api/chat/init.ts @@ -1,11 +1,10 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; -import { connectToDatabase, Chat } from '@/service/mongo'; +import { Chat, ChatItem } from '@/service/mongo'; import type { InitChatResponse } from '@/api/response/chat'; import { authUser } from '@/service/utils/auth'; import { ChatItemType } from '@/types/chat'; import { authApp } from '@/service/utils/auth'; -import mongoose from 'mongoose'; import type { ChatSchema } from '@/types/mongoSchema'; import { getSpecialModule, getChatModelNameList } from '@/components/ChatBox/utils'; import { TaskResponseKeyEnum } from '@/constants/chat'; @@ -27,8 +26,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }); } - await connectToDatabase(); - // 校验使用权限 const app = ( await authApp({ @@ -39,49 +36,42 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }) ).app; - // 历史记录 + // get app and history const { chat, history = [] }: { chat?: ChatSchema; history?: ChatItemType[] } = await (async () => { if (chatId) { // auth chatId const [chat, history] = await Promise.all([ - Chat.findOne({ - chatId, - userId - }), - Chat.aggregate([ + Chat.findOne( { - $match: { - chatId, - userId: new mongoose.Types.ObjectId(userId) - } + chatId, + userId }, + 'title variables' + ), + ChatItem.find( { - $project: { - content: { - $slice: ['$content', -30] // 返回 content 数组的最后 30 个元素 - } - } + chatId, + userId }, - { $unwind: '$content' }, - { - $project: { - _id: '$content._id', - obj: '$content.obj', - value: '$content.value', - [TaskResponseKeyEnum.responseData]: `$content.${TaskResponseKeyEnum.responseData}` - } - } - ]) + `dataId obj value ${TaskResponseKeyEnum.responseData}` + ) + .sort({ _id: -1 }) + .limit(30) ]); if (!chat) { throw new Error('聊天框不存在'); } - return { history, chat }; + history.reverse(); + return { app, history, chat }; } return {}; })(); + if (!app) { + throw new Error('Auth App Error'); + } + const isOwner = String(app.userId) === userId; jsonRes(res, { @@ -108,3 +98,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }); } } + +export const config = { + api: { + bodyParser: { + sizeLimit: '10mb' + } + } +}; diff --git a/client/src/pages/api/chat/removeHistory.ts b/client/src/pages/api/chat/removeHistory.ts index 83195bbd5..87fe57f1b 100644 --- a/client/src/pages/api/chat/removeHistory.ts +++ b/client/src/pages/api/chat/removeHistory.ts @@ -1,6 +1,6 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; -import { connectToDatabase, Chat } from '@/service/mongo'; +import { connectToDatabase, Chat, ChatItem } from '@/service/mongo'; import { authUser } from '@/service/utils/auth'; type Props = { @@ -17,16 +17,28 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) await connectToDatabase(); if (chatId) { - await Chat.findOneAndRemove({ - chatId, - userId - }); + await Promise.all([ + Chat.findOneAndRemove({ + chatId, + userId + }), + ChatItem.deleteMany({ + userId, + chatId + }) + ]); } if (appId) { - await Chat.deleteMany({ - appId, - userId - }); + await Promise.all([ + Chat.deleteMany({ + appId, + userId + }), + ChatItem.deleteMany({ + userId, + appId + }) + ]); } jsonRes(res); diff --git a/client/src/pages/api/openapi/v1/chat/completions.ts b/client/src/pages/api/openapi/v1/chat/completions.ts index 01141b93a..f5b9d8f6b 100644 --- a/client/src/pages/api/openapi/v1/chat/completions.ts +++ b/client/src/pages/api/openapi/v1/chat/completions.ts @@ -28,7 +28,7 @@ import { BillSourceEnum } from '@/constants/user'; import { ChatHistoryItemResType } from '@/types/chat'; import { UserModelSchema } from '@/types/mongoSchema'; -export type MessageItemType = ChatCompletionRequestMessage & { _id?: string }; +export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string }; type FastGptWebChatProps = { chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history appId?: string; @@ -172,7 +172,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex content: [ prompt, { - _id: messages[messages.length - 1]._id, + dataId: messages[messages.length - 1].dataId, obj: ChatRoleEnum.AI, value: answerText, responseData diff --git a/client/src/pages/api/openapi/v1/chat/getHistory.ts b/client/src/pages/api/openapi/v1/chat/getHistory.ts index 743297238..f44097346 100644 --- a/client/src/pages/api/openapi/v1/chat/getHistory.ts +++ b/client/src/pages/api/openapi/v1/chat/getHistory.ts @@ -2,10 +2,9 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; import { authUser } from '@/service/utils/auth'; -import { connectToDatabase, Chat } from '@/service/mongo'; +import { connectToDatabase, ChatItem } from '@/service/mongo'; import { Types } from 'mongoose'; import type { ChatItemType } from '@/types/chat'; -import { TaskResponseKeyEnum } from '@/constants/chat'; export type Props = { chatId?: string; @@ -37,30 +36,37 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) export async function getChatHistory({ chatId, userId, - limit = 20 + limit = 30 }: Props & { userId: string }): Promise { if (!chatId) { return { history: [] }; } - const history = await Chat.aggregate([ - { $match: { chatId, userId: new Types.ObjectId(userId) } }, + const history = await ChatItem.aggregate([ { - $project: { - content: { - $slice: ['$content', -limit] // 返回 content 数组的最后20个元素 - } + $match: { + chatId, + userId: new Types.ObjectId(userId) } }, - { $unwind: '$content' }, + { + $sort: { + _id: -1 + } + }, + { + $limit: limit + }, { $project: { - obj: '$content.obj', - value: '$content.value', - [TaskResponseKeyEnum.responseData]: `$content.responseData` + dataId: 1, + obj: 1, + value: 1 } } ]); + history.reverse(); + return { history }; } diff --git a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeAnswer.tsx b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeAnswer.tsx index c47afe226..eeb0f5ea9 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeAnswer.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeAnswer.tsx @@ -2,15 +2,13 @@ import React from 'react'; import { NodeProps } from 'reactflow'; import NodeCard from '../modules/NodeCard'; import { FlowModuleItemType } from '@/types/flow'; -import Divider from '../modules/Divider'; import Container from '../modules/Container'; import RenderInput from '../render/RenderInput'; -const NodeAnswer = ({ - data: { moduleId, inputs, outputs, onChangeNode, ...props } -}: NodeProps) => { +const NodeAnswer = ({ data }: NodeProps) => { + const { moduleId, inputs, outputs, onChangeNode } = data; return ( - + diff --git a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeCQNode.tsx b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeCQNode.tsx index 425aabd17..efc89d461 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeCQNode.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeCQNode.tsx @@ -13,11 +13,10 @@ import MyIcon from '@/components/Icon'; import { FlowOutputItemTypeEnum, FlowValueTypeEnum, SpecialInputKeyEnum } from '@/constants/flow'; import SourceHandle from '../render/SourceHandle'; -const NodeCQNode = ({ - data: { moduleId, inputs, outputs, onChangeNode, ...props } -}: NodeProps) => { +const NodeCQNode = ({ data }: NodeProps) => { + const { moduleId, inputs, outputs, onChangeNode } = data; return ( - + ) => { +const NodeChat = ({ data }: NodeProps) => { + const { moduleId, inputs, outputs, onChangeNode } = data; const outputsLen = useMemo( () => outputs.filter((item) => item.type !== FlowOutputItemTypeEnum.hidden).length, [outputs] ); return ( - + ) => { - return ; +const NodeAnswer = ({ data }: NodeProps) => { + return ; }; export default React.memo(NodeAnswer); diff --git a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeExtract.tsx b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeExtract.tsx index 2bab4ce05..7ae49816b 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeExtract.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeExtract.tsx @@ -15,14 +15,13 @@ import ExtractFieldModal from '../modules/ExtractFieldModal'; import { ContextExtractEnum } from '@/constants/flow/flowField'; import { FlowOutputItemTypeEnum, FlowValueTypeEnum } from '@/constants/flow'; -const NodeExtract = ({ - data: { inputs, outputs, moduleId, onChangeNode, onDelEdge, ...props } -}: NodeProps) => { +const NodeExtract = ({ data }: NodeProps) => { + const { inputs, outputs, moduleId, onChangeNode, onDelEdge } = data; const { t } = useTranslation(); const [editExtractFiled, setEditExtractField] = useState(); return ( - + ) => { +const NodeHistory = ({ data }: NodeProps) => { + const { inputs, outputs, moduleId, onChangeNode } = data; return ( - + - + - + ); diff --git a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHttp.tsx b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHttp.tsx index 70e247e58..2359f068c 100644 --- a/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHttp.tsx +++ b/client/src/pages/app/detail/components/AdEdit/components/Nodes/NodeHttp.tsx @@ -13,11 +13,10 @@ import { FlowInputItemTypeEnum, FlowOutputItemTypeEnum, FlowValueTypeEnum } from import { customAlphabet } from 'nanoid'; const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 6); -const NodeHttp = ({ - data: { moduleId, inputs, outputs, onChangeNode, ...props } -}: NodeProps) => { +const NodeHttp = ({ data }: NodeProps) => { + const { moduleId, inputs, outputs, onChangeNode } = data; return ( - +