Test shorurl (#4686)

* Short-chain burying (#4678)

* TrackRegisterParams

* 新增工作流导入功能,支持从URL获取工作流JSON数据并创建应用。实现了URL验证、CORS处理、剪贴板读取等功能,确保用户能够顺利导入工作流数据。

* 更新工作流导入功能,将导入逻辑从utils模块迁移至workflow模块,并修正相关导入路径。此更改有助于代码结构的清晰和模块化。

* 优化工作流导入组件,重构导入逻辑,增加从URL获取工作流数据的功能,并实现JSON配置导入窗口。修复了状态管理和错误处理,提升用户体验。

* 更新工作流导入功能,增加对UTM参数的支持,优化从URL获取工作流数据的逻辑,并重构相关API接口。修复了状态管理和错误处理,提升了用户体验。

* 更新创建应用的API接口,将UTM参数的字段名称从`shorUrlId`和`projectCode`修改为`shorUrlPlatform`和`shorUrlProjectCode`,以提高代码的可读性和一致性。

* impoter json

* Optimize the logic

* delete some console

* fix

* perf: sem code

---------

Co-authored-by: dreamer6680 <1468683855@qq.com>
This commit is contained in:
Archer
2025-04-27 22:56:42 +08:00
committed by GitHub
parent 5357aa402b
commit 659b8b1106
17 changed files with 285 additions and 61 deletions

View File

@@ -5,6 +5,7 @@ import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils'
import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constants';
import type { AppSchema } from '@fastgpt/global/core/app/type';
import { defaultNodeVersion } from '@fastgpt/global/core/workflow/node/constant';
import { ShortUrlParams } from '@fastgpt/global/support/marketing/type';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant';
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
@@ -27,10 +28,11 @@ export type CreateAppBody = {
modules: AppSchema['modules'];
edges?: AppSchema['edges'];
chatConfig?: AppSchema['chatConfig'];
utmParams?: ShortUrlParams;
};
async function handler(req: ApiRequestProps<CreateAppBody>) {
const { parentId, name, avatar, type, modules, edges, chatConfig } = req.body;
const { parentId, name, avatar, type, modules, edges, chatConfig, utmParams } = req.body;
if (!name || !type || !Array.isArray(modules)) {
return Promise.reject(CommonErrEnum.inheritPermissionError);
@@ -66,7 +68,9 @@ async function handler(req: ApiRequestProps<CreateAppBody>) {
type,
uid: userId,
teamId,
tmbId
tmbId,
appId,
...utmParams
});
return appId;

View File

@@ -0,0 +1,51 @@
import { NextAPI } from '@/service/middleware/entry';
import { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import axios from 'axios';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
export type FetchWorkflowBody = {
url: string;
};
export type FetchWorkflowQuery = {
url: string;
};
export type FetchWorkflowResponseType = ApiResponseType<{
data: JSON;
}>;
async function handler(
req: ApiRequestProps<FetchWorkflowBody, FetchWorkflowQuery>,
res: FetchWorkflowResponseType
) {
await authCert({ req, authToken: true });
const url = req.body?.url || req.query?.url;
if (!url) {
return Promise.reject('app:type.error.URLempty');
}
const response = await axios.get(url, {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'User-Agent': 'Mozilla/5.0 (compatible; FastGPT/1.0)'
},
timeout: 30000,
validateStatus: (status) => status < 500
});
const contentType = response.headers['content-type'] || '';
if (!response.data || response.data.length === 0) {
return Promise.reject('app:type.error.workflowresponseempty');
}
JSON.parse(JSON.stringify(response.data));
return response.data;
}
export default NextAPI(handler);

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react';
import React, { useMemo, useState, useEffect } from 'react';
import { Box, Flex, Button, useDisclosure, Input, InputGroup } from '@chakra-ui/react';
import { AddIcon } from '@chakra-ui/icons';
import { serviceSideProps } from '@/web/common/i18n/utils';
@@ -33,6 +33,8 @@ import { PermissionValueType } from '@fastgpt/global/support/permission/type';
import DashboardContainer from '@/pageComponents/dashboard/Container';
import List from '@/pageComponents/dashboard/apps/List';
import MCPToolsEditModal from '@/pageComponents/dashboard/apps/MCPToolsEditModal';
import { getUtmWorkflow } from '@/web/support/marketing/utils';
import { useMount } from 'ahooks';
const CreateModal = dynamic(() => import('@/pageComponents/dashboard/apps/CreateModal'));
const EditFolderModal = dynamic(
@@ -71,12 +73,20 @@ const MyApps = ({ MenuIcon }: { MenuIcon: JSX.Element }) => {
onOpen: onOpenCreateMCPTools,
onClose: onCloseCreateMCPTools
} = useDisclosure();
const [editFolder, setEditFolder] = useState<EditFolderFormType>();
const {
isOpen: isOpenJsonImportModal,
onOpen: onOpenJsonImportModal,
onClose: onCloseJsonImportModal
} = useDisclosure();
const [editFolder, setEditFolder] = useState<EditFolderFormType>();
//if there is a workflow url in the session storage, open the json import modal and import the workflow
useMount(() => {
if (getUtmWorkflow()) {
onOpenJsonImportModal();
}
});
const { runAsync: onCreateFolder } = useRequest2(postCreateAppFolder, {
onSuccess() {

View File

@@ -30,6 +30,7 @@ import { getDocPath } from '@/web/common/system/doc';
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
import LoginForm from '@/pageComponents/login/LoginForm/LoginForm';
import { useToast } from '@fastgpt/web/hooks/useToast';
import { getBdVId } from '@/web/support/marketing/utils';
const RegisterForm = dynamic(() => import('@/pageComponents/login/RegisterForm'));
const ForgetPasswordForm = dynamic(() => import('@/pageComponents/login/ForgetPasswordForm'));
@@ -64,7 +65,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
setUserInfo(res.user);
const decodeLastRoute = decodeURIComponent(lastRoute);
// 检查是否是当前的 route
const navigateTo =
decodeLastRoute && !decodeLastRoute.includes('/login')
? decodeLastRoute
@@ -90,7 +91,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
/* default login type */
useEffect(() => {
const bd_vid = sessionStorage.getItem('bd_vid');
const bd_vid = getBdVId();
if (bd_vid) {
setPageType(LoginPageTypeEnum.passwordLogin);
return;

View File

@@ -11,6 +11,13 @@ import { serviceSideProps } from '@/web/common/i18n/utils';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useTranslation } from 'next-i18next';
import { OAuthEnum } from '@fastgpt/global/support/user/constant';
import {
getBdVId,
getFastGPTSem,
getInviterId,
getSourceDomain,
removeFastGPTSem
} from '@/web/support/marketing/utils';
let isOauthLogging = false;
@@ -40,18 +47,10 @@ const provider = () => {
type: loginStore?.provider || OAuthEnum.sso,
props,
callbackUrl: `${location.origin}/login/provider`,
inviterId: localStorage.getItem('inviterId') || undefined,
bd_vid: sessionStorage.getItem('bd_vid') || undefined,
fastgpt_sem: (() => {
try {
return sessionStorage.getItem('fastgpt_sem')
? JSON.parse(sessionStorage.getItem('fastgpt_sem')!)
: undefined;
} catch {
return undefined;
}
})(),
sourceDomain: sessionStorage.getItem('sourceDomain') || undefined
inviterId: getInviterId(),
bd_vid: getBdVId(),
fastgpt_sem: getFastGPTSem(),
sourceDomain: getSourceDomain()
});
if (!res) {
@@ -63,6 +62,8 @@ const provider = () => {
router.replace('/login');
}, 1000);
}
removeFastGPTSem();
loginSuccess(res);
} catch (error) {
toast({