mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 21:13:50 +00:00
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:
14
packages/global/support/marketing/type.d.ts
vendored
Normal file
14
packages/global/support/marketing/type.d.ts
vendored
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export type ShortUrlParams = {
|
||||||
|
shortUrlSource?: string; // Article, video
|
||||||
|
shortUrlMedium?: string; // bilibili, youtube
|
||||||
|
shortUrlContent?: string; // project id
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TrackRegisterParams = {
|
||||||
|
inviterId?: string;
|
||||||
|
bd_vid?: string;
|
||||||
|
fastgpt_sem?: {
|
||||||
|
keyword?: string;
|
||||||
|
} & ShortUrlParams;
|
||||||
|
sourceDomain?: string;
|
||||||
|
};
|
10
packages/global/support/user/login/api.d.ts
vendored
10
packages/global/support/user/login/api.d.ts
vendored
@@ -1,16 +1,10 @@
|
|||||||
|
import { TrackRegisterParams } from '../../marketing/type';
|
||||||
|
|
||||||
export type GetWXLoginQRResponse = {
|
export type GetWXLoginQRResponse = {
|
||||||
code: string;
|
code: string;
|
||||||
codeUrl: string;
|
codeUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TrackRegisterParams = {
|
|
||||||
inviterId?: string;
|
|
||||||
bd_vid?: string;
|
|
||||||
fastgpt_sem?: {
|
|
||||||
keyword: string;
|
|
||||||
};
|
|
||||||
sourceDomain?: string;
|
|
||||||
};
|
|
||||||
export type AccountRegisterBody = {
|
export type AccountRegisterBody = {
|
||||||
username: string;
|
username: string;
|
||||||
code: string;
|
code: string;
|
||||||
|
@@ -6,6 +6,7 @@ import { OAuthEnum } from '@fastgpt/global/support/user/constant';
|
|||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||||
import { getAppLatestVersion } from '../../../core/app/version/controller';
|
import { getAppLatestVersion } from '../../../core/app/version/controller';
|
||||||
|
import { ShortUrlParams } from '@fastgpt/global/support/marketing/type';
|
||||||
|
|
||||||
const createTrack = ({ event, data }: { event: TrackEnum; data: Record<string, any> }) => {
|
const createTrack = ({ event, data }: { event: TrackEnum; data: Record<string, any> }) => {
|
||||||
if (!global.feConfigs?.isPlus) return;
|
if (!global.feConfigs?.isPlus) return;
|
||||||
@@ -31,7 +32,13 @@ export const pushTrack = {
|
|||||||
data
|
data
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
createApp: (data: PushTrackCommonType & { type: AppTypeEnum }) => {
|
createApp: (
|
||||||
|
data: PushTrackCommonType &
|
||||||
|
ShortUrlParams & {
|
||||||
|
type: AppTypeEnum;
|
||||||
|
appId: string;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
return createTrack({
|
return createTrack({
|
||||||
event: TrackEnum.createApp,
|
event: TrackEnum.createApp,
|
||||||
data
|
data
|
||||||
|
@@ -182,9 +182,14 @@
|
|||||||
"type.Http plugin": "HTTP Plugin",
|
"type.Http plugin": "HTTP Plugin",
|
||||||
"type.Import from json": "Import JSON",
|
"type.Import from json": "Import JSON",
|
||||||
"type.Import from json tip": "Create applications directly through JSON configuration files",
|
"type.Import from json tip": "Create applications directly through JSON configuration files",
|
||||||
|
"type.Import from json_error": "Failed to get workflow data, please check the URL or manually paste the JSON data",
|
||||||
|
"type.Import from json_loading": "Workflow data is being retrieved, please wait...",
|
||||||
"type.Plugin": "Plugin",
|
"type.Plugin": "Plugin",
|
||||||
"type.Simple bot": "Simple App",
|
"type.Simple bot": "Simple App",
|
||||||
"type.Workflow bot": "Workflow",
|
"type.Workflow bot": "Workflow",
|
||||||
|
"type.error.URLempty": "The URL cannot be empty",
|
||||||
|
"type.error.Workflow data is empty": "No workflow data was obtained",
|
||||||
|
"type.error.workflowresponseempty": "Response content is empty",
|
||||||
"type_not_recognized": "App type not recognized",
|
"type_not_recognized": "App type not recognized",
|
||||||
"upload_file_max_amount": "Maximum File Quantity",
|
"upload_file_max_amount": "Maximum File Quantity",
|
||||||
"upload_file_max_amount_tip": "Maximum number of files uploaded in a single round of conversation",
|
"upload_file_max_amount_tip": "Maximum number of files uploaded in a single round of conversation",
|
||||||
|
@@ -189,11 +189,16 @@
|
|||||||
"type.Http plugin": "HTTP 插件",
|
"type.Http plugin": "HTTP 插件",
|
||||||
"type.Import from json": "导入 JSON 配置",
|
"type.Import from json": "导入 JSON 配置",
|
||||||
"type.Import from json tip": "通过 JSON 配置文件,直接创建应用",
|
"type.Import from json tip": "通过 JSON 配置文件,直接创建应用",
|
||||||
|
"type.Import from json_error": "获取工作流数据失败,请检查URL或手动粘贴JSON数据",
|
||||||
|
"type.Import from json_loading": "正在获取工作流数据,请稍候...",
|
||||||
"type.MCP tools": "MCP 工具集",
|
"type.MCP tools": "MCP 工具集",
|
||||||
"type.MCP_tools_url": "MCP 地址",
|
"type.MCP_tools_url": "MCP 地址",
|
||||||
"type.Plugin": "插件",
|
"type.Plugin": "插件",
|
||||||
"type.Simple bot": "简易应用",
|
"type.Simple bot": "简易应用",
|
||||||
"type.Workflow bot": "工作流",
|
"type.Workflow bot": "工作流",
|
||||||
|
"type.error.URLempty": "URL不能为空",
|
||||||
|
"type.error.Workflow data is empty": "没有获取到工作流数据",
|
||||||
|
"type.error.workflowresponseempty": "响应内容为空",
|
||||||
"type_not_recognized": "未识别到应用类型",
|
"type_not_recognized": "未识别到应用类型",
|
||||||
"upload_file_max_amount": "最大文件数量",
|
"upload_file_max_amount": "最大文件数量",
|
||||||
"upload_file_max_amount_tip": "单轮对话中最大上传文件数量",
|
"upload_file_max_amount_tip": "单轮对话中最大上传文件数量",
|
||||||
|
@@ -182,9 +182,14 @@
|
|||||||
"type.Http plugin": "HTTP 外掛",
|
"type.Http plugin": "HTTP 外掛",
|
||||||
"type.Import from json": "匯入 JSON 設定",
|
"type.Import from json": "匯入 JSON 設定",
|
||||||
"type.Import from json tip": "透過 JSON 設定文件,直接建立應用",
|
"type.Import from json tip": "透過 JSON 設定文件,直接建立應用",
|
||||||
|
"type.Import from json_error": "獲取工作流數據失敗,請檢查URL或手動粘貼JSON數據",
|
||||||
|
"type.Import from json_loading": "正在獲取工作流數據,請稍候...",
|
||||||
"type.Plugin": "外掛",
|
"type.Plugin": "外掛",
|
||||||
"type.Simple bot": "簡易應用程式",
|
"type.Simple bot": "簡易應用程式",
|
||||||
"type.Workflow bot": "工作流程",
|
"type.Workflow bot": "工作流程",
|
||||||
|
"type.error.URLempty": "URL不能為空",
|
||||||
|
"type.error.Workflow data is empty": "沒有獲取到工作流數據",
|
||||||
|
"type.error.workflowresponseempty": "響應內容為空",
|
||||||
"type_not_recognized": "未識別到應用程式類型",
|
"type_not_recognized": "未識別到應用程式類型",
|
||||||
"upload_file_max_amount": "最大檔案數量",
|
"upload_file_max_amount": "最大檔案數量",
|
||||||
"upload_file_max_amount_tip": "單輪對話中最大上傳檔案數量",
|
"upload_file_max_amount_tip": "單輪對話中最大上傳檔案數量",
|
||||||
|
@@ -16,6 +16,14 @@ import { postCreateApp } from '@/web/core/app/api';
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { form2AppWorkflow } from '@/web/core/app/utils';
|
import { form2AppWorkflow } from '@/web/core/app/utils';
|
||||||
import ImportAppConfigEditor from '@/pageComponents/app/ImportAppConfigEditor';
|
import ImportAppConfigEditor from '@/pageComponents/app/ImportAppConfigEditor';
|
||||||
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
|
import { getFetchWorkflow } from '@/web/core/app/api/app';
|
||||||
|
import {
|
||||||
|
getUtmParams,
|
||||||
|
getUtmWorkflow,
|
||||||
|
removeUtmParams,
|
||||||
|
removeUtmWorkflow
|
||||||
|
} from '@/web/support/marketing/utils';
|
||||||
|
|
||||||
type FormType = {
|
type FormType = {
|
||||||
avatar: string;
|
avatar: string;
|
||||||
@@ -37,6 +45,24 @@ const JsonImportModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
});
|
});
|
||||||
const workflowStr = watch('workflowStr');
|
const workflowStr = watch('workflowStr');
|
||||||
|
|
||||||
|
const { loading: isFetching } = useRequest2(
|
||||||
|
async () => {
|
||||||
|
const url = getUtmWorkflow();
|
||||||
|
if (!url) return;
|
||||||
|
|
||||||
|
const workflowData = await getFetchWorkflow({ url });
|
||||||
|
|
||||||
|
setValue('workflowStr', JSON.stringify(workflowData, null, 2));
|
||||||
|
|
||||||
|
const utmParams = getUtmParams();
|
||||||
|
if (utmParams.shortUrlContent) setValue('name', utmParams.shortUrlContent);
|
||||||
|
|
||||||
|
removeUtmWorkflow();
|
||||||
|
removeUtmParams();
|
||||||
|
},
|
||||||
|
{ manual: false }
|
||||||
|
);
|
||||||
|
|
||||||
const avatar = watch('avatar');
|
const avatar = watch('avatar');
|
||||||
const {
|
const {
|
||||||
File,
|
File,
|
||||||
@@ -97,7 +123,8 @@ const JsonImportModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
type: appType,
|
type: appType,
|
||||||
modules: workflow.nodes,
|
modules: workflow.nodes,
|
||||||
edges: workflow.edges,
|
edges: workflow.edges,
|
||||||
chatConfig: workflow.chatConfig
|
chatConfig: workflow.chatConfig,
|
||||||
|
utmParams: getUtmParams()
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -116,7 +143,7 @@ const JsonImportModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
<MyModal
|
<MyModal
|
||||||
isOpen
|
isOpen
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
isLoading={isCreating}
|
isLoading={isCreating || isFetching}
|
||||||
title={t('app:type.Import from json')}
|
title={t('app:type.Import from json')}
|
||||||
iconSrc="common/importLight"
|
iconSrc="common/importLight"
|
||||||
iconColor={'primary.600'}
|
iconColor={'primary.600'}
|
||||||
|
@@ -13,7 +13,8 @@ import { checkIsWecomTerminal } from '@fastgpt/global/support/user/login/constan
|
|||||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { GET, POST } from '@/web/common/api/request';
|
import { POST } from '@/web/common/api/request';
|
||||||
|
import { getBdVId } from '@/web/support/marketing/utils';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -107,7 +108,7 @@ const FormLayout = ({ children, setPageType, pageType }: Props) => {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const show_oauth = useMemo(
|
const show_oauth = useMemo(
|
||||||
() => !sessionStorage.getItem('bd_vid') && !!(feConfigs?.sso?.url || oAuthList.length > 0),
|
() => !getBdVId() && !!(feConfigs?.sso?.url || oAuthList.length > 0),
|
||||||
[feConfigs?.sso?.url, oAuthList.length]
|
[feConfigs?.sso?.url, oAuthList.length]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -12,6 +12,13 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
|
import {
|
||||||
|
getBdVId,
|
||||||
|
getFastGPTSem,
|
||||||
|
getInviterId,
|
||||||
|
getSourceDomain,
|
||||||
|
removeFastGPTSem
|
||||||
|
} from '@/web/support/marketing/utils';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
loginSuccess: (e: ResLogin) => void;
|
loginSuccess: (e: ResLogin) => void;
|
||||||
@@ -50,20 +57,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
username,
|
username,
|
||||||
code,
|
code,
|
||||||
password,
|
password,
|
||||||
inviterId: localStorage.getItem('inviterId') || undefined,
|
inviterId: getInviterId(),
|
||||||
bd_vid: sessionStorage.getItem('bd_vid') || undefined,
|
bd_vid: getBdVId(),
|
||||||
fastgpt_sem: (() => {
|
fastgpt_sem: getFastGPTSem(),
|
||||||
try {
|
sourceDomain: getSourceDomain()
|
||||||
return sessionStorage.getItem('fastgpt_sem')
|
|
||||||
? JSON.parse(sessionStorage.getItem('fastgpt_sem')!)
|
|
||||||
: undefined;
|
|
||||||
} catch {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
})(),
|
|
||||||
sourceDomain: sessionStorage.getItem('sourceDomain') || undefined
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
removeFastGPTSem();
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
status: 'success',
|
status: 'success',
|
||||||
|
@@ -5,6 +5,7 @@ import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils'
|
|||||||
import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import type { AppSchema } from '@fastgpt/global/core/app/type';
|
import type { AppSchema } from '@fastgpt/global/core/app/type';
|
||||||
import { defaultNodeVersion } from '@fastgpt/global/core/workflow/node/constant';
|
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 { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
||||||
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
|
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
|
||||||
@@ -27,10 +28,11 @@ export type CreateAppBody = {
|
|||||||
modules: AppSchema['modules'];
|
modules: AppSchema['modules'];
|
||||||
edges?: AppSchema['edges'];
|
edges?: AppSchema['edges'];
|
||||||
chatConfig?: AppSchema['chatConfig'];
|
chatConfig?: AppSchema['chatConfig'];
|
||||||
|
utmParams?: ShortUrlParams;
|
||||||
};
|
};
|
||||||
|
|
||||||
async function handler(req: ApiRequestProps<CreateAppBody>) {
|
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)) {
|
if (!name || !type || !Array.isArray(modules)) {
|
||||||
return Promise.reject(CommonErrEnum.inheritPermissionError);
|
return Promise.reject(CommonErrEnum.inheritPermissionError);
|
||||||
@@ -66,7 +68,9 @@ async function handler(req: ApiRequestProps<CreateAppBody>) {
|
|||||||
type,
|
type,
|
||||||
uid: userId,
|
uid: userId,
|
||||||
teamId,
|
teamId,
|
||||||
tmbId
|
tmbId,
|
||||||
|
appId,
|
||||||
|
...utmParams
|
||||||
});
|
});
|
||||||
|
|
||||||
return appId;
|
return appId;
|
||||||
|
51
projects/app/src/pages/api/core/app/fetchWorkflow.ts
Normal file
51
projects/app/src/pages/api/core/app/fetchWorkflow.ts
Normal 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);
|
@@ -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 { Box, Flex, Button, useDisclosure, Input, InputGroup } from '@chakra-ui/react';
|
||||||
import { AddIcon } from '@chakra-ui/icons';
|
import { AddIcon } from '@chakra-ui/icons';
|
||||||
import { serviceSideProps } from '@/web/common/i18n/utils';
|
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 DashboardContainer from '@/pageComponents/dashboard/Container';
|
||||||
import List from '@/pageComponents/dashboard/apps/List';
|
import List from '@/pageComponents/dashboard/apps/List';
|
||||||
import MCPToolsEditModal from '@/pageComponents/dashboard/apps/MCPToolsEditModal';
|
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 CreateModal = dynamic(() => import('@/pageComponents/dashboard/apps/CreateModal'));
|
||||||
const EditFolderModal = dynamic(
|
const EditFolderModal = dynamic(
|
||||||
@@ -71,12 +73,20 @@ const MyApps = ({ MenuIcon }: { MenuIcon: JSX.Element }) => {
|
|||||||
onOpen: onOpenCreateMCPTools,
|
onOpen: onOpenCreateMCPTools,
|
||||||
onClose: onCloseCreateMCPTools
|
onClose: onCloseCreateMCPTools
|
||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
|
|
||||||
|
const [editFolder, setEditFolder] = useState<EditFolderFormType>();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen: isOpenJsonImportModal,
|
isOpen: isOpenJsonImportModal,
|
||||||
onOpen: onOpenJsonImportModal,
|
onOpen: onOpenJsonImportModal,
|
||||||
onClose: onCloseJsonImportModal
|
onClose: onCloseJsonImportModal
|
||||||
} = useDisclosure();
|
} = 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, {
|
const { runAsync: onCreateFolder } = useRequest2(postCreateAppFolder, {
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
|
@@ -30,6 +30,7 @@ import { getDocPath } from '@/web/common/system/doc';
|
|||||||
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
|
import { getWebReqUrl } from '@fastgpt/web/common/system/utils';
|
||||||
import LoginForm from '@/pageComponents/login/LoginForm/LoginForm';
|
import LoginForm from '@/pageComponents/login/LoginForm/LoginForm';
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
|
import { getBdVId } from '@/web/support/marketing/utils';
|
||||||
|
|
||||||
const RegisterForm = dynamic(() => import('@/pageComponents/login/RegisterForm'));
|
const RegisterForm = dynamic(() => import('@/pageComponents/login/RegisterForm'));
|
||||||
const ForgetPasswordForm = dynamic(() => import('@/pageComponents/login/ForgetPasswordForm'));
|
const ForgetPasswordForm = dynamic(() => import('@/pageComponents/login/ForgetPasswordForm'));
|
||||||
@@ -64,7 +65,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
|
|||||||
setUserInfo(res.user);
|
setUserInfo(res.user);
|
||||||
|
|
||||||
const decodeLastRoute = decodeURIComponent(lastRoute);
|
const decodeLastRoute = decodeURIComponent(lastRoute);
|
||||||
// 检查是否是当前的 route
|
|
||||||
const navigateTo =
|
const navigateTo =
|
||||||
decodeLastRoute && !decodeLastRoute.includes('/login')
|
decodeLastRoute && !decodeLastRoute.includes('/login')
|
||||||
? decodeLastRoute
|
? decodeLastRoute
|
||||||
@@ -90,7 +91,7 @@ const Login = ({ ChineseRedirectUrl }: { ChineseRedirectUrl: string }) => {
|
|||||||
|
|
||||||
/* default login type */
|
/* default login type */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const bd_vid = sessionStorage.getItem('bd_vid');
|
const bd_vid = getBdVId();
|
||||||
if (bd_vid) {
|
if (bd_vid) {
|
||||||
setPageType(LoginPageTypeEnum.passwordLogin);
|
setPageType(LoginPageTypeEnum.passwordLogin);
|
||||||
return;
|
return;
|
||||||
|
@@ -11,6 +11,13 @@ import { serviceSideProps } from '@/web/common/i18n/utils';
|
|||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { OAuthEnum } from '@fastgpt/global/support/user/constant';
|
import { OAuthEnum } from '@fastgpt/global/support/user/constant';
|
||||||
|
import {
|
||||||
|
getBdVId,
|
||||||
|
getFastGPTSem,
|
||||||
|
getInviterId,
|
||||||
|
getSourceDomain,
|
||||||
|
removeFastGPTSem
|
||||||
|
} from '@/web/support/marketing/utils';
|
||||||
|
|
||||||
let isOauthLogging = false;
|
let isOauthLogging = false;
|
||||||
|
|
||||||
@@ -40,18 +47,10 @@ const provider = () => {
|
|||||||
type: loginStore?.provider || OAuthEnum.sso,
|
type: loginStore?.provider || OAuthEnum.sso,
|
||||||
props,
|
props,
|
||||||
callbackUrl: `${location.origin}/login/provider`,
|
callbackUrl: `${location.origin}/login/provider`,
|
||||||
inviterId: localStorage.getItem('inviterId') || undefined,
|
inviterId: getInviterId(),
|
||||||
bd_vid: sessionStorage.getItem('bd_vid') || undefined,
|
bd_vid: getBdVId(),
|
||||||
fastgpt_sem: (() => {
|
fastgpt_sem: getFastGPTSem(),
|
||||||
try {
|
sourceDomain: getSourceDomain()
|
||||||
return sessionStorage.getItem('fastgpt_sem')
|
|
||||||
? JSON.parse(sessionStorage.getItem('fastgpt_sem')!)
|
|
||||||
: undefined;
|
|
||||||
} catch {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
})(),
|
|
||||||
sourceDomain: sessionStorage.getItem('sourceDomain') || undefined
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
@@ -63,6 +62,8 @@ const provider = () => {
|
|||||||
router.replace('/login');
|
router.replace('/login');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeFastGPTSem();
|
||||||
loginSuccess(res);
|
loginSuccess(res);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
toast({
|
toast({
|
||||||
|
@@ -7,14 +7,28 @@ import { useMemoizedFn, useMount } from 'ahooks';
|
|||||||
import { TrackEventName } from '../common/system/constants';
|
import { TrackEventName } from '../common/system/constants';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { useUserStore } from '../support/user/useUserStore';
|
import { useUserStore } from '../support/user/useUserStore';
|
||||||
|
import {
|
||||||
|
setBdVId,
|
||||||
|
setFastGPTSem,
|
||||||
|
setInviterId,
|
||||||
|
setSourceDomain,
|
||||||
|
setUtmParams,
|
||||||
|
setUtmWorkflow
|
||||||
|
} from '../support/marketing/utils';
|
||||||
|
import { ShortUrlParams } from '@fastgpt/global/support/marketing/type';
|
||||||
|
|
||||||
export const useInitApp = () => {
|
export const useInitApp = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { hiId, bd_vid, k, sourceDomain } = router.query as {
|
const { hiId, bd_vid, k, sourceDomain, utm_source, utm_medium, utm_content, utm_workflow } =
|
||||||
|
router.query as {
|
||||||
hiId?: string;
|
hiId?: string;
|
||||||
bd_vid?: string;
|
bd_vid?: string;
|
||||||
k?: string;
|
k?: string;
|
||||||
sourceDomain?: string;
|
sourceDomain?: string;
|
||||||
|
utm_source?: string;
|
||||||
|
utm_medium?: string;
|
||||||
|
utm_content?: string;
|
||||||
|
utm_workflow?: string;
|
||||||
};
|
};
|
||||||
const { loadGitStar, setInitd, feConfigs } = useSystemStore();
|
const { loadGitStar, setInitd, feConfigs } = useSystemStore();
|
||||||
const { userInfo } = useUserStore();
|
const { userInfo } = useUserStore();
|
||||||
@@ -69,20 +83,21 @@ export const useInitApp = () => {
|
|||||||
pollingInterval: 300000 // 5 minutes refresh
|
pollingInterval: 300000 // 5 minutes refresh
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Marketing data track
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
hiId && localStorage.setItem('inviterId', hiId);
|
setInviterId(hiId);
|
||||||
bd_vid && sessionStorage.setItem('bd_vid', bd_vid);
|
setBdVId(bd_vid);
|
||||||
k && sessionStorage.setItem('fastgpt_sem', JSON.stringify({ keyword: k }));
|
setUtmWorkflow(utm_workflow);
|
||||||
|
setSourceDomain(sourceDomain);
|
||||||
|
|
||||||
const formatSourceDomain = (() => {
|
const utmParams: ShortUrlParams = {
|
||||||
if (sourceDomain) return sourceDomain;
|
...(utm_source && { shortUrlSource: utm_source }),
|
||||||
return document.referrer;
|
...(utm_medium && { shortUrlMedium: utm_medium }),
|
||||||
})();
|
...(utm_content && { shortUrlContent: utm_content })
|
||||||
|
};
|
||||||
if (formatSourceDomain && !sessionStorage.getItem('sourceDomain')) {
|
setUtmParams(utmParams);
|
||||||
sessionStorage.setItem('sourceDomain', formatSourceDomain);
|
setFastGPTSem({ keyword: k, ...utmParams });
|
||||||
}
|
}, [bd_vid, hiId, k, utm_workflow, sourceDomain, utm_source, utm_medium, utm_content]);
|
||||||
}, [bd_vid, hiId, k, sourceDomain]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
feConfigs,
|
feConfigs,
|
||||||
|
@@ -10,6 +10,10 @@ import type {
|
|||||||
} from '@/pages/api/core/app/transitionWorkflow';
|
} from '@/pages/api/core/app/transitionWorkflow';
|
||||||
import type { copyAppQuery, copyAppResponse } from '@/pages/api/core/app/copy';
|
import type { copyAppQuery, copyAppResponse } from '@/pages/api/core/app/copy';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
FetchWorkflowQuery,
|
||||||
|
FetchWorkflowResponseType
|
||||||
|
} from '@/pages/api/core/app/fetchWorkflow';
|
||||||
/* folder */
|
/* folder */
|
||||||
export const postCreateAppFolder = (data: CreateAppFolderBody) =>
|
export const postCreateAppFolder = (data: CreateAppFolderBody) =>
|
||||||
POST('/core/app/folder/create', data);
|
POST('/core/app/folder/create', data);
|
||||||
@@ -25,3 +29,6 @@ export const postTransition2Workflow = (data: transitionWorkflowBody) =>
|
|||||||
POST<transitionWorkflowResponse>('/core/app/transitionWorkflow', data);
|
POST<transitionWorkflowResponse>('/core/app/transitionWorkflow', data);
|
||||||
|
|
||||||
export const postCopyApp = (data: copyAppQuery) => POST<copyAppResponse>('/core/app/copy', data);
|
export const postCopyApp = (data: copyAppQuery) => POST<copyAppResponse>('/core/app/copy', data);
|
||||||
|
|
||||||
|
export const getFetchWorkflow = (data: FetchWorkflowQuery) =>
|
||||||
|
GET<FetchWorkflowResponseType>('/core/app/fetchWorkflow', data);
|
||||||
|
77
projects/app/src/web/support/marketing/utils.ts
Normal file
77
projects/app/src/web/support/marketing/utils.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { ShortUrlParams, TrackRegisterParams } from '@fastgpt/global/support/marketing/type';
|
||||||
|
|
||||||
|
export const getInviterId = () => {
|
||||||
|
return localStorage.getItem('inviterId') || undefined;
|
||||||
|
};
|
||||||
|
export const setInviterId = (inviterId?: string) => {
|
||||||
|
if (!inviterId) return;
|
||||||
|
localStorage.setItem('inviterId', inviterId);
|
||||||
|
};
|
||||||
|
export const removeInviterId = () => {
|
||||||
|
localStorage.removeItem('inviterId');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getBdVId = () => {
|
||||||
|
return sessionStorage.getItem('bd_vid') || undefined;
|
||||||
|
};
|
||||||
|
export const setBdVId = (bdVid?: string) => {
|
||||||
|
if (!bdVid) return;
|
||||||
|
sessionStorage.setItem('bd_vid', bdVid);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUtmWorkflow = () => {
|
||||||
|
return sessionStorage.getItem('utm_workflow') || undefined;
|
||||||
|
};
|
||||||
|
export const setUtmWorkflow = (utmWorkflow?: string) => {
|
||||||
|
if (!utmWorkflow) return;
|
||||||
|
sessionStorage.setItem('utm_workflow', utmWorkflow);
|
||||||
|
};
|
||||||
|
export const removeUtmWorkflow = () => {
|
||||||
|
sessionStorage.removeItem('utm_workflow');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUtmParams = () => {
|
||||||
|
try {
|
||||||
|
const params = JSON.parse(localStorage.getItem('utm_params') || '{}');
|
||||||
|
return params as ShortUrlParams;
|
||||||
|
} catch (error) {
|
||||||
|
return {} as ShortUrlParams;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const setUtmParams = (utmParams?: ShortUrlParams) => {
|
||||||
|
if (!utmParams || Object.keys(utmParams).length === 0) return;
|
||||||
|
localStorage.setItem('utm_params', JSON.stringify(utmParams));
|
||||||
|
};
|
||||||
|
export const removeUtmParams = () => {
|
||||||
|
localStorage.removeItem('utm_params');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getFastGPTSem = () => {
|
||||||
|
try {
|
||||||
|
return localStorage.getItem('fastgpt_sem')
|
||||||
|
? JSON.parse(localStorage.getItem('fastgpt_sem')!)
|
||||||
|
: undefined;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const setFastGPTSem = (fastgptSem?: TrackRegisterParams['fastgpt_sem']) => {
|
||||||
|
if (!fastgptSem) return;
|
||||||
|
localStorage.setItem('fastgpt_sem', JSON.stringify(fastgptSem));
|
||||||
|
};
|
||||||
|
export const removeFastGPTSem = () => {
|
||||||
|
localStorage.removeItem('fastgpt_sem');
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getSourceDomain = () => {
|
||||||
|
return sessionStorage.getItem('sourceDomain') || undefined;
|
||||||
|
};
|
||||||
|
export const setSourceDomain = (sourceDomain?: string) => {
|
||||||
|
const formatSourceDomain = (() => {
|
||||||
|
if (sourceDomain) return sourceDomain;
|
||||||
|
return document.referrer;
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (!formatSourceDomain || getSourceDomain()) return;
|
||||||
|
sessionStorage.setItem('sourceDomain', formatSourceDomain);
|
||||||
|
};
|
Reference in New Issue
Block a user