mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 05:12:39 +00:00
Feat: Node latest version (#4905)
* node versions add keep the latest option (#4899) * node versions add keep the latest option * i18n * perf: version code * fix: ts * hide system version * hide system version * hide system version * fix: ts * fix: ts --------- Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
2
projects/app/src/global/core/chat/api.d.ts
vendored
2
projects/app/src/global/core/chat/api.d.ts
vendored
@@ -24,7 +24,7 @@ export type GetChatRecordsProps = OutLinkChatAuthProps & {
|
||||
appId: string;
|
||||
chatId?: string;
|
||||
loadCustomFeedbacks?: boolean;
|
||||
type: `${GetChatTypeEnum}`;
|
||||
type?: `${GetChatTypeEnum}`;
|
||||
};
|
||||
|
||||
export type InitOutLinkChatProps = {
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useMemo, useRef } from 'react';
|
||||
import { Box, Button, Flex, HStack, useDisclosure, type FlexProps } from '@chakra-ui/react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { Box, Button, Flex, useDisclosure, type FlexProps } from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import type { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node.d';
|
||||
@@ -15,7 +15,7 @@ import { ToolSourceHandle, ToolTargetHandle } from './Handle/ToolHandle';
|
||||
import { useEditTextarea } from '@fastgpt/web/hooks/useEditTextarea';
|
||||
import { ConnectionSourceHandle, ConnectionTargetHandle } from './Handle/ConnectionHandle';
|
||||
import { useDebug } from '../../hooks/useDebug';
|
||||
import { getPreviewPluginNode } from '@/web/core/app/api/plugin';
|
||||
import { getPreviewPluginNode, getToolVersionList } from '@/web/core/app/api/plugin';
|
||||
import { storeNode2FlowNode } from '@/web/core/workflow/utils';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
@@ -104,12 +104,9 @@ const NodeCard = (props: Props) => {
|
||||
}, [nodeList, nodeId]);
|
||||
const isAppNode = node && AppNodeFlowNodeTypeMap[node?.flowNodeType];
|
||||
const showVersion = useMemo(() => {
|
||||
if (!isAppNode || !node?.pluginId) return false;
|
||||
if (!isAppNode || !node?.pluginId || node?.pluginData?.error) return false;
|
||||
if ([FlowNodeTypeEnum.tool, FlowNodeTypeEnum.toolSet].includes(node.flowNodeType)) return false;
|
||||
if (node.pluginId.split('-').length > 1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return typeof node.version === 'string';
|
||||
}, [isAppNode, node]);
|
||||
|
||||
const { data: nodeTemplate } = useRequest2(
|
||||
@@ -617,11 +614,10 @@ const NodeVersion = React.memo(function NodeVersion({ node }: { node: FlowNodeIt
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
// Load version list
|
||||
const { ScrollData, data: versionList } = useScrollPagination(getAppVersionList, {
|
||||
const { ScrollData, data: versionList } = useScrollPagination(getToolVersionList, {
|
||||
pageSize: 20,
|
||||
params: {
|
||||
appId: node.pluginId,
|
||||
isPublish: true
|
||||
toolId: node.pluginId
|
||||
},
|
||||
refreshDeps: [node.pluginId, isOpen],
|
||||
disalbed: !isOpen,
|
||||
@@ -653,18 +649,23 @@ const NodeVersion = React.memo(function NodeVersion({ node }: { node: FlowNodeIt
|
||||
}
|
||||
);
|
||||
|
||||
const renderList = useCreation(
|
||||
() =>
|
||||
versionList.map((item) => ({
|
||||
const renderVersionList = useCreation(
|
||||
() => [
|
||||
{
|
||||
label: t('app:keep_the_latest'),
|
||||
value: ''
|
||||
},
|
||||
...versionList.map((item) => ({
|
||||
label: item.versionName,
|
||||
value: item._id
|
||||
})),
|
||||
}))
|
||||
],
|
||||
[node.isLatestVersion, node.version, t, versionList]
|
||||
);
|
||||
const valueLabel = useMemo(() => {
|
||||
return (
|
||||
<Flex alignItems={'center'} gap={0.5}>
|
||||
{node?.versionLabel}
|
||||
{node?.version === '' ? t('app:keep_the_latest') : node?.versionLabel}
|
||||
{!node.isLatestVersion && (
|
||||
<MyTag type="fill" colorSchema={'adora'} fontSize={'mini'} borderRadius={'lg'}>
|
||||
{t('app:not_the_newest')}
|
||||
@@ -672,7 +673,7 @@ const NodeVersion = React.memo(function NodeVersion({ node }: { node: FlowNodeIt
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
}, [node.isLatestVersion, node?.versionLabel, t]);
|
||||
}, [node.isLatestVersion, node?.version, node?.versionLabel, t]);
|
||||
|
||||
return (
|
||||
<MySelect
|
||||
@@ -685,7 +686,7 @@ const NodeVersion = React.memo(function NodeVersion({ node }: { node: FlowNodeIt
|
||||
placeholder={node?.versionLabel}
|
||||
variant={'whitePrimaryOutline'}
|
||||
size={'sm'}
|
||||
list={renderList}
|
||||
list={renderVersionList}
|
||||
ScrollData={(props) => (
|
||||
<ScrollData minH={'100px'} maxH={'40vh'}>
|
||||
{props.children}
|
||||
|
@@ -3,7 +3,6 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import { checkNode } from '@/service/core/app/utils';
|
||||
import { rewriteAppWorkflowToDetail } from '@fastgpt/service/core/app/utils';
|
||||
/* 获取应用详情 */
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
@@ -23,6 +22,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
await rewriteAppWorkflowToDetail({
|
||||
nodes: app.modules,
|
||||
teamId,
|
||||
ownerTmbId: app.tmbId,
|
||||
isRoot
|
||||
});
|
||||
|
||||
@@ -34,12 +34,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...app,
|
||||
modules: await Promise.all(
|
||||
app.modules.map((node) => checkNode({ node, ownerTmbId: app.tmbId }))
|
||||
)
|
||||
};
|
||||
return app;
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
@@ -4,7 +4,7 @@
|
||||
import type { NextApiResponse } from 'next';
|
||||
import {
|
||||
getChildAppPreviewNode,
|
||||
splitCombinePluginId
|
||||
splitCombineToolId
|
||||
} from '@fastgpt/service/core/app/plugin/controller';
|
||||
import { type FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node.d';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
@@ -21,7 +21,7 @@ async function handler(
|
||||
): Promise<FlowNodeTemplateType> {
|
||||
const { appId, versionId } = req.query;
|
||||
|
||||
const { source } = await splitCombinePluginId(appId);
|
||||
const { source } = splitCombineToolId(appId);
|
||||
|
||||
if (source === PluginSourceEnum.personal) {
|
||||
await authApp({ req, authToken: true, appId, per: ReadPermissionVal });
|
||||
|
86
projects/app/src/pages/api/core/app/plugin/getVersionList.ts
Normal file
86
projects/app/src/pages/api/core/app/plugin/getVersionList.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import type { NextApiResponse } from 'next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema';
|
||||
import { type PaginationProps, type PaginationResponse } from '@fastgpt/web/common/fetch/type';
|
||||
import { type ApiRequestProps } from '@fastgpt/service/type/next';
|
||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
|
||||
import { splitCombineToolId } from '@fastgpt/service/core/app/plugin/controller';
|
||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||
import { getSystemPluginTemplates } from '@fastgpt/plugins/register';
|
||||
import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin';
|
||||
import { Types } from '@fastgpt/service/common/mongo';
|
||||
|
||||
export type getToolVersionListProps = PaginationProps<{
|
||||
toolId?: string;
|
||||
}>;
|
||||
|
||||
export type getToolVersionResponse = PaginationResponse<{
|
||||
_id: string;
|
||||
versionName: string;
|
||||
}>;
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<getToolVersionListProps>,
|
||||
_res: NextApiResponse<any>
|
||||
): Promise<getToolVersionResponse> {
|
||||
const { toolId } = req.body;
|
||||
const { offset, pageSize } = parsePaginationRequest(req);
|
||||
|
||||
if (!toolId) {
|
||||
return {
|
||||
total: 0,
|
||||
list: []
|
||||
};
|
||||
}
|
||||
|
||||
const { source, pluginId: formatToolId } = splitCombineToolId(toolId);
|
||||
|
||||
// Auth
|
||||
const appId = await (async () => {
|
||||
if (source === PluginSourceEnum.personal) {
|
||||
const { app } = await authApp({
|
||||
appId: formatToolId,
|
||||
req,
|
||||
per: ReadPermissionVal,
|
||||
authToken: true
|
||||
});
|
||||
return app._id;
|
||||
} else {
|
||||
const item = getSystemPluginTemplates().find((plugin) => plugin.id === formatToolId);
|
||||
if (!item) return Promise.reject(PluginErrEnum.unAuth);
|
||||
return item.associatedPluginId;
|
||||
}
|
||||
})();
|
||||
|
||||
if (!appId || !Types.ObjectId.isValid(appId)) {
|
||||
return {
|
||||
total: 0,
|
||||
list: []
|
||||
};
|
||||
}
|
||||
|
||||
const match = {
|
||||
appId,
|
||||
isPublish: true
|
||||
};
|
||||
|
||||
const [result, total] = await Promise.all([
|
||||
await MongoAppVersion.find(match, 'versionName')
|
||||
.sort({
|
||||
time: -1
|
||||
})
|
||||
.skip(offset)
|
||||
.limit(pageSize)
|
||||
.lean(),
|
||||
MongoAppVersion.countDocuments(match)
|
||||
]);
|
||||
|
||||
return {
|
||||
total,
|
||||
list: result
|
||||
};
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
@@ -5,7 +5,6 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { type AppVersionSchemaType } from '@fastgpt/global/core/app/version';
|
||||
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
|
||||
import { checkNode } from '@/service/core/app/utils';
|
||||
import { rewriteAppWorkflowToDetail } from '@fastgpt/service/core/app/utils';
|
||||
|
||||
type Props = {
|
||||
@@ -34,14 +33,12 @@ async function handler(
|
||||
await rewriteAppWorkflowToDetail({
|
||||
nodes: result.nodes,
|
||||
teamId,
|
||||
ownerTmbId: app.tmbId,
|
||||
isRoot
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
nodes: await Promise.all(
|
||||
result.nodes.map((n) => checkNode({ node: n, ownerTmbId: app.tmbId }))
|
||||
),
|
||||
versionName: result?.versionName || formatTime2YMDHM(result?.time)
|
||||
};
|
||||
}
|
||||
|
@@ -31,13 +31,16 @@ async function handler(
|
||||
per: WritePermissionVal
|
||||
});
|
||||
|
||||
const version = await getAppLatestVersion(req.query.appId, app);
|
||||
|
||||
await rewriteAppWorkflowToDetail({
|
||||
nodes: app.modules,
|
||||
nodes: version.nodes,
|
||||
teamId,
|
||||
isRoot
|
||||
isRoot,
|
||||
ownerTmbId: app.tmbId
|
||||
});
|
||||
|
||||
return getAppLatestVersion(req.query.appId, app);
|
||||
return version;
|
||||
}
|
||||
|
||||
export default NextAPI(handler);
|
||||
|
@@ -24,7 +24,7 @@ import { saveChat } from '@fastgpt/service/core/chat/saveChat';
|
||||
import { getAppLatestVersion } from '@fastgpt/service/core/app/version/controller';
|
||||
import {
|
||||
getChildAppPreviewNode,
|
||||
splitCombinePluginId
|
||||
splitCombineToolId
|
||||
} from '@fastgpt/service/core/app/plugin/controller';
|
||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||
import { authAppByTmbId } from '@fastgpt/service/support/permission/app/auth';
|
||||
@@ -137,46 +137,3 @@ export const getScheduleTriggerApp = async () => {
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
export const checkNode = async ({
|
||||
node,
|
||||
ownerTmbId
|
||||
}: {
|
||||
node: StoreNodeItemType;
|
||||
ownerTmbId: string;
|
||||
}): Promise<StoreNodeItemType> => {
|
||||
const pluginId = node.pluginId;
|
||||
if (!pluginId) return node;
|
||||
|
||||
try {
|
||||
const { source } = await splitCombinePluginId(pluginId);
|
||||
|
||||
if (source === PluginSourceEnum.personal) {
|
||||
await authAppByTmbId({
|
||||
tmbId: ownerTmbId,
|
||||
appId: pluginId,
|
||||
per: ReadPermissionVal
|
||||
});
|
||||
}
|
||||
|
||||
const preview = await getChildAppPreviewNode({ appId: pluginId });
|
||||
return {
|
||||
...node,
|
||||
pluginData: {
|
||||
version: preview.version,
|
||||
diagram: preview.diagram,
|
||||
userGuide: preview.userGuide,
|
||||
courseUrl: preview.courseUrl,
|
||||
name: preview.name,
|
||||
avatar: preview.avatar
|
||||
}
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
...node,
|
||||
pluginData: {
|
||||
error: getErrText(error)
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@@ -24,6 +24,10 @@ import { type McpToolConfigType } from '@fastgpt/global/core/app/type';
|
||||
import type { updateMCPToolsBody } from '@/pages/api/core/app/mcpTools/update';
|
||||
import type { RunMCPToolBody } from '@/pages/api/support/mcp/client/runTool';
|
||||
import type { getMCPToolsBody } from '@/pages/api/support/mcp/client/getTools';
|
||||
import type {
|
||||
getToolVersionListProps,
|
||||
getToolVersionResponse
|
||||
} from '@/pages/api/core/app/plugin/getVersionList';
|
||||
|
||||
/* ============ team plugin ============== */
|
||||
export const getTeamPlugTemplates = (data?: ListAppBody) =>
|
||||
@@ -71,6 +75,9 @@ export const getSystemPluginPaths = (data: GetPathProps) => {
|
||||
export const getPreviewPluginNode = (data: GetPreviewNodeQuery) =>
|
||||
GET<FlowNodeTemplateType>('/core/app/plugin/getPreviewNode', data);
|
||||
|
||||
export const getToolVersionList = (data: getToolVersionListProps) =>
|
||||
POST<getToolVersionResponse>('/core/app/plugin/getVersionList', data);
|
||||
|
||||
/* ============ mcp tools ============== */
|
||||
export const postCreateMCPTools = (data: createMCPToolsBody) =>
|
||||
POST('/core/app/mcpTools/create', data);
|
||||
|
@@ -51,7 +51,7 @@ const ChatRecordContextProvider = ({
|
||||
params
|
||||
}: {
|
||||
children: ReactNode;
|
||||
params: Record<string, any>;
|
||||
params: Omit<getPaginationRecordsBody, 'offset' | 'pageSize'>;
|
||||
}) => {
|
||||
const ChatBoxRef = useContextSelector(ChatItemContext, (v) => v.ChatBoxRef);
|
||||
const [isChatRecordsLoaded, setIsChatRecordsLoaded] = useState(false);
|
||||
|
Reference in New Issue
Block a user