From db6fc53840e7b3e07f0d8523235dc78be0e9a873 Mon Sep 17 00:00:00 2001 From: Archer <545436317@qq.com> Date: Tue, 30 Apr 2024 12:42:13 +0800 Subject: [PATCH] Publish histories (#1331) * fix http plugin edge (#95) * fix http plugin edge * use getHandleId * perf: i18n file * feat: histories list * perf: request lock * fix: ts * move box components * fix: edit form refresh --------- Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com> --- .vscode/nextapi.code-snippets | 2 +- .vscode/settings.json | 2 +- packages/global/core/app/version.d.ts | 1 + .../global/core/plugin/httpPlugin/utils.ts | 9 +- .../core/workflow/template/constants.ts | 38 ---- packages/web/common/fetch/type.d.ts | 8 + .../web/components/common/Icon/constants.ts | 2 + .../common/Icon/icons/common/publishFill.svg | 8 +- .../icons/core/workflow/revertVersion.svg | 4 + .../icons/core/workflow/versionHistories.svg | 5 + .../components/common/Icon/icons/history.svg | 4 +- .../web/components/common/MyBox/index.tsx | 19 ++ .../common/MyDrawer/CustomRightDrawer.tsx | 80 ++++++++ packages/web/core/workflow/constants.ts | 41 ++++ packages/web/hooks/useScrollPagination.tsx | 122 ++++++++++++ packages/web/package.json | 1 + pnpm-lock.yaml | 3 + project.inlang/settings.json | 2 +- .../{public/locales => i18n}/en/common.json | 7 + .../{public/locales => i18n}/zh/common.json | 7 + projects/app/next-i18next.config.js | 3 +- .../src/components/PageContainer/index.tsx | 2 +- .../app/src/components/common/MyBox/index.tsx | 19 -- .../src/components/core/dataset/QuoteItem.tsx | 2 +- .../components/core/dataset/SelectModal.tsx | 2 +- .../core/workflow/Flow/NodeTemplatesModal.tsx | 4 +- .../components/PublishHistoriesSlider.tsx | 179 ++++++++++++++++++ .../src/components/core/workflow/context.tsx | 22 ++- projects/app/src/global/core/app/api.d.ts | 7 + .../pages/account/components/BillTable.tsx | 2 +- .../src/pages/api/core/app/version/list.ts | 34 ++++ .../src/pages/api/core/app/version/revert.ts | 73 +++++++ .../app/detail/components/FlowEdit/Header.tsx | 101 ++++++---- .../src/pages/app/detail/components/Logs.tsx | 2 +- .../detail/components/SimpleEdit/EditForm.tsx | 20 +- .../components/SimpleEdit/ToolSelectModal.tsx | 2 +- projects/app/src/pages/app/list/index.tsx | 1 - projects/app/src/pages/chat/share.tsx | 2 +- projects/app/src/pages/chat/team.tsx | 2 +- .../detail/components/CollectionCard.tsx | 2 +- .../Import/components/FileSelector.tsx | 2 +- .../detail/components/InputDataModal.tsx | 2 +- .../app/src/pages/dataset/detail/index.tsx | 2 +- projects/app/src/types/i18n.d.ts | 2 +- .../app/src/web/core/app/store/useAppStore.ts | 6 + projects/app/src/web/core/app/versionApi.ts | 10 +- 46 files changed, 741 insertions(+), 129 deletions(-) create mode 100644 packages/web/common/fetch/type.d.ts create mode 100644 packages/web/components/common/Icon/icons/core/workflow/revertVersion.svg create mode 100644 packages/web/components/common/Icon/icons/core/workflow/versionHistories.svg create mode 100644 packages/web/components/common/MyBox/index.tsx create mode 100644 packages/web/components/common/MyDrawer/CustomRightDrawer.tsx create mode 100644 packages/web/core/workflow/constants.ts create mode 100644 packages/web/hooks/useScrollPagination.tsx rename projects/app/{public/locales => i18n}/en/common.json (99%) rename projects/app/{public/locales => i18n}/zh/common.json (99%) delete mode 100644 projects/app/src/components/common/MyBox/index.tsx create mode 100644 projects/app/src/components/core/workflow/components/PublishHistoriesSlider.tsx create mode 100644 projects/app/src/pages/api/core/app/version/list.ts create mode 100644 projects/app/src/pages/api/core/app/version/revert.ts diff --git a/.vscode/nextapi.code-snippets b/.vscode/nextapi.code-snippets index 899fc468a..145b57c96 100644 --- a/.vscode/nextapi.code-snippets +++ b/.vscode/nextapi.code-snippets @@ -17,7 +17,7 @@ "", "type Response = {};", "", - "async function handler(req: NextApiRequest, res: NextApiResponse): Promise<{}> {", + "async function handler(req: NextApiRequest, res: NextApiResponse): Promise {", " $1", " return {}", "}", diff --git a/.vscode/settings.json b/.vscode/settings.json index beb0783c3..b60a6c967 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,7 +4,7 @@ "typescript.tsdk": "node_modules/typescript/lib", "prettier.prettierPath": "", "i18n-ally.localesPaths": [ - "projects/app/public/locales", + "projects/app/i18n", ], "i18n-ally.enabledParsers": ["json"], "i18n-ally.keystyle": "nested", diff --git a/packages/global/core/app/version.d.ts b/packages/global/core/app/version.d.ts index 7b0dbba63..152014e7a 100644 --- a/packages/global/core/app/version.d.ts +++ b/packages/global/core/app/version.d.ts @@ -2,6 +2,7 @@ import { StoreNodeItemType } from '../workflow/type'; import { StoreEdgeItemType } from '../workflow/type/edge'; export type AppVersionSchemaType = { + _id: string; appId: string; time: Date; nodes: StoreNodeItemType[]; diff --git a/packages/global/core/plugin/httpPlugin/utils.ts b/packages/global/core/plugin/httpPlugin/utils.ts index 4c6dfcf8b..487fef592 100644 --- a/packages/global/core/plugin/httpPlugin/utils.ts +++ b/packages/global/core/plugin/httpPlugin/utils.ts @@ -14,6 +14,7 @@ import { CreateOnePluginParams } from '../controller'; import { StoreNodeItemType } from '../../workflow/type'; import { HttpImgUrl } from '../../../common/file/image/constants'; import SwaggerParser from '@apidevtools/swagger-parser'; +import { getHandleId } from '../../../core/workflow/utils'; export const str2OpenApiSchema = async (yamlStr = ''): Promise => { try { @@ -378,14 +379,14 @@ export const httpApiSchema2Plugins = async ({ { source: pluginInputId, target: httpId, - sourcePort: `${pluginInputId}-source-right`, - targetPort: `${httpId}-target-left` + sourceHandle: getHandleId(pluginInputId, 'source', 'right'), + targetHandle: getHandleId(httpId, 'target', 'left') }, { source: httpId, target: pluginOutputId, - sourcePort: `${httpId}-source-right`, - targetPort: `${pluginOutputId}-target-left` + sourceHandle: getHandleId(httpId, 'source', 'right'), + targetHandle: getHandleId(pluginOutputId, 'target', 'left') } ]; diff --git a/packages/global/core/workflow/template/constants.ts b/packages/global/core/workflow/template/constants.ts index f83c33eec..b3471b172 100644 --- a/packages/global/core/workflow/template/constants.ts +++ b/packages/global/core/workflow/template/constants.ts @@ -83,41 +83,3 @@ export const moduleTemplatesFlat: FlowNodeTemplateType[] = [ lafModule, ifElseNode ]; - -export const moduleTemplatesList: nodeTemplateListType = [ - { - type: FlowNodeTemplateTypeEnum.systemInput, - label: 'core.module.template.System input module', - list: [] - }, - { - type: FlowNodeTemplateTypeEnum.textAnswer, - label: 'core.module.template.Response module', - list: [] - }, - { - type: FlowNodeTemplateTypeEnum.functionCall, - label: 'core.module.template.Function module', - list: [] - }, - { - type: FlowNodeTemplateTypeEnum.tools, - label: 'core.module.template.Tool module', - list: [] - }, - { - type: FlowNodeTemplateTypeEnum.externalCall, - label: 'core.module.template.External module', - list: [] - }, - { - type: FlowNodeTemplateTypeEnum.personalPlugin, - label: '', - list: [] - }, - { - type: FlowNodeTemplateTypeEnum.other, - label: '其他', - list: [] - } -]; diff --git a/packages/web/common/fetch/type.d.ts b/packages/web/common/fetch/type.d.ts new file mode 100644 index 000000000..a04ef7bee --- /dev/null +++ b/packages/web/common/fetch/type.d.ts @@ -0,0 +1,8 @@ +export type PaginationProps = T & { + current: number; + pageSize: number; +}; +export type PaginationResponse = { + total: number; + list: T[]; +}; diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 62936a422..eec77bb57 100644 --- a/packages/web/components/common/Icon/constants.ts +++ b/packages/web/components/common/Icon/constants.ts @@ -142,10 +142,12 @@ export const iconPaths = { import('./icons/core/workflow/inputType/selectLLM.svg'), 'core/workflow/inputType/switch': () => import('./icons/core/workflow/inputType/switch.svg'), 'core/workflow/inputType/textarea': () => import('./icons/core/workflow/inputType/textarea.svg'), + 'core/workflow/revertVersion': () => import('./icons/core/workflow/revertVersion.svg'), 'core/workflow/runError': () => import('./icons/core/workflow/runError.svg'), 'core/workflow/runSkip': () => import('./icons/core/workflow/runSkip.svg'), 'core/workflow/runSuccess': () => import('./icons/core/workflow/runSuccess.svg'), 'core/workflow/running': () => import('./icons/core/workflow/running.svg'), + 'core/workflow/versionHistories': () => import('./icons/core/workflow/versionHistories.svg'), date: () => import('./icons/date.svg'), delete: () => import('./icons/delete.svg'), edit: () => import('./icons/edit.svg'), diff --git a/packages/web/components/common/Icon/icons/common/publishFill.svg b/packages/web/components/common/Icon/icons/common/publishFill.svg index f4bec3711..005eec350 100644 --- a/packages/web/components/common/Icon/icons/common/publishFill.svg +++ b/packages/web/components/common/Icon/icons/common/publishFill.svg @@ -1,6 +1,4 @@ - - + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/core/workflow/revertVersion.svg b/packages/web/components/common/Icon/icons/core/workflow/revertVersion.svg new file mode 100644 index 000000000..3d6dd03df --- /dev/null +++ b/packages/web/components/common/Icon/icons/core/workflow/revertVersion.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/core/workflow/versionHistories.svg b/packages/web/components/common/Icon/icons/core/workflow/versionHistories.svg new file mode 100644 index 000000000..6b5182644 --- /dev/null +++ b/packages/web/components/common/Icon/icons/core/workflow/versionHistories.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/packages/web/components/common/Icon/icons/history.svg b/packages/web/components/common/Icon/icons/history.svg index c26670011..dbda22401 100644 --- a/packages/web/components/common/Icon/icons/history.svg +++ b/packages/web/components/common/Icon/icons/history.svg @@ -1,4 +1,4 @@ - + + d="M9.00005 3.27783C5.56362 3.27783 2.77783 6.06362 2.77783 9.50005C2.77783 12.9365 5.56362 15.7223 9.00005 15.7223C12.4365 15.7223 15.2223 12.9365 15.2223 9.50005C15.2223 6.06362 12.4365 3.27783 9.00005 3.27783ZM1.27783 9.50005C1.27783 5.23519 4.73519 1.77783 9.00005 1.77783C13.2649 1.77783 16.7223 5.23519 16.7223 9.50005C16.7223 13.7649 13.2649 17.2223 9.00005 17.2223C4.73519 17.2223 1.27783 13.7649 1.27783 9.50005ZM9.00005 4.56672C9.41427 4.56672 9.75005 4.90251 9.75005 5.31672V9.03653L12.1244 10.2237C12.4948 10.4089 12.645 10.8594 12.4598 11.2299C12.2745 11.6004 11.824 11.7506 11.4535 11.5653L8.66464 10.1709C8.41056 10.0438 8.25005 9.78413 8.25005 9.50005V5.31672C8.25005 4.90251 8.58584 4.56672 9.00005 4.56672Z" /> \ No newline at end of file diff --git a/packages/web/components/common/MyBox/index.tsx b/packages/web/components/common/MyBox/index.tsx new file mode 100644 index 000000000..1cdd5be2d --- /dev/null +++ b/packages/web/components/common/MyBox/index.tsx @@ -0,0 +1,19 @@ +import React, { forwardRef } from 'react'; +import { Box, BoxProps } from '@chakra-ui/react'; +import Loading from '../MyLoading'; + +type Props = BoxProps & { + isLoading?: boolean; + text?: string; +}; + +const MyBox = ({ text, isLoading, children, ...props }: Props, ref: any) => { + return ( + + {isLoading && } + {children} + + ); +}; + +export default forwardRef(MyBox); diff --git a/packages/web/components/common/MyDrawer/CustomRightDrawer.tsx b/packages/web/components/common/MyDrawer/CustomRightDrawer.tsx new file mode 100644 index 000000000..911e62072 --- /dev/null +++ b/packages/web/components/common/MyDrawer/CustomRightDrawer.tsx @@ -0,0 +1,80 @@ +import React from 'react'; +import MyIcon from '../Icon'; +import { Flex, Image, Box, CloseButton, FlexProps } from '@chakra-ui/react'; +import { useLoading } from '../../../hooks/useLoading'; + +type Props = FlexProps & { + onClose: () => void; + iconSrc?: string; + title?: any; + isLoading?: boolean; + showMask?: boolean; +}; + +const CustomRightDrawer = ({ + onClose, + iconSrc, + title, + maxW = ['90vw', '30vw'], + children, + isLoading, + showMask = true, + ...props +}: Props) => { + const { Loading } = useLoading(); + return ( + + + {iconSrc && ( + <> + {iconSrc.startsWith('/') ? ( + + ) : ( + + )} + + )} + + {title} + + + + + + {children} + + + + ); +}; + +export default React.memo(CustomRightDrawer); diff --git a/packages/web/core/workflow/constants.ts b/packages/web/core/workflow/constants.ts new file mode 100644 index 000000000..4f0826ef3 --- /dev/null +++ b/packages/web/core/workflow/constants.ts @@ -0,0 +1,41 @@ +import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants'; +import { nodeTemplateListType } from '@fastgpt/global/core/workflow/type'; +import { TFunction } from 'next-i18next'; + +export const workflowNodeTemplateList = (t: TFunction): nodeTemplateListType => [ + { + type: FlowNodeTemplateTypeEnum.systemInput, + label: t('core.module.template.System input module'), + list: [] + }, + { + type: FlowNodeTemplateTypeEnum.textAnswer, + label: t('core.module.template.Response module'), + list: [] + }, + { + type: FlowNodeTemplateTypeEnum.functionCall, + label: t('core.module.template.Function module'), + list: [] + }, + { + type: FlowNodeTemplateTypeEnum.tools, + label: t('core.module.template.Tool module'), + list: [] + }, + { + type: FlowNodeTemplateTypeEnum.externalCall, + label: t('core.module.template.External module'), + list: [] + }, + { + type: FlowNodeTemplateTypeEnum.personalPlugin, + label: '', + list: [] + }, + { + type: FlowNodeTemplateTypeEnum.other, + label: t('common.Other'), + list: [] + } +]; diff --git a/packages/web/hooks/useScrollPagination.tsx b/packages/web/hooks/useScrollPagination.tsx new file mode 100644 index 000000000..bbafb66b9 --- /dev/null +++ b/packages/web/hooks/useScrollPagination.tsx @@ -0,0 +1,122 @@ +import { useRef, useState, useEffect } from 'react'; +import { Box, BoxProps } from '@chakra-ui/react'; +import { useToast } from './useToast'; +import { getErrText } from '@fastgpt/global/common/error/utils'; +import { PaginationProps, PaginationResponse } from '../common/fetch/type'; +import { useBoolean, useLockFn, useMemoizedFn, useMount, useScroll, useVirtualList } from 'ahooks'; +import MyBox from '../components/common/MyBox'; +import { useTranslation } from 'next-i18next'; + +export function useScrollPagination< + TParams extends PaginationProps, + TData extends PaginationResponse +>( + api: (data: TParams) => Promise, + { + itemHeight = 50, + overscan = 10, + + pageSize = 10, + defaultParams = {} + }: { + itemHeight: number; + overscan?: number; + + pageSize?: number; + defaultParams?: Record; + } +) { + const { t } = useTranslation(); + const containerRef = useRef(null); + const wrapperRef = useRef(null); + + const noMore = useRef(false); + + const { toast } = useToast(); + const [current, setCurrent] = useState(1); + const [data, setData] = useState([]); + const [isLoading, { setTrue, setFalse }] = useBoolean(false); + + const [list] = useVirtualList(data, { + containerTarget: containerRef, + wrapperTarget: wrapperRef, + itemHeight, + overscan + }); + + const loadData = useLockFn(async (num: number = current) => { + if (noMore.current) return; + + setTrue(); + + try { + const res = await api({ + current: num, + pageSize, + ...defaultParams + } as TParams); + + setCurrent(num); + + if (num === 1) { + // reload + setData(res.list); + noMore.current = res.list.length >= res.total; + } else { + const totalLength = data.length + res.list.length; + noMore.current = totalLength >= res.total; + setData((prev) => [...prev, ...res.list]); + } + } catch (error: any) { + toast({ + title: getErrText(error, '获取数据异常'), + status: 'error' + }); + console.log(error); + } + + setFalse(); + }); + + const ScrollList = useMemoizedFn( + ({ + children, + isLoading, + ...props + }: { children: React.ReactNode; isLoading?: boolean } & BoxProps) => { + return ( + + {children} + {noMore.current && ( + + {t('common.No more data')} + + )} + + ); + } + ); + + useMount(() => { + loadData(1); + }); + + const scroll = useScroll(containerRef); + useEffect(() => { + if (!containerRef.current) return; + + const { scrollTop, scrollHeight, clientHeight } = containerRef.current; + + if (scrollTop + clientHeight >= scrollHeight - 100) { + loadData(current + 1); + } + }, [scroll]); + + return { + containerRef, + list, + isLoading, + ScrollList, + fetchData: loadData + }; +} diff --git a/packages/web/package.json b/packages/web/package.json index b244f462b..dad5429d2 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -18,6 +18,7 @@ "@lexical/utils": "0.12.6", "@monaco-editor/react": "^4.6.0", "@tanstack/react-query": "^4.24.10", + "ahooks": "^3.7.11", "date-fns": "2.30.0", "dayjs": "^1.11.7", "i18next": "23.10.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c4017210f..c570f1da2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -259,6 +259,9 @@ importers: '@tanstack/react-query': specifier: ^4.24.10 version: 4.24.10(react-dom@18.2.0)(react@18.2.0) + ahooks: + specifier: ^3.7.11 + version: 3.7.11(react@18.2.0) date-fns: specifier: 2.30.0 version: 2.30.0 diff --git a/project.inlang/settings.json b/project.inlang/settings.json index d8ee3e8b8..aff02ae44 100644 --- a/project.inlang/settings.json +++ b/project.inlang/settings.json @@ -14,7 +14,7 @@ ], "plugin.inlang.i18next": { "pathPattern": { - "common": "./projects/app/public/locales/{languageTag}/common.json" + "common": "./projects/app/i18n/{languageTag}/common.json" }, "variableReferencePattern": [ "{{", "}}" diff --git a/projects/app/public/locales/en/common.json b/projects/app/i18n/en/common.json similarity index 99% rename from projects/app/public/locales/en/common.json rename to projects/app/i18n/en/common.json index d5ec7a5cb..363be447a 100644 --- a/projects/app/public/locales/en/common.json +++ b/projects/app/i18n/en/common.json @@ -117,6 +117,7 @@ "Name is empty": "Name is empty", "New Create": "Create", "Next Step": "Next", + "No more data": "No more", "Not open": "Close", "Number of words": "{{amount}} words", "OK": "OK", @@ -1101,6 +1102,7 @@ "Check Failed": "Workflow verification fails. Check whether the node or connection is normal", "Confirm stop debug": "Do you want to terminate debugging? Debugging information is not retained.", "Copy node": "Copy node", + "Current workflow": "Current workflow", "Custom inputs": "Inputs", "Custom outputs": "Outputs", "Custom variable": "Custom variable", @@ -1148,6 +1150,11 @@ "target": "Target Data", "textarea": "Textarea" }, + "publish": { + "OnRevert version": "Click back to that version", + "OnRevert version confirm": "Are you sure to roll back the version?", + "histories": "Publiish histories" + }, "tool": { "Handle": "Tool handle", "Select Tool": "Select Tool" diff --git a/projects/app/public/locales/zh/common.json b/projects/app/i18n/zh/common.json similarity index 99% rename from projects/app/public/locales/zh/common.json rename to projects/app/i18n/zh/common.json index cd71bc55f..974456b61 100644 --- a/projects/app/public/locales/zh/common.json +++ b/projects/app/i18n/zh/common.json @@ -117,6 +117,7 @@ "Name is empty": "名称不能为空", "New Create": "新建", "Next Step": "下一步", + "No more data": "没有更多了~", "Not open": "未开启", "Number of words": "{{amount}}字", "OK": "好的", @@ -1103,6 +1104,7 @@ "Check Failed": "工作流校验失败,请检查节点是否正确填值,以及连线是否正常", "Confirm stop debug": "确认终止调试?调试信息将会不保留。", "Copy node": "已复制节点", + "Current workflow": "当前工作流", "Custom inputs": "自定义输入", "Custom outputs": "自定义输出", "Custom variable": "自定义变量", @@ -1150,6 +1152,11 @@ "target": "外部数据", "textarea": "多行输入框" }, + "publish": { + "OnRevert version": "点击回退到该版本", + "OnRevert version confirm": "确认回退该版本?", + "histories": "发布记录" + }, "tool": { "Handle": "工具连接器", "Select Tool": "选择工具" diff --git a/projects/app/next-i18next.config.js b/projects/app/next-i18next.config.js index cfa61e903..34e536154 100644 --- a/projects/app/next-i18next.config.js +++ b/projects/app/next-i18next.config.js @@ -9,7 +9,6 @@ module.exports = { locales: ['en', 'zh'], localeDetection: false }, - localePath: - typeof window === 'undefined' ? require('path').resolve('./public/locales') : '/public/locales', + localePath: typeof window === 'undefined' ? require('path').resolve('./i18n') : '/i18n', reloadOnPrerender: process.env.NODE_ENV === 'development' }; diff --git a/projects/app/src/components/PageContainer/index.tsx b/projects/app/src/components/PageContainer/index.tsx index 8284df146..16f9fee02 100644 --- a/projects/app/src/components/PageContainer/index.tsx +++ b/projects/app/src/components/PageContainer/index.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useTheme, type BoxProps } from '@chakra-ui/react'; -import MyBox from '../common/MyBox'; +import MyBox from '@fastgpt/web/components/common/MyBox'; const PageContainer = ({ children, diff --git a/projects/app/src/components/common/MyBox/index.tsx b/projects/app/src/components/common/MyBox/index.tsx deleted file mode 100644 index fd46ea788..000000000 --- a/projects/app/src/components/common/MyBox/index.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { Box, BoxProps } from '@chakra-ui/react'; -import Loading from '@fastgpt/web/components/common/MyLoading'; - -type Props = BoxProps & { - isLoading?: boolean; - text?: string; -}; - -const MyBox = ({ text, isLoading, children, ...props }: Props) => { - return ( - - {isLoading && } - {children} - - ); -}; - -export default MyBox; diff --git a/projects/app/src/components/core/dataset/QuoteItem.tsx b/projects/app/src/components/core/dataset/QuoteItem.tsx index 9da768ffc..52bd75680 100644 --- a/projects/app/src/components/core/dataset/QuoteItem.tsx +++ b/projects/app/src/components/core/dataset/QuoteItem.tsx @@ -7,7 +7,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon'; import { useTranslation } from 'next-i18next'; import MyTooltip from '@/components/MyTooltip'; import dynamic from 'next/dynamic'; -import MyBox from '@/components/common/MyBox'; +import MyBox from '@fastgpt/web/components/common/MyBox'; import { SearchScoreTypeEnum, SearchScoreTypeMap } from '@fastgpt/global/core/dataset/constants'; const InputDataModal = dynamic(() => import('@/pages/dataset/detail/components/InputDataModal')); diff --git a/projects/app/src/components/core/dataset/SelectModal.tsx b/projects/app/src/components/core/dataset/SelectModal.tsx index a0018e232..d1ea2f44a 100644 --- a/projects/app/src/components/core/dataset/SelectModal.tsx +++ b/projects/app/src/components/core/dataset/SelectModal.tsx @@ -5,7 +5,7 @@ import React, { Dispatch, useMemo, useState } from 'react'; import { useTranslation } from 'next-i18next'; import { Box } from '@chakra-ui/react'; import ParentPaths from '@/components/common/ParentPaths'; -import MyBox from '@/components/common/MyBox'; +import MyBox from '@fastgpt/web/components/common/MyBox'; type PathItemType = { parentId: string; diff --git a/projects/app/src/components/core/workflow/Flow/NodeTemplatesModal.tsx b/projects/app/src/components/core/workflow/Flow/NodeTemplatesModal.tsx index 5980a7a07..974807558 100644 --- a/projects/app/src/components/core/workflow/Flow/NodeTemplatesModal.tsx +++ b/projects/app/src/components/core/workflow/Flow/NodeTemplatesModal.tsx @@ -14,7 +14,7 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { getPreviewPluginModule } from '@/web/core/plugin/api'; import { useToast } from '@fastgpt/web/hooks/useToast'; import { getErrText } from '@fastgpt/global/common/error/utils'; -import { moduleTemplatesList } from '@fastgpt/global/core/workflow/template/constants'; +import { workflowNodeTemplateList } from '@fastgpt/web/core/workflow/constants'; import RowTabs from '@fastgpt/web/components/common/Tabs/RowTabs'; import { useWorkflowStore } from '@/web/core/workflow/store/workflow'; import { useRequest } from '@fastgpt/web/hooks/useRequest'; @@ -259,7 +259,7 @@ const RenderList = React.memo(function RenderList({ const setNodes = useContextSelector(WorkflowContext, (v) => v.setNodes); const formatTemplates = useMemo(() => { - const copy: nodeTemplateListType = JSON.parse(JSON.stringify(moduleTemplatesList)); + const copy: nodeTemplateListType = JSON.parse(JSON.stringify(workflowNodeTemplateList(t))); templates.forEach((item) => { const index = copy.findIndex((template) => template.type === item.templateType); if (index === -1) return; diff --git a/projects/app/src/components/core/workflow/components/PublishHistoriesSlider.tsx b/projects/app/src/components/core/workflow/components/PublishHistoriesSlider.tsx new file mode 100644 index 000000000..80617fde5 --- /dev/null +++ b/projects/app/src/components/core/workflow/components/PublishHistoriesSlider.tsx @@ -0,0 +1,179 @@ +import React, { useState } from 'react'; +import { getPublishList, postRevertVersion } from '@/web/core/app/versionApi'; +import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination'; +import CustomRightDrawer from '@fastgpt/web/components/common/MyDrawer/CustomRightDrawer'; +import { useTranslation } from 'next-i18next'; +import { useMemoizedFn } from 'ahooks'; +import { Box, Button, Flex } from '@chakra-ui/react'; +import { formatTime2YMDHM } from '@fastgpt/global/common/string/time'; +import { useContextSelector } from 'use-context-selector'; +import { WorkflowContext } from '../context'; +import { useAppStore } from '@/web/core/app/store/useAppStore'; +import { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; +import MyIcon from '@fastgpt/web/components/common/Icon'; +import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; +import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; +import { useRequest } from '@fastgpt/web/hooks/useRequest'; + +const PublishHistoriesSlider = () => { + const { t } = useTranslation(); + const { openConfirm, ConfirmModal } = useConfirm({ + content: t('core.workflow.publish.OnRevert version confirm') + }); + + const { appDetail, setAppDetail } = useAppStore(); + const appId = useContextSelector(WorkflowContext, (e) => e.appId); + const setIsShowVersionHistories = useContextSelector( + WorkflowContext, + (e) => e.setIsShowVersionHistories + ); + const initData = useContextSelector(WorkflowContext, (e) => e.initData); + + const [selectedHistoryId, setSelectedHistoryId] = useState(); + + const { list, ScrollList, isLoading } = useScrollPagination(getPublishList, { + itemHeight: 49, + overscan: 20, + + pageSize: 30, + defaultParams: { + appId + } + }); + + const onClose = useMemoizedFn(() => { + setIsShowVersionHistories(false); + }); + + const onPreview = useMemoizedFn((data: AppVersionSchemaType) => { + setSelectedHistoryId(data._id); + + initData({ + nodes: data.nodes, + edges: data.edges + }); + }); + const onCloseSlider = useMemoizedFn(() => { + setSelectedHistoryId(undefined); + initData({ + nodes: appDetail.modules, + edges: appDetail.edges + }); + onClose(); + }); + + const { mutate: onRevert, isLoading: isReverting } = useRequest({ + mutationFn: async (data: AppVersionSchemaType) => { + if (!appId) return; + await postRevertVersion(appId, { + versionId: data._id, + editNodes: appDetail.modules, + editEdges: appDetail.edges + }); + + setAppDetail({ + ...appDetail, + modules: data.nodes, + edges: data.edges + }); + + onCloseSlider(); + } + }); + + const showLoading = isLoading || isReverting; + + return ( + <> + + + + {list.map((data, index) => { + const item = data.data; + + return ( + onPreview(item)} + > + + + {formatTime2YMDHM(item.time)} + + {item._id === selectedHistoryId && ( + + { + e.stopPropagation(); + openConfirm(() => onRevert(item))(); + }} + /> + + )} + + ); + })} + + + + + ); +}; + +export default React.memo(PublishHistoriesSlider); diff --git a/projects/app/src/components/core/workflow/context.tsx b/projects/app/src/components/core/workflow/context.tsx index 91a21dc68..66e50982b 100644 --- a/projects/app/src/components/core/workflow/context.tsx +++ b/projects/app/src/components/core/workflow/context.tsx @@ -36,10 +36,12 @@ import { createContext } from 'use-context-selector'; import { defaultRunningStatus } from './constants'; import { checkNodeRunStatus } from '@fastgpt/global/core/workflow/runtime/utils'; import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus'; +import { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; type OnChange = (changes: ChangesType[]) => void; type WorkflowContextType = { + appId?: string; mode: 'app' | 'plugin'; basicNodeTemplates: FlowNodeTemplateType[]; filterAppIds?: string[]; @@ -83,7 +85,6 @@ type WorkflowContextType = { }; initData: (e: { nodes: StoreNodeItemType[]; edges: StoreEdgeItemType[] }) => Promise; - // debug // debug workflowDebugData: | { @@ -103,6 +104,10 @@ type WorkflowContextType = { runtimeEdges: RuntimeEdgeItemType[]; }) => Promise; onStopNodeDebug: () => void; + + // version history + isShowVersionHistories: boolean; + setIsShowVersionHistories: React.Dispatch>; }; type ContextValueProps = Pick< @@ -201,6 +206,10 @@ export const WorkflowContext = createContext({ }, onChangeNode: function (e: FlowNodeChangeProps): void { throw new Error('Function not implemented.'); + }, + isShowVersionHistories: false, + setIsShowVersionHistories: function (value: React.SetStateAction): void { + throw new Error('Function not implemented.'); } }); @@ -616,6 +625,10 @@ const WorkflowContextProvider = ({ [onNextNodeDebug, onStopNodeDebug] ); + /* Version histories */ + const [isShowVersionHistories, setIsShowVersionHistories] = useState(false); + + /* event bus */ useEffect(() => { eventBus.on(EventNameEnum.requestWorkflowStore, () => { eventBus.emit(EventNameEnum.receiveWorkflowStore, { @@ -630,6 +643,7 @@ const WorkflowContextProvider = ({ return ( {children} diff --git a/projects/app/src/global/core/app/api.d.ts b/projects/app/src/global/core/app/api.d.ts index 8e609084c..0931c75ed 100644 --- a/projects/app/src/global/core/app/api.d.ts +++ b/projects/app/src/global/core/app/api.d.ts @@ -25,3 +25,10 @@ export type PostPublishAppProps = { nodes: AppSchema['modules']; edges: AppSchema['edges']; }; + +export type PostRevertAppProps = { + versionId: string; + // edit workflow + editNodes: AppSchema['modules']; + editEdges: AppSchema['edges']; +}; diff --git a/projects/app/src/pages/account/components/BillTable.tsx b/projects/app/src/pages/account/components/BillTable.tsx index 6facc346e..d469fdc84 100644 --- a/projects/app/src/pages/account/components/BillTable.tsx +++ b/projects/app/src/pages/account/components/BillTable.tsx @@ -26,7 +26,7 @@ import { billTypeMap } from '@fastgpt/global/support/wallet/bill/constants'; // import { usePagination } from '@/web/common/hooks/usePagination'; -import MyBox from '@/components/common/MyBox'; +import MyBox from '@fastgpt/web/components/common/MyBox'; import { useRequest } from '@fastgpt/web/hooks/useRequest'; import { standardSubLevelMap, subModeMap } from '@fastgpt/global/support/wallet/sub/constants'; import MySelect from '@fastgpt/web/components/common/MySelect'; diff --git a/projects/app/src/pages/api/core/app/version/list.ts b/projects/app/src/pages/api/core/app/version/list.ts new file mode 100644 index 000000000..e213b6e94 --- /dev/null +++ b/projects/app/src/pages/api/core/app/version/list.ts @@ -0,0 +1,34 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { NextAPI } from '@/service/middle/entry'; +import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema'; +import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; +import { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; + +type Props = PaginationProps<{ + appId: string; +}>; + +type Response = PaginationResponse; + +async function handler(req: NextApiRequest, res: NextApiResponse): Promise { + const { current, pageSize, appId } = req.body as Props; + + const [result, total] = await Promise.all([ + MongoAppVersion.find({ + appId + }) + .sort({ + time: -1 + }) + .skip((current - 1) * pageSize) + .limit(pageSize), + MongoAppVersion.countDocuments({ appId }) + ]); + + return { + total, + list: result + }; +} + +export default NextAPI(handler); diff --git a/projects/app/src/pages/api/core/app/version/revert.ts b/projects/app/src/pages/api/core/app/version/revert.ts new file mode 100644 index 000000000..0b5d1efcb --- /dev/null +++ b/projects/app/src/pages/api/core/app/version/revert.ts @@ -0,0 +1,73 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { NextAPI } from '@/service/middle/entry'; +import { authApp } from '@fastgpt/service/support/permission/auth/app'; +import { MongoAppVersion } from '@fastgpt/service/core/app/versionSchema'; +import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; +import { MongoApp } from '@fastgpt/service/core/app/schema'; +import { beforeUpdateAppFormat } from '@fastgpt/service/core/app/controller'; +import { getGuideModule, splitGuideModule } from '@fastgpt/global/core/workflow/utils'; +import { getNextTimeByCronStringAndTimezone } from '@fastgpt/global/common/string/time'; +import { PostRevertAppProps } from '@/global/core/app/api'; + +type Response = {}; + +async function handler(req: NextApiRequest, res: NextApiResponse): Promise<{}> { + const { appId } = req.query as { appId: string }; + const { editNodes = [], editEdges = [], versionId } = req.body as PostRevertAppProps; + + await authApp({ appId, req, per: 'w', authToken: true }); + + const version = await MongoAppVersion.findOne({ + _id: versionId, + appId + }); + + if (!version) { + throw new Error('version not found'); + } + + const { nodes: formatEditNodes } = beforeUpdateAppFormat({ nodes: editNodes }); + + const { scheduledTriggerConfig } = splitGuideModule(getGuideModule(version.nodes)); + + await mongoSessionRun(async (session) => { + // 为编辑中的数据创建一个版本 + await MongoAppVersion.create( + [ + { + appId, + nodes: formatEditNodes, + edges: editEdges + } + ], + { session } + ); + + // 为历史版本再创建一个版本 + await MongoAppVersion.create( + [ + { + appId, + nodes: version.nodes, + edges: version.edges + } + ], + { session } + ); + + // update app + await MongoApp.findByIdAndUpdate(appId, { + modules: version.nodes, + edges: version.edges, + updateTime: new Date(), + scheduledTriggerConfig, + scheduledTriggerNextTime: scheduledTriggerConfig + ? getNextTimeByCronStringAndTimezone(scheduledTriggerConfig) + : null + }); + }); + + return {}; +} + +export default NextAPI(handler); diff --git a/projects/app/src/pages/app/detail/components/FlowEdit/Header.tsx b/projects/app/src/pages/app/detail/components/FlowEdit/Header.tsx index f7b85fccc..3416b3e95 100644 --- a/projects/app/src/pages/app/detail/components/FlowEdit/Header.tsx +++ b/projects/app/src/pages/app/detail/components/FlowEdit/Header.tsx @@ -22,12 +22,15 @@ import { } from '@/web/core/workflow/utils'; import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; -import { useQuery } from '@tanstack/react-query'; import { formatTime2HM } from '@fastgpt/global/common/string/time'; import { useContextSelector } from 'use-context-selector'; import { WorkflowContext, getWorkflowStore } from '@/components/core/workflow/context'; +import { useInterval } from 'ahooks'; const ImportSettings = dynamic(() => import('@/components/core/workflow/Flow/ImportSettings')); +const PublishHistories = dynamic( + () => import('@/components/core/workflow/components/PublishHistoriesSlider') +); type Props = { app: AppSchema; onClose: () => void }; @@ -55,7 +58,6 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({ const { openConfirm: openConfigPublish, ConfirmModal } = useConfirm({ content: t('core.app.Publish Confirm') }); - const { isOpen: isOpenImport, onOpen: onOpenImport, onClose: onCloseImport } = useDisclosure(); const { publishApp, updateAppDetail } = useAppStore(); const edges = useContextSelector(WorkflowContext, (v) => v.edges); @@ -63,6 +65,17 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({ const [saveLabel, setSaveLabel] = useState(t('core.app.Onclick to save')); const onUpdateNodeError = useContextSelector(WorkflowContext, (v) => v.onUpdateNodeError); + const { isOpen: isOpenImport, onOpen: onOpenImport, onClose: onCloseImport } = useDisclosure(); + + const isShowVersionHistories = useContextSelector( + WorkflowContext, + (v) => v.isShowVersionHistories + ); + const setIsShowVersionHistories = useContextSelector( + WorkflowContext, + (v) => v.setIsShowVersionHistories + ); + const flowData2StoreDataAndCheck = useCallback(async () => { const { nodes } = await getWorkflowStore(); const checkResults = checkWorkflowNodeAndConnection({ nodes, edges }); @@ -81,6 +94,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({ }, [edges, onUpdateNodeError, t, toast]); const onclickSave = useCallback(async () => { + if (isShowVersionHistories) return; const { nodes } = await getWorkflowStore(); if (nodes.length === 0) return null; @@ -107,7 +121,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({ setIsSaving(false); return null; - }, [updateAppDetail, app._id, edges, t]); + }, [isShowVersionHistories, edges, updateAppDetail, app._id, t]); const onclickPublish = useCallback(async () => { setIsSaving(true); @@ -160,15 +174,16 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({ } }, [copyData, flowData2StoreDataAndCheck, t]); + // effect useBeforeunload({ callback: onclickSave, tip: t('core.common.tip.leave page') }); - useQuery(['autoSave'], onclickSave, { - refetchInterval: 20 * 1000, - enabled: !!app._id - }); + useInterval(() => { + if (!app._id) return; + onclickSave(); + }, 20000); const Render = useMemo(() => { return ( @@ -180,6 +195,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({ alignItems={'center'} userSelect={'none'} bg={'myGray.25'} + h={'67px'} > - + {app.name} - - - {saveLabel} - - + {!isShowVersionHistories && ( + + + {saveLabel} + + + )} @@ -217,7 +235,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({ } aria-label={''} size={'sm'} @@ -238,10 +256,19 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({ ]} /> - } + aria-label={''} size={'sm'} - leftIcon={} + w={'30px'} + variant={'whitePrimary'} + onClick={() => setIsShowVersionHistories(true)} + /> + + } variant={'whitePrimary'} onClick={async () => { const data = await flowData2StoreDataAndCheck(); @@ -250,17 +277,20 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({ } }} > - {t('core.Chat test')} + {t('core.workflow.Debug')} - } - onClick={openConfigPublish(onclickPublish)} - > - {t('core.app.Publish')} - + {!isShowVersionHistories && ( + } + onClick={openConfigPublish(onclickPublish)} + > + {t('core.app.Publish')} + + )} @@ -275,8 +305,10 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({ onclickPublish, onclickSave, openConfigPublish, + isShowVersionHistories, saveAndBack, saveLabel, + setIsShowVersionHistories, setWorkflowTestData, t, theme.borders.base @@ -286,6 +318,7 @@ const RenderHeaderContainer = React.memo(function RenderHeaderContainer({ <> {Render} {isOpenImport && } + {isShowVersionHistories && } ); }); diff --git a/projects/app/src/pages/app/detail/components/Logs.tsx b/projects/app/src/pages/app/detail/components/Logs.tsx index 96bda9538..4f7542b95 100644 --- a/projects/app/src/pages/app/detail/components/Logs.tsx +++ b/projects/app/src/pages/app/detail/components/Logs.tsx @@ -28,7 +28,7 @@ import { getInitChatInfo } from '@/web/core/chat/api'; import Tag from '@fastgpt/web/components/common/Tag/index'; import MyModal from '@fastgpt/web/components/common/MyModal'; import { addDays } from 'date-fns'; -import MyBox from '@/components/common/MyBox'; +import MyBox from '@fastgpt/web/components/common/MyBox'; import { usePagination } from '@fastgpt/web/hooks/usePagination'; import DateRangePicker, { DateRangeType } from '@fastgpt/web/components/common/DateRangePicker'; import { formatChatValue2InputType } from '@/components/ChatBox/utils'; diff --git a/projects/app/src/pages/app/detail/components/SimpleEdit/EditForm.tsx b/projects/app/src/pages/app/detail/components/SimpleEdit/EditForm.tsx index e3aaf0eb1..eab41769a 100644 --- a/projects/app/src/pages/app/detail/components/SimpleEdit/EditForm.tsx +++ b/projects/app/src/pages/app/detail/components/SimpleEdit/EditForm.tsx @@ -1,4 +1,4 @@ -import React, { useMemo, useTransition } from 'react'; +import React, { useEffect, useMemo, useTransition } from 'react'; import { Box, Flex, Grid, BoxProps, useTheme, useDisclosure, Button } from '@chakra-ui/react'; import { AddIcon, QuestionOutlineIcon, SmallAddIcon } from '@chakra-ui/icons'; import { useFieldArray, UseFormReturn } from 'react-hook-form'; @@ -28,6 +28,7 @@ import type { SettingAIDataType } from '@fastgpt/global/core/app/type.d'; import DeleteIcon, { hoverDeleteStyles } from '@fastgpt/web/components/common/Icon/delete'; import { TTSTypeEnum } from '@/constants/app'; import { getSystemVariables } from '@/web/core/app/utils'; +import { useUpdate } from 'ahooks'; const DatasetSelectModal = dynamic(() => import('@/components/core/app/DatasetSelectModal')); const DatasetParamsModal = dynamic(() => import('@/components/core/app/DatasetParamsModal')); @@ -65,6 +66,7 @@ const EditForm = ({ const { allDatasets } = useDatasetStore(); const { llmModelList } = useSystemStore(); const [, startTst] = useTransition(); + const refresh = useUpdate(); const { setValue, getValues, handleSubmit, control, watch } = editForm; @@ -72,7 +74,6 @@ const EditForm = ({ control, name: 'dataset.datasets' }); - const selectedTools = watch('selectedTools'); const { isOpen: isOpenDatasetSelect, @@ -106,6 +107,7 @@ const EditForm = ({ const tts = getValues('userGuide.tts'); const whisperConfig = getValues('userGuide.whisper'); const postQuestionGuide = getValues('userGuide.questionGuide'); + const selectedTools = watch('selectedTools'); const selectDatasets = useMemo( () => allDatasets.filter((item) => datasets.find((dataset) => dataset.datasetId === item._id)), @@ -131,6 +133,16 @@ const EditForm = ({ errorToast: t('common.Save Failed') }); + useEffect(() => { + const wat = watch((data) => { + refresh(); + }); + + return () => { + wat.unsubscribe(); + }; + }, []); + return ( {/* title */} @@ -459,7 +471,9 @@ const EditForm = ({ {isOpenToolsSelect && ( setValue('selectedTools', [...selectedTools, e])} + onAddTool={(e) => { + setValue('selectedTools', [...selectedTools, e]); + }} onRemoveTool={(e) => { setValue( 'selectedTools', diff --git a/projects/app/src/pages/app/detail/components/SimpleEdit/ToolSelectModal.tsx b/projects/app/src/pages/app/detail/components/SimpleEdit/ToolSelectModal.tsx index 70b041dfb..9c52f1bc5 100644 --- a/projects/app/src/pages/app/detail/components/SimpleEdit/ToolSelectModal.tsx +++ b/projects/app/src/pages/app/detail/components/SimpleEdit/ToolSelectModal.tsx @@ -29,7 +29,7 @@ import Avatar from '@/components/Avatar'; import MyIcon from '@fastgpt/web/components/common/Icon'; import { AddIcon } from '@chakra-ui/icons'; import { getPreviewPluginModule } from '@/web/core/plugin/api'; -import MyBox from '@/components/common/MyBox'; +import MyBox from '@fastgpt/web/components/common/MyBox'; import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants'; import ParentPaths from '@/components/common/ParentPaths'; import { PluginTypeEnum } from '@fastgpt/global/core/plugin/constants'; diff --git a/projects/app/src/pages/app/list/index.tsx b/projects/app/src/pages/app/list/index.tsx index 016c74661..15da16e10 100644 --- a/projects/app/src/pages/app/list/index.tsx +++ b/projects/app/src/pages/app/list/index.tsx @@ -23,7 +23,6 @@ const MyApps = () => { const router = useRouter(); const { userInfo } = useUserStore(); const { myApps, loadMyApps } = useAppStore(); - const [teamsTags, setTeamTags] = useState([]); const { openConfirm, ConfirmModal } = useConfirm({ title: '删除提示', content: '确认删除该应用所有信息?' diff --git a/projects/app/src/pages/chat/share.tsx b/projects/app/src/pages/chat/share.tsx index da481ea7f..7f83646f2 100644 --- a/projects/app/src/pages/chat/share.tsx +++ b/projects/app/src/pages/chat/share.tsx @@ -26,7 +26,7 @@ import { getInitOutLinkChatInfo } from '@/web/core/chat/api'; import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils'; import { useChatStore } from '@/web/core/chat/storeChat'; import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants'; -import MyBox from '@/components/common/MyBox'; +import MyBox from '@fastgpt/web/components/common/MyBox'; import { MongoOutLink } from '@fastgpt/service/support/outLink/schema'; import { OutLinkWithAppType } from '@fastgpt/global/support/outLink/type'; import { addLog } from '@fastgpt/service/common/system/log'; diff --git a/projects/app/src/pages/chat/team.tsx b/projects/app/src/pages/chat/team.tsx index 285e0d2fd..ba2ceb8dd 100644 --- a/projects/app/src/pages/chat/team.tsx +++ b/projects/app/src/pages/chat/team.tsx @@ -32,7 +32,7 @@ import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d'; import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils'; import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants'; import { getErrText } from '@fastgpt/global/common/error/utils'; -import MyBox from '@/components/common/MyBox'; +import MyBox from '@fastgpt/web/components/common/MyBox'; import SliderApps from './components/SliderApps'; import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt'; diff --git a/projects/app/src/pages/dataset/detail/components/CollectionCard.tsx b/projects/app/src/pages/dataset/detail/components/CollectionCard.tsx index 39c145e1c..87896ef17 100644 --- a/projects/app/src/pages/dataset/detail/components/CollectionCard.tsx +++ b/projects/app/src/pages/dataset/detail/components/CollectionCard.tsx @@ -60,7 +60,7 @@ import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant'; import { useDatasetStore } from '@/web/core/dataset/store/dataset'; import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type'; import { DatasetCollectionSyncResultEnum } from '@fastgpt/global/core/dataset/constants'; -import MyBox from '@/components/common/MyBox'; +import MyBox from '@fastgpt/web/components/common/MyBox'; import { usePagination } from '@fastgpt/web/hooks/usePagination'; import { ImportDataSourceEnum } from '@fastgpt/global/core/dataset/constants'; diff --git a/projects/app/src/pages/dataset/detail/components/Import/components/FileSelector.tsx b/projects/app/src/pages/dataset/detail/components/Import/components/FileSelector.tsx index dc709c5f9..650503900 100644 --- a/projects/app/src/pages/dataset/detail/components/Import/components/FileSelector.tsx +++ b/projects/app/src/pages/dataset/detail/components/Import/components/FileSelector.tsx @@ -1,4 +1,4 @@ -import MyBox from '@/components/common/MyBox'; +import MyBox from '@fastgpt/web/components/common/MyBox'; import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; import { useToast } from '@fastgpt/web/hooks/useToast'; import { Box, FlexProps } from '@chakra-ui/react'; diff --git a/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx b/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx index d74bbafd5..4114ecbd7 100644 --- a/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx +++ b/projects/app/src/pages/dataset/detail/components/InputDataModal.tsx @@ -23,7 +23,7 @@ import DeleteIcon from '@fastgpt/web/components/common/Icon/delete'; import { defaultCollectionDetail } from '@/constants/dataset'; import { getDocPath } from '@/web/common/system/doc'; import RawSourceBox from '@/components/core/dataset/RawSourceBox'; -import MyBox from '@/components/common/MyBox'; +import MyBox from '@fastgpt/web/components/common/MyBox'; import { getErrText } from '@fastgpt/global/common/error/utils'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; diff --git a/projects/app/src/pages/dataset/detail/index.tsx b/projects/app/src/pages/dataset/detail/index.tsx index df8377509..f4d8534e1 100644 --- a/projects/app/src/pages/dataset/detail/index.tsx +++ b/projects/app/src/pages/dataset/detail/index.tsx @@ -27,7 +27,7 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { useRequest } from '@fastgpt/web/hooks/useRequest'; import DatasetTypeTag from '@/components/core/dataset/DatasetTypeTag'; import Head from 'next/head'; -import MyBox from '@/components/common/MyBox'; +import MyBox from '@fastgpt/web/components/common/MyBox'; const DataCard = dynamic(() => import('./components/DataCard')); const Test = dynamic(() => import('./components/Test')); diff --git a/projects/app/src/types/i18n.d.ts b/projects/app/src/types/i18n.d.ts index 4db8b6a81..4c571e349 100644 --- a/projects/app/src/types/i18n.d.ts +++ b/projects/app/src/types/i18n.d.ts @@ -1,5 +1,5 @@ import 'i18next'; -//import common from '../../public/locales/en/common.json'; +//import common from '../../i18n/en/common.json'; interface I18nNamespaces { common: any; diff --git a/projects/app/src/web/core/app/store/useAppStore.ts b/projects/app/src/web/core/app/store/useAppStore.ts index 8f6bec3b3..6e5a8acda 100644 --- a/projects/app/src/web/core/app/store/useAppStore.ts +++ b/projects/app/src/web/core/app/store/useAppStore.ts @@ -16,6 +16,7 @@ type State = { updateAppDetail(appId: string, data: AppUpdateParams): Promise; publishApp(appId: string, data: PostPublishAppProps): Promise; clearAppModules(): void; + setAppDetail(data: AppDetailType): void; }; export const useAppStore = create()( @@ -61,6 +62,11 @@ export const useAppStore = create()( }; }); }, + setAppDetail(data: AppDetailType) { + set((state) => { + state.appDetail = data; + }); + }, clearAppModules() { set((state) => { diff --git a/projects/app/src/web/core/app/versionApi.ts b/projects/app/src/web/core/app/versionApi.ts index 1d1657dc7..d6c446bc1 100644 --- a/projects/app/src/web/core/app/versionApi.ts +++ b/projects/app/src/web/core/app/versionApi.ts @@ -1,5 +1,13 @@ -import { PostPublishAppProps } from '@/global/core/app/api'; +import { PostPublishAppProps, PostRevertAppProps } from '@/global/core/app/api'; import { GET, POST, DELETE, PUT } from '@/web/common/api/request'; +import { AppVersionSchemaType } from '@fastgpt/global/core/app/version'; +import { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fetch/type'; export const postPublishApp = (appId: string, data: PostPublishAppProps) => POST(`/core/app/version/publish?appId=${appId}`, data); + +export const getPublishList = (data: PaginationProps<{ appId: string }>) => + POST>('/core/app/version/list', data); + +export const postRevertVersion = (appId: string, data: PostRevertAppProps) => + POST(`/core/app/version/revert?appId=${appId}`, data);