mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-29 09:44:47 +00:00
Publish app - feishu and wecom (#2375)
* feat(app publish): feishu bot (#2290) * feat: feishu publish channel fe * feat: enable feishu fe, feat: feishu token api * feat: feishu bot * chore: extract saveChat from projects/app * chore: remove debug log output * feat: Basic Info * chore: feishu bot fe adjusting * feat: feishu bot docs * feat: new tmpData collection for all tmpdata * chore: compress the image * perf: feishu config * feat: source name * perf: text desc * perf: load system plugins * perf: chat source * feat(publish): Wecom bot (#2343) * chore: Wecom Config * feat(fe): wecom config fe * feat: wecom fe * chore: uses the newest editmodal * feat: update png; adjust the fe * chore: adjust fe * perf: publish app ui --------- Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
This commit is contained in:
@@ -75,9 +75,9 @@ const MyRadio = ({
|
||||
{!!item.icon && (
|
||||
<>
|
||||
{item.icon.startsWith('/') ? (
|
||||
<Image src={item.icon} mr={'14px'} w={iconSize} alt={''} />
|
||||
<Image src={item.icon} mr={'14px'} w={iconSize} alt={''} fill={'primary.600'} />
|
||||
) : (
|
||||
<MyIcon mr={'14px'} name={item.icon as any} w={iconSize} />
|
||||
<MyIcon mr={'14px'} name={item.icon as any} w={iconSize} fill={'primary.600'} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
@@ -25,7 +25,6 @@ import {
|
||||
} from '@/web/support/openapi/api';
|
||||
import type { EditApiKeyProps } from '@/global/support/openapi/api.d';
|
||||
import { useQuery, useMutation } from '@tanstack/react-query';
|
||||
import { useLoading } from '@fastgpt/web/hooks/useLoading';
|
||||
import dayjs from 'dayjs';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import { useCopyData } from '@/web/common/hooks/useCopyData';
|
||||
@@ -53,7 +52,6 @@ const defaultEditData: EditProps = {
|
||||
|
||||
const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
|
||||
const { t } = useTranslation();
|
||||
const { Loading } = useLoading();
|
||||
const theme = useTheme();
|
||||
const { copyData } = useCopyData();
|
||||
const { feConfigs } = useSystemStore();
|
||||
@@ -82,7 +80,7 @@ const ApiKeyTable = ({ tips, appId }: { tips: string; appId?: string }) => {
|
||||
|
||||
useEffect(() => {
|
||||
setBaseUrl(feConfigs?.customApiDomain || `${location.origin}/api`);
|
||||
}, []);
|
||||
}, [feConfigs?.customApiDomain]);
|
||||
|
||||
return (
|
||||
<MyBox
|
||||
|
@@ -2,7 +2,7 @@ import type { NextApiResponse } from 'next';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { NodeTemplateListItemType } from '@fastgpt/global/core/workflow/type/node.d';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { getSystemPluginTemplates } from '@fastgpt/plugins/register';
|
||||
import { getSystemPlugins } from '@/service/core/app/plugin';
|
||||
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
@@ -24,7 +24,7 @@ async function handler(
|
||||
|
||||
const formatParentId = parentId || null;
|
||||
|
||||
return getSystemPluginTemplates().then((res) =>
|
||||
return getSystemPlugins().then((res) =>
|
||||
res
|
||||
// Just show the active plugins
|
||||
.filter((item) => item.isActive)
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { ParentIdType, ParentTreePathItemType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import { getSystemPluginTemplates } from '@fastgpt/plugins/register';
|
||||
import { getSystemPlugins } from '@/service/core/app/plugin';
|
||||
|
||||
export type pathQuery = {
|
||||
parentId: ParentIdType;
|
||||
@@ -19,7 +19,7 @@ async function handler(
|
||||
|
||||
if (!parentId) return [];
|
||||
|
||||
const plugins = await getSystemPluginTemplates();
|
||||
const plugins = await getSystemPlugins();
|
||||
const plugin = plugins.find((item) => item.id === parentId);
|
||||
|
||||
if (!plugin) return [];
|
||||
|
20
projects/app/src/pages/api/support/outLink/feishu/[token].ts
Normal file
20
projects/app/src/pages/api/support/outLink/feishu/[token].ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { POST } from '@fastgpt/service/common/api/plusRequest';
|
||||
|
||||
export type OutLinkFeishuQuery = any;
|
||||
export type OutLinkFeishuBody = any;
|
||||
export type OutLinkFeishuResponse = {};
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<OutLinkFeishuBody, OutLinkFeishuQuery>,
|
||||
res: ApiResponseType<any>
|
||||
): Promise<void> {
|
||||
// send to pro
|
||||
const { token } = req.query;
|
||||
const result = await POST<any>(`support/outLink/feishu/${token}`, req.body, {
|
||||
headers: req.headers as any
|
||||
});
|
||||
res.json(result);
|
||||
}
|
||||
|
||||
export default handler;
|
@@ -13,7 +13,7 @@ export type OutLinkUpdateResponse = {};
|
||||
async function handler(
|
||||
req: ApiRequestProps<OutLinkUpdateBody, OutLinkUpdateQuery>
|
||||
): Promise<OutLinkUpdateResponse> {
|
||||
const { _id, name, responseDetail, limit } = req.body;
|
||||
const { _id, name, responseDetail, limit, app } = req.body;
|
||||
|
||||
if (!_id) {
|
||||
return Promise.reject(CommonErrEnum.missingParams);
|
||||
@@ -24,7 +24,8 @@ async function handler(
|
||||
await MongoOutLink.findByIdAndUpdate(_id, {
|
||||
name,
|
||||
responseDetail,
|
||||
limit
|
||||
limit,
|
||||
app
|
||||
});
|
||||
return {};
|
||||
}
|
||||
|
31
projects/app/src/pages/api/support/outLink/wecom/[token].ts
Normal file
31
projects/app/src/pages/api/support/outLink/wecom/[token].ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { plusRequest } from '@fastgpt/service/common/api/plusRequest';
|
||||
|
||||
export type OutLinkWecomQuery = any;
|
||||
export type OutLinkWecomBody = any;
|
||||
export type OutLinkWecomResponse = {};
|
||||
|
||||
async function handler(
|
||||
req: ApiRequestProps<OutLinkWecomBody, OutLinkWecomQuery>,
|
||||
res: ApiResponseType<any>
|
||||
): Promise<any> {
|
||||
const { token, type } = req.query;
|
||||
const result = await plusRequest({
|
||||
url: `support/outLink/wecom/${token}`,
|
||||
params: {
|
||||
...req.query,
|
||||
type
|
||||
},
|
||||
data: req.body
|
||||
});
|
||||
if (result.data?.data?.message) {
|
||||
// chanllege
|
||||
res.send(result.data.data.message);
|
||||
res.end();
|
||||
}
|
||||
|
||||
res.send('success');
|
||||
res.end();
|
||||
}
|
||||
|
||||
export default handler;
|
@@ -21,7 +21,7 @@ import {
|
||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { GPTMessages2Chats, chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt';
|
||||
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||
import { saveChat } from '@/service/utils/chat/saveChat';
|
||||
import { saveChat } from '@fastgpt/service/core/chat/saveChat';
|
||||
import { responseWrite } from '@fastgpt/service/common/response';
|
||||
import { pushChatUsage } from '@/service/support/wallet/usage/push';
|
||||
import { authOutLinkChatStart } from '@/service/support/permission/auth/outLink';
|
||||
|
@@ -71,35 +71,33 @@ const Logs = () => {
|
||||
const [detailLogsId, setDetailLogsId] = useState<string>();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box {...cardStyles} boxShadow={2} px={[4, 8]} py={[4, 6]}>
|
||||
{isPc && (
|
||||
<>
|
||||
<Box fontWeight={'bold'} fontSize={['md', 'lg']} mb={2}>
|
||||
{appT('chat_logs')}
|
||||
<Flex flexDirection={'column'} h={'100%'}>
|
||||
{isPc && (
|
||||
<Box {...cardStyles} boxShadow={2} px={[4, 8]} py={[4, 6]}>
|
||||
<Box fontWeight={'bold'} fontSize={['md', 'lg']} mb={2}>
|
||||
{appT('chat_logs')}
|
||||
</Box>
|
||||
<Box color={'myGray.500'} fontSize={'sm'}>
|
||||
{appT('chat_logs_tips')},{' '}
|
||||
<Box
|
||||
as={'span'}
|
||||
mr={2}
|
||||
textDecoration={'underline'}
|
||||
cursor={'pointer'}
|
||||
onClick={onOpenMarkDesc}
|
||||
>
|
||||
{t('common:core.chat.Read Mark Description')}
|
||||
</Box>
|
||||
<Box color={'myGray.500'} fontSize={'sm'}>
|
||||
{appT('chat_logs_tips')},{' '}
|
||||
<Box
|
||||
as={'span'}
|
||||
mr={2}
|
||||
textDecoration={'underline'}
|
||||
cursor={'pointer'}
|
||||
onClick={onOpenMarkDesc}
|
||||
>
|
||||
{t('common:core.chat.Read Mark Description')}
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* table */}
|
||||
<Flex
|
||||
flexDirection={'column'}
|
||||
{...cardStyles}
|
||||
boxShadow={3.5}
|
||||
mt={4}
|
||||
mt={[0, 4]}
|
||||
px={[4, 8]}
|
||||
py={[4, 6]}
|
||||
flex={'1 0 0'}
|
||||
@@ -214,7 +212,7 @@ const Logs = () => {
|
||||
>
|
||||
<ModalBody whiteSpace={'pre-wrap'}>{t('common:core.chat.Mark Description')}</ModalBody>
|
||||
</MyModal>
|
||||
</>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -1,32 +1,35 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Flex, Box, Button, ModalFooter, ModalBody, Input } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
import { Flex, Box, Button, ModalFooter, ModalBody, Input, Link, Grid } from '@chakra-ui/react';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
|
||||
import type { FeishuType, OutLinkEditType } from '@fastgpt/global/support/outLink/type';
|
||||
import type { FeishuAppType, OutLinkEditType } from '@fastgpt/global/support/outLink/type';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import dayjs from 'dayjs';
|
||||
import { createShareChat, updateShareChat } from '@/web/support/outLink/api';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import BasicInfo from '../components/BasicInfo';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
|
||||
const FeiShuEditModal = ({
|
||||
appId,
|
||||
defaultData,
|
||||
onClose,
|
||||
onCreate,
|
||||
onEdit
|
||||
onEdit,
|
||||
isEdit = false
|
||||
}: {
|
||||
appId: string;
|
||||
defaultData: OutLinkEditType<FeishuType>;
|
||||
defaultData: OutLinkEditType<FeishuAppType>;
|
||||
onClose: () => void;
|
||||
onCreate: (id: string) => void;
|
||||
onEdit: () => void;
|
||||
isEdit?: boolean;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { publishT } = useI18n();
|
||||
const {
|
||||
register,
|
||||
setValue,
|
||||
@@ -35,174 +38,101 @@ const FeiShuEditModal = ({
|
||||
defaultValues: defaultData
|
||||
});
|
||||
|
||||
const isEdit = useMemo(() => !!defaultData?._id, [defaultData]);
|
||||
|
||||
const { mutate: onclickCreate, isLoading: creating } = useRequest({
|
||||
mutationFn: async (e: OutLinkEditType<FeishuType>) => {
|
||||
const { runAsync: onclickCreate, loading: creating } = useRequest2(
|
||||
(e) =>
|
||||
createShareChat({
|
||||
...e,
|
||||
appId,
|
||||
type: PublishChannelEnum.feishu
|
||||
});
|
||||
},
|
||||
errorToast: t('common:common.Create Failed'),
|
||||
onSuccess: onCreate
|
||||
});
|
||||
const { mutate: onclickUpdate, isLoading: updating } = useRequest({
|
||||
mutationFn: (e: OutLinkEditType<FeishuType>) => {
|
||||
return updateShareChat(e);
|
||||
},
|
||||
}),
|
||||
{
|
||||
errorToast: t('common:common.Create Failed'),
|
||||
successToast: t('common:common.Create Success'),
|
||||
onSuccess: onCreate
|
||||
}
|
||||
);
|
||||
|
||||
const { runAsync: onclickUpdate, loading: updating } = useRequest2((e) => updateShareChat(e), {
|
||||
errorToast: t('common:common.Update Failed'),
|
||||
successToast: t('common:common.Update Success'),
|
||||
onSuccess: onEdit
|
||||
});
|
||||
|
||||
const { feConfigs } = useSystemStore();
|
||||
const { isPc } = useSystem();
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen={true}
|
||||
iconSrc="/imgs/modal/shareFill.svg"
|
||||
title={isEdit ? publishT('edit_link') : publishT('create_link')}
|
||||
iconSrc="core/app/publish/lark"
|
||||
title={isEdit ? t('publish:edit_feishu_bot') : t('publish:new_feishu_bot')}
|
||||
minW={['auto', '60rem']}
|
||||
>
|
||||
<ModalBody>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box flex={'0 0 90px'}>{t('common:Name')}</Box>
|
||||
<Input
|
||||
placeholder={publishT('feishu_name') || 'link_name'} // TODO: i18n
|
||||
maxLength={20}
|
||||
{...register('name', {
|
||||
required: t('common:common.name_is_empty') || 'name_is_empty'
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={4}>
|
||||
<Flex flex={'0 0 90px'} alignItems={'center'}>
|
||||
QPM
|
||||
<QuestionTip ml={1} label={publishT('qpm_tips' || '')}></QuestionTip>
|
||||
<ModalBody display={'grid'} gridTemplateColumns={['1fr', '1fr 1fr']} fontSize={'14px'} p={0}>
|
||||
<Box p={8} h={['auto', '400px']} borderRight={'base'}>
|
||||
<BasicInfo register={register} setValue={setValue} defaultData={defaultData} />
|
||||
</Box>
|
||||
<Flex p={8} h={['auto', '400px']} flexDirection="column" gap={6}>
|
||||
<Flex alignItems="center">
|
||||
<Box color="myGray.600">{t('publish:feishu_api')}</Box>
|
||||
{feConfigs?.docUrl && (
|
||||
<Link
|
||||
href={feConfigs.openAPIDocUrl || getDocPath('/docs/use-cases/feishu-bot')}
|
||||
target={'_blank'}
|
||||
ml={2}
|
||||
color={'primary.500'}
|
||||
fontSize={'sm'}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name="book" mr="1" />
|
||||
{t('common:common.Read document')}
|
||||
</Flex>
|
||||
</Link>
|
||||
)}
|
||||
</Flex>
|
||||
<Input
|
||||
max={1000}
|
||||
{...register('limit.QPM', {
|
||||
min: 0,
|
||||
max: 1000,
|
||||
valueAsNumber: true,
|
||||
required: publishT('qpm_is_empty') || ''
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={4}>
|
||||
<Flex flex={'0 0 90px'} alignItems={'center'}>
|
||||
{t('common:support.outlink.Max usage points')}
|
||||
<QuestionTip
|
||||
ml={1}
|
||||
label={t('common:support.outlink.Max usage points tip')}
|
||||
></QuestionTip>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 6.25rem'} required>
|
||||
App ID
|
||||
</FormLabel>
|
||||
<Input
|
||||
placeholder={t('common:core.module.http.AppId')}
|
||||
{...register('app.appId', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Input
|
||||
{...register('limit.maxUsagePoints', {
|
||||
min: -1,
|
||||
max: 10000000,
|
||||
valueAsNumber: true,
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={4}>
|
||||
<Flex flex={'0 0 90px'} alignItems={'center'}>
|
||||
{t('common:common.Expired Time')}
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 6.25rem'} required>
|
||||
App Secret
|
||||
</FormLabel>
|
||||
<Input
|
||||
placeholder={'App Secret'}
|
||||
{...register('app.appSecret', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Input
|
||||
type="datetime-local"
|
||||
defaultValue={
|
||||
defaultData.limit?.expiredTime
|
||||
? dayjs(defaultData.limit?.expiredTime).format('YYYY-MM-DDTHH:mm')
|
||||
: ''
|
||||
}
|
||||
onChange={(e) => {
|
||||
setValue('limit.expiredTime', new Date(e.target.value));
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={4}>
|
||||
<Flex flex={'0 0 90px'} alignItems={'center'}>
|
||||
{t('common:default_reply')}
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 6.25rem'}>Encrypt Key</FormLabel>
|
||||
<Input placeholder="Encrypt Key" {...register('app.encryptKey')} />
|
||||
</Flex>
|
||||
<Input
|
||||
placeholder={publishT('default_response') || 'link_name'}
|
||||
maxLength={20}
|
||||
{...register('defaultResponse', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={4}>
|
||||
<Flex flex={'0 0 90px'} alignItems={'center'}>
|
||||
{t('common:reply_now')}
|
||||
|
||||
<Box flex={1}></Box>
|
||||
|
||||
<Flex justifyContent={'end'}>
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
|
||||
{t('common:common.Close')}
|
||||
</Button>
|
||||
<Button
|
||||
isLoading={creating || updating}
|
||||
onClick={submitShareChat((data) =>
|
||||
isEdit ? onclickUpdate(data) : onclickCreate(data)
|
||||
)}
|
||||
>
|
||||
{t('common:common.Confirm')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<Input
|
||||
placeholder={publishT('default_response') || 'link_name'}
|
||||
maxLength={20}
|
||||
{...register('immediateResponse', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={4}>
|
||||
<Box flex={'0 0 90px'}>{t('common:core.module.http.AppId')}</Box>
|
||||
<Input
|
||||
placeholder={t('common:core.module.http.AppId') || 'link_name'}
|
||||
// maxLength={20}
|
||||
{...register('app.appId', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={4}>
|
||||
<Box flex={'0 0 90px'}>{t('common:core.module.http.AppSecret' as any)}</Box>
|
||||
<Input
|
||||
placeholder={'App Secret'}
|
||||
// maxLength={20}
|
||||
{...register('app.appSecret', {
|
||||
required: t('common:common.name_is_empty') || 'name_is_empty'
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={4}>
|
||||
<Box flex={'0 0 90px'}>Encrypt Key</Box>
|
||||
<Input
|
||||
placeholder="Encrypt Key"
|
||||
// maxLength={20}
|
||||
{...register('app.encryptKey', {
|
||||
required: t('common:common.name_is_empty') || 'name_is_empty'
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'} mt={4}>
|
||||
<Box flex={'0 0 90px'}>Verification Token</Box>
|
||||
<Input
|
||||
placeholder="Verification Token"
|
||||
// maxLength={20}
|
||||
{...register('app.verificationToken', {
|
||||
required: t('common:common.name_is_empty') || 'name_is_empty'
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
{/* <Flex alignItems={'center'} mt={4}> */}
|
||||
{/* <Flex flex={'0 0 90px'} alignItems={'center'}> */}
|
||||
{/* 限制回复 */}
|
||||
{/* </Flex> */}
|
||||
{/* <Switch {...register('wecomConfig.ReplyLimit')} size={'lg'} /> */}
|
||||
{/* </Flex> */}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
|
||||
{t('common:common.Close')}
|
||||
</Button>
|
||||
<Button
|
||||
isLoading={creating || updating}
|
||||
onClick={submitShareChat((data) => (isEdit ? onclickUpdate(data) : onclickCreate(data)))}
|
||||
>
|
||||
{t('common:common.Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
Flex,
|
||||
Box,
|
||||
@@ -9,61 +9,82 @@ import {
|
||||
Tr,
|
||||
Th,
|
||||
Td,
|
||||
Tbody
|
||||
Tbody,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useLoading } from '@fastgpt/web/hooks/useLoading';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getShareChatList, delShareChatById } from '@/web/support/outLink/api';
|
||||
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
|
||||
import { useCopyData } from '@/web/common/hooks/useCopyData';
|
||||
import { defaultFeishuOutLinkForm } from '@/web/core/app/constants';
|
||||
import type { FeishuType, OutLinkEditType } from '@fastgpt/global/support/outLink/type.d';
|
||||
import type { FeishuAppType, OutLinkEditType } from '@fastgpt/global/support/outLink/type.d';
|
||||
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import dayjs from 'dayjs';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
|
||||
const FeiShuEditModal = dynamic(() => import('./FeiShuEditModal'));
|
||||
const ShowShareLinkModal = dynamic(() => import('../components/showShareLinkModal'));
|
||||
|
||||
const FeiShu = ({ appId }: { appId: string }) => {
|
||||
const { t } = useTranslation();
|
||||
const { Loading, setIsLoading } = useLoading();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const { copyData } = useCopyData();
|
||||
const [editFeiShuLinkData, setEditFeiShuLinkData] = useState<OutLinkEditType<FeishuType>>();
|
||||
const { toast } = useToast();
|
||||
const {
|
||||
isFetching,
|
||||
data: shareChatList = [],
|
||||
refetch: refetchShareChatList
|
||||
} = useQuery(['initShareChatList', appId], () =>
|
||||
getShareChatList<FeishuType>({ appId, type: PublishChannelEnum.feishu })
|
||||
const [editFeiShuLinkData, setEditFeiShuLinkData] = useState<OutLinkEditType<FeishuAppType>>();
|
||||
const [isEdit, setIsEdit] = useState<boolean>(false);
|
||||
|
||||
const baseUrl = useMemo(
|
||||
() => feConfigs?.customApiDomain || `${location.origin}/api`,
|
||||
[feConfigs?.customApiDomain]
|
||||
);
|
||||
|
||||
const {
|
||||
data: shareChatList = [],
|
||||
loading: isFetching,
|
||||
runAsync: refetchShareChatList
|
||||
} = useRequest2(
|
||||
() => getShareChatList<FeishuAppType>({ appId, type: PublishChannelEnum.feishu }),
|
||||
{
|
||||
manual: false
|
||||
}
|
||||
);
|
||||
|
||||
const {
|
||||
onOpen: openShowShareLinkModal,
|
||||
isOpen: showShareLinkModalOpen,
|
||||
onClose: closeShowShareLinkModal
|
||||
} = useDisclosure();
|
||||
|
||||
const [showShareLink, setShowShareLink] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<Box position={'relative'} pt={3} px={5} minH={'50vh'}>
|
||||
<Flex justifyContent={'space-between'}>
|
||||
<Flex justifyContent={'space-between'} flexDirection="row">
|
||||
<Box fontWeight={'bold'} fontSize={['md', 'lg']}>
|
||||
{t('common:core.app.publish.Fei shu bot publish')}
|
||||
</Box>
|
||||
<Button
|
||||
variant={'whitePrimary'}
|
||||
variant={'primary'}
|
||||
colorScheme={'blue'}
|
||||
size={['sm', 'md']}
|
||||
leftIcon={<MyIcon name={'common/addLight'} w="1.25rem" color="white" />}
|
||||
ml={3}
|
||||
{...(shareChatList.length >= 10
|
||||
? {
|
||||
isDisabled: true,
|
||||
title: t('common:core.app.share.Amount limit tip')
|
||||
}
|
||||
: {})}
|
||||
onClick={() => setEditFeiShuLinkData(defaultFeishuOutLinkForm)}
|
||||
onClick={() => {
|
||||
setEditFeiShuLinkData(defaultFeishuOutLinkForm);
|
||||
setIsEdit(false);
|
||||
}}
|
||||
>
|
||||
{t('common:core.app.share.Create link')}
|
||||
{t('common:add_new')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<TableContainer mt={3}>
|
||||
@@ -112,11 +133,22 @@ const FeiShu = ({ appId }: { appId: string }) => {
|
||||
: t('common:common.Un used')}
|
||||
</Td>
|
||||
<Td display={'flex'} alignItems={'center'}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShowShareLink(`${baseUrl}/support/outLink/feishu/${item.shareId}`);
|
||||
openShowShareLinkModal();
|
||||
}}
|
||||
size={'sm'}
|
||||
mr={3}
|
||||
variant={'whitePrimary'}
|
||||
>
|
||||
{t('publish:request_address')}
|
||||
</Button>
|
||||
<MyMenu
|
||||
Button={
|
||||
<MyIcon
|
||||
name={'more'}
|
||||
_hover={{ bg: 'myGray.100 ' }}
|
||||
_hover={{ bg: 'myGray.100' }}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'md'}
|
||||
w={'14px'}
|
||||
@@ -129,7 +161,7 @@ const FeiShu = ({ appId }: { appId: string }) => {
|
||||
{
|
||||
label: t('common:common.Edit'),
|
||||
icon: 'edit',
|
||||
onClick: () =>
|
||||
onClick: () => {
|
||||
setEditFeiShuLinkData({
|
||||
_id: item._id,
|
||||
name: item.name,
|
||||
@@ -138,7 +170,9 @@ const FeiShu = ({ appId }: { appId: string }) => {
|
||||
responseDetail: item.responseDetail,
|
||||
defaultResponse: item.defaultResponse,
|
||||
immediateResponse: item.immediateResponse
|
||||
})
|
||||
});
|
||||
setIsEdit(true);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common:common.Delete'),
|
||||
@@ -167,27 +201,24 @@ const FeiShu = ({ appId }: { appId: string }) => {
|
||||
{editFeiShuLinkData && (
|
||||
<FeiShuEditModal
|
||||
appId={appId}
|
||||
// type={'feishu' as PublishChannelEnum}
|
||||
defaultData={editFeiShuLinkData}
|
||||
onCreate={(id) => {
|
||||
refetchShareChatList();
|
||||
setEditFeiShuLinkData(undefined);
|
||||
}}
|
||||
onEdit={() => {
|
||||
toast({
|
||||
status: 'success',
|
||||
title: t('common:common.Update Successful')
|
||||
});
|
||||
refetchShareChatList();
|
||||
setEditFeiShuLinkData(undefined);
|
||||
}}
|
||||
onCreate={() => Promise.all([refetchShareChatList(), setEditFeiShuLinkData(undefined)])}
|
||||
onEdit={() => Promise.all([refetchShareChatList(), setEditFeiShuLinkData(undefined)])}
|
||||
onClose={() => setEditFeiShuLinkData(undefined)}
|
||||
isEdit={isEdit}
|
||||
/>
|
||||
)}
|
||||
{shareChatList.length === 0 && !isFetching && (
|
||||
<EmptyTip text={t('common:core.app.share.Not share link')}></EmptyTip>
|
||||
)}
|
||||
<Loading loading={isFetching} fixed={false} />
|
||||
{showShareLinkModalOpen && (
|
||||
<ShowShareLinkModal
|
||||
shareLink={showShareLink ?? ''}
|
||||
onClose={closeShowShareLinkModal}
|
||||
img="/imgs/outlink/feishu-copylink-instruction.png"
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@@ -0,0 +1,168 @@
|
||||
import React from 'react';
|
||||
import { Flex, Box, Button, ModalBody, Input, Link } from '@chakra-ui/react';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
|
||||
import type { WecomAppType, OutLinkEditType } from '@fastgpt/global/support/outLink/type';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { createShareChat, updateShareChat } from '@/web/support/outLink/api';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import BasicInfo from '../components/BasicInfo';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
|
||||
const WecomEditModal = ({
|
||||
appId,
|
||||
defaultData,
|
||||
onClose,
|
||||
onCreate,
|
||||
onEdit,
|
||||
isEdit = false
|
||||
}: {
|
||||
appId: string;
|
||||
defaultData: OutLinkEditType<WecomAppType>;
|
||||
onClose: () => void;
|
||||
onCreate: (id: string) => void;
|
||||
onEdit: () => void;
|
||||
isEdit?: boolean;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
register,
|
||||
setValue,
|
||||
handleSubmit: submitShareChat
|
||||
} = useForm({
|
||||
defaultValues: defaultData
|
||||
});
|
||||
|
||||
const { runAsync: onclickCreate, loading: creating } = useRequest2(
|
||||
(e) =>
|
||||
createShareChat({
|
||||
...e,
|
||||
appId,
|
||||
type: PublishChannelEnum.wecom
|
||||
}),
|
||||
{
|
||||
errorToast: t('common:common.Create Failed'),
|
||||
successToast: t('common:common.Create Success'),
|
||||
onSuccess: onCreate
|
||||
}
|
||||
);
|
||||
|
||||
const { runAsync: onclickUpdate, loading: updating } = useRequest2((e) => updateShareChat(e), {
|
||||
errorToast: t('common:common.Update Failed'),
|
||||
successToast: t('common:common.Update Success'),
|
||||
onSuccess: onEdit
|
||||
});
|
||||
|
||||
const { feConfigs } = useSystemStore();
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
iconSrc="core/app/publish/wecom"
|
||||
title={isEdit ? t('publish:wecom.edit_modal_title') : t('publish:wecom.create_modal_title')}
|
||||
minW={['auto', '60rem']}
|
||||
>
|
||||
<ModalBody display={'grid'} gridTemplateColumns={['1fr', '1fr 1fr']} fontSize={'14px'} p={0}>
|
||||
<Box p={8} minH={['auto', '400px']} borderRight={'base'}>
|
||||
<BasicInfo register={register} setValue={setValue} defaultData={defaultData} />
|
||||
</Box>
|
||||
<Flex p={8} minH={['auto', '400px']} flexDirection="column" gap={6}>
|
||||
<Flex alignItems="center">
|
||||
<Box color="myGray.600">{t('publish:wecom.api')}</Box>
|
||||
{feConfigs?.docUrl && (
|
||||
<Link
|
||||
href={feConfigs.openAPIDocUrl || getDocPath('/docs/use-cases/wecom-bot')}
|
||||
target={'_blank'}
|
||||
ml={2}
|
||||
color={'primary.500'}
|
||||
fontSize={'sm'}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
<MyIcon name="book" mr="1" />
|
||||
{t('common:common.Read document')}
|
||||
</Flex>
|
||||
</Link>
|
||||
)}
|
||||
</Flex>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 6.25rem'} required>
|
||||
Corp ID
|
||||
</FormLabel>
|
||||
<Input
|
||||
placeholder="Corp ID"
|
||||
{...register('app.CorpId', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 6.25rem'} required>
|
||||
Agent ID
|
||||
</FormLabel>
|
||||
<Input
|
||||
placeholder="Agent ID"
|
||||
{...register('app.AgentId', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 6.25rem'} required>
|
||||
Secret
|
||||
</FormLabel>
|
||||
<Input
|
||||
placeholder="Secret"
|
||||
{...register('app.SuiteSecret', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 6.25rem'} required>
|
||||
Token
|
||||
</FormLabel>
|
||||
<Input
|
||||
placeholder="Token"
|
||||
{...register('app.CallbackToken', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 6.25rem'} required>
|
||||
AES Key
|
||||
</FormLabel>
|
||||
<Input
|
||||
placeholder="AES Key"
|
||||
{...register('app.CallbackEncodingAesKey', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
<Box flex={1}></Box>
|
||||
|
||||
<Flex justifyContent={'end'}>
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
|
||||
{t('common:common.Close')}
|
||||
</Button>
|
||||
<Button
|
||||
isLoading={creating || updating}
|
||||
onClick={submitShareChat((data) =>
|
||||
isEdit ? onclickUpdate(data) : onclickCreate(data)
|
||||
)}
|
||||
>
|
||||
{t('common:common.Confirm')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default WecomEditModal;
|
@@ -0,0 +1,223 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import {
|
||||
Flex,
|
||||
Box,
|
||||
Button,
|
||||
TableContainer,
|
||||
Table,
|
||||
Thead,
|
||||
Tr,
|
||||
Th,
|
||||
Td,
|
||||
Tbody,
|
||||
useDisclosure
|
||||
} from '@chakra-ui/react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useLoading } from '@fastgpt/web/hooks/useLoading';
|
||||
import { getShareChatList, delShareChatById } from '@/web/support/outLink/api';
|
||||
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
|
||||
import { defaultOutLinkForm } from '@/web/core/app/constants';
|
||||
import type { WecomAppType, OutLinkEditType } from '@fastgpt/global/support/outLink/type.d';
|
||||
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import dayjs from 'dayjs';
|
||||
import dynamic from 'next/dynamic';
|
||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
|
||||
const WecomEditModal = dynamic(() => import('./WecomEditModal'));
|
||||
const ShowShareLinkModal = dynamic(() => import('../components/showShareLinkModal'));
|
||||
|
||||
const Wecom = ({ appId }: { appId: string }) => {
|
||||
const { t } = useTranslation();
|
||||
const { Loading, setIsLoading } = useLoading();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const [editWecomData, setEditWecomData] = useState<OutLinkEditType<WecomAppType>>();
|
||||
const [isEdit, setIsEdit] = useState<boolean>(false);
|
||||
|
||||
const baseUrl = useMemo(
|
||||
() => feConfigs?.customApiDomain || `${location.origin}/api`,
|
||||
[feConfigs?.customApiDomain]
|
||||
);
|
||||
|
||||
const {
|
||||
data: shareChatList = [],
|
||||
loading: isFetching,
|
||||
runAsync: refetchShareChatList
|
||||
} = useRequest2(() => getShareChatList<WecomAppType>({ appId, type: PublishChannelEnum.wecom }), {
|
||||
manual: false
|
||||
});
|
||||
|
||||
const {
|
||||
onOpen: openShowShareLinkModal,
|
||||
isOpen: showShareLinkModalOpen,
|
||||
onClose: closeShowShareLinkModal
|
||||
} = useDisclosure();
|
||||
|
||||
const [showShareLink, setShowShareLink] = useState<string | null>(null);
|
||||
|
||||
return (
|
||||
<Box position={'relative'} pt={3} px={5} minH={'50vh'}>
|
||||
<Flex justifyContent={'space-between'} flexDirection="row">
|
||||
<Box fontWeight={'bold'} fontSize={['md', 'lg']}>
|
||||
{t('publish:wecom.title')}
|
||||
</Box>
|
||||
<Button
|
||||
variant={'primary'}
|
||||
colorScheme={'blue'}
|
||||
size={['sm', 'md']}
|
||||
leftIcon={<MyIcon name={'common/addLight'} w="1.25rem" color="white" />}
|
||||
ml={3}
|
||||
{...(shareChatList.length >= 10
|
||||
? {
|
||||
isDisabled: true,
|
||||
title: t('common:core.app.share.Amount limit tip')
|
||||
}
|
||||
: {})}
|
||||
onClick={() => {
|
||||
setEditWecomData(defaultOutLinkForm as any); // HACK
|
||||
setIsEdit(false);
|
||||
}}
|
||||
>
|
||||
{t('common:add_new')}
|
||||
</Button>
|
||||
</Flex>
|
||||
<TableContainer mt={3}>
|
||||
<Table variant={'simple'} w={'100%'} overflowX={'auto'} fontSize={'sm'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>{t('common:common.Name')} </Th>
|
||||
<Th> {t('common:support.outlink.Usage points')} </Th>
|
||||
{feConfigs?.isPlus && (
|
||||
<>
|
||||
<Th>{t('common:core.app.share.Ip limit title')} </Th>
|
||||
<Th> {t('common:common.Expired Time')} </Th>
|
||||
</>
|
||||
)}
|
||||
<Th>{t('common:common.Last use time')} </Th>
|
||||
<Th> </Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{shareChatList.map((item) => (
|
||||
<Tr key={item._id}>
|
||||
<Td>{item.name} </Td>
|
||||
<Td>
|
||||
{Math.round(item.usagePoints)}
|
||||
{feConfigs?.isPlus
|
||||
? `${
|
||||
item.limit?.maxUsagePoints && item.limit.maxUsagePoints > -1
|
||||
? ` / ${item.limit.maxUsagePoints}`
|
||||
: ` / ${t('common:common.Unlimited')}`
|
||||
}`
|
||||
: ''}
|
||||
</Td>
|
||||
{feConfigs?.isPlus && (
|
||||
<>
|
||||
<Td>{item?.limit?.QPM || '-'} </Td>
|
||||
<Td>
|
||||
{item?.limit?.expiredTime
|
||||
? dayjs(item.limit?.expiredTime).format('YYYY/MM/DD\nHH:mm')
|
||||
: '-'}
|
||||
</Td>
|
||||
</>
|
||||
)}
|
||||
<Td>
|
||||
{item.lastTime
|
||||
? t(formatTimeToChatTime(item.lastTime) as any)
|
||||
: t('common:common.Un used')}
|
||||
</Td>
|
||||
<Td display={'flex'} alignItems={'center'}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setShowShareLink(`${baseUrl}/support/outLink/wecom/${item.shareId}`);
|
||||
openShowShareLinkModal();
|
||||
}}
|
||||
size={'sm'}
|
||||
mr={3}
|
||||
variant={'whitePrimary'}
|
||||
>
|
||||
{t('publish:request_address')}
|
||||
</Button>
|
||||
<MyMenu
|
||||
Button={
|
||||
<MyIcon
|
||||
name={'more'}
|
||||
_hover={{ bg: 'myGray.100' }}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'md'}
|
||||
w={'14px'}
|
||||
p={2}
|
||||
/>
|
||||
}
|
||||
menuList={[
|
||||
{
|
||||
children: [
|
||||
{
|
||||
label: t('common:common.Edit'),
|
||||
icon: 'edit',
|
||||
onClick: () => {
|
||||
setEditWecomData({
|
||||
_id: item._id,
|
||||
name: item.name,
|
||||
limit: item.limit,
|
||||
app: item.app,
|
||||
responseDetail: item.responseDetail,
|
||||
defaultResponse: item.defaultResponse,
|
||||
immediateResponse: item.immediateResponse
|
||||
});
|
||||
setIsEdit(true);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: t('common:common.Delete'),
|
||||
icon: 'delete',
|
||||
onClick: async () => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
await delShareChatById(item._id);
|
||||
refetchShareChatList();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
{editWecomData && (
|
||||
<WecomEditModal
|
||||
appId={appId}
|
||||
defaultData={editWecomData}
|
||||
onCreate={() => Promise.all([refetchShareChatList(), setEditWecomData(undefined)])}
|
||||
onEdit={() => Promise.all([refetchShareChatList(), setEditWecomData(undefined)])}
|
||||
onClose={() => setEditWecomData(undefined)}
|
||||
isEdit={isEdit}
|
||||
/>
|
||||
)}
|
||||
{shareChatList.length === 0 && !isFetching && (
|
||||
<EmptyTip text={t('common:core.app.share.Not share link')}> </EmptyTip>
|
||||
)}
|
||||
<Loading loading={isFetching} fixed={false} />
|
||||
{showShareLinkModalOpen && (
|
||||
<ShowShareLinkModal
|
||||
shareLink={showShareLink ?? ''}
|
||||
onClose={closeShowShareLinkModal}
|
||||
img="/imgs/outlink/wecom-copylink-instruction.png"
|
||||
/>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Wecom);
|
@@ -0,0 +1,87 @@
|
||||
import React from 'react';
|
||||
import { Box, Flex, Input } from '@chakra-ui/react';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import dayjs from 'dayjs';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { UseFormRegister, UseFormSetValue } from 'react-hook-form';
|
||||
import { OutLinkEditType } from '@fastgpt/global/support/outLink/type';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
|
||||
function BasicInfo({
|
||||
register,
|
||||
setValue,
|
||||
defaultData
|
||||
}: {
|
||||
register: UseFormRegister<OutLinkEditType<any>>;
|
||||
setValue: UseFormSetValue<OutLinkEditType<any>>;
|
||||
defaultData: OutLinkEditType<any>;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Flex flexDirection="column" gap={6}>
|
||||
<Box color="myGray.600">{t('publish:basic_info')}</Box>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel required flex={'0 0 6.25rem'}>
|
||||
{t('common:Name')}
|
||||
</FormLabel>
|
||||
<Input
|
||||
placeholder={t('publish:publish_name')}
|
||||
maxLength={20}
|
||||
{...register('name', {
|
||||
required: t('common:common.name_is_empty')
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 6.25rem'} alignItems={'center'}>
|
||||
QPM
|
||||
<QuestionTip ml={1} label={t('publish:qpm_tips')}></QuestionTip>
|
||||
</FormLabel>
|
||||
<Input
|
||||
max={1000}
|
||||
{...register('limit.QPM', {
|
||||
min: 0,
|
||||
max: 1000,
|
||||
valueAsNumber: true,
|
||||
required: t('publish:qpm_is_empty')
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 6.25rem'} alignItems={'center'}>
|
||||
{t('common:support.outlink.Max usage points')}
|
||||
<QuestionTip
|
||||
ml={1}
|
||||
label={t('common:support.outlink.Max usage points tip')}
|
||||
></QuestionTip>
|
||||
</FormLabel>
|
||||
<Input
|
||||
{...register('limit.maxUsagePoints', {
|
||||
min: -1,
|
||||
max: 10000000,
|
||||
valueAsNumber: true,
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 6.25rem'} alignItems={'center'}>
|
||||
{t('common:common.Expired Time')}
|
||||
</FormLabel>
|
||||
<Input
|
||||
type="datetime-local"
|
||||
defaultValue={
|
||||
defaultData.limit?.expiredTime
|
||||
? dayjs(defaultData.limit?.expiredTime).format('YYYY-MM-DDTHH:mm')
|
||||
: ''
|
||||
}
|
||||
onChange={(e) => {
|
||||
setValue('limit.expiredTime', new Date(e.target.value));
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
export default BasicInfo;
|
@@ -0,0 +1,50 @@
|
||||
import { useCopyData } from '@/web/common/hooks/useCopyData';
|
||||
import { Box, Image, Flex, ModalBody } from '@chakra-ui/react';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export type ShowShareLinkModalProps = {
|
||||
shareLink: string;
|
||||
onClose: () => void;
|
||||
img: string;
|
||||
};
|
||||
|
||||
function ShowShareLinkModal({ shareLink, onClose, img }: ShowShareLinkModalProps) {
|
||||
const { copyData } = useCopyData();
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<MyModal onClose={onClose} title={t('publish:show_share_link_modal_title')}>
|
||||
<ModalBody>
|
||||
<Box borderRadius={'md'} bg={'myGray.100'} overflow={'hidden'} fontSize={'sm'}>
|
||||
<Flex
|
||||
p={3}
|
||||
bg={'myWhite.500'}
|
||||
border="base"
|
||||
borderTopLeftRadius={'md'}
|
||||
borderTopRightRadius={'md'}
|
||||
>
|
||||
<Box flex={1}>{t('publish:copy_link_hint')}</Box>
|
||||
<MyIcon
|
||||
name={'copy'}
|
||||
w={'16px'}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'primary.500' }}
|
||||
onClick={() => copyData(shareLink)}
|
||||
/>
|
||||
</Flex>
|
||||
<Box whiteSpace={'pre'} p={3} overflowX={'auto'}>
|
||||
{shareLink}
|
||||
</Box>
|
||||
</Box>
|
||||
<Box mt="4" borderRadius="0.5rem" border="1px" borderStyle="solid" borderColor="myGray.200">
|
||||
<Image src={img} borderRadius="0.5rem" alt="" />
|
||||
</Box>
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
);
|
||||
}
|
||||
|
||||
export default ShowShareLinkModal;
|
@@ -1,5 +1,5 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { Box, Flex, useTheme } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
|
||||
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
|
||||
import dynamic from 'next/dynamic';
|
||||
@@ -10,14 +10,18 @@ import { useTranslation } from 'next-i18next';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext } from '../context';
|
||||
import { cardStyles } from '../constants';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
|
||||
import Link from './Link';
|
||||
const Link = dynamic(() => import('./Link'));
|
||||
const API = dynamic(() => import('./API'));
|
||||
const FeiShu = dynamic(() => import('./FeiShu'));
|
||||
const Wecom = dynamic(() => import('./Wecom'));
|
||||
|
||||
const OutLink = () => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { feConfigs } = useSystemStore();
|
||||
const { toast } = useToast();
|
||||
|
||||
const appId = useContextSelector(AppContext, (v) => v.appId);
|
||||
|
||||
@@ -26,33 +30,65 @@ const OutLink = () => {
|
||||
icon: '/imgs/modal/shareFill.svg',
|
||||
title: t('common:core.app.Share link'),
|
||||
desc: t('common:core.app.Share link desc'),
|
||||
value: PublishChannelEnum.share
|
||||
value: PublishChannelEnum.share,
|
||||
isProFn: false
|
||||
},
|
||||
{
|
||||
icon: 'support/outlink/apikeyFill',
|
||||
title: t('common:core.app.Api request'),
|
||||
desc: t('common:core.app.Api request desc'),
|
||||
value: PublishChannelEnum.apikey
|
||||
value: PublishChannelEnum.apikey,
|
||||
isProFn: false
|
||||
},
|
||||
{
|
||||
icon: 'core/app/publish/lark',
|
||||
title: t('publish:feishu_bot'),
|
||||
desc: t('publish:feishu_bot_desc'),
|
||||
value: PublishChannelEnum.feishu,
|
||||
isProFn: true
|
||||
},
|
||||
{
|
||||
icon: 'core/app/publish/wecom',
|
||||
title: t('publish:wecom.bot'),
|
||||
desc: t('publish:wecom.bot_desc'),
|
||||
value: PublishChannelEnum.wecom,
|
||||
isProFn: true
|
||||
}
|
||||
// {
|
||||
// icon: 'core/app/publish/lark',
|
||||
// title: t('common:core.app.publish.Fei shu bot'),
|
||||
// desc: t('common:core.app.publish.Fei Shu Bot Desc'),
|
||||
// value: PublishChannelEnum.feishu
|
||||
// }
|
||||
]);
|
||||
|
||||
const [linkType, setLinkType] = useState<PublishChannelEnum>(PublishChannelEnum.share);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
display={['block', 'flex']}
|
||||
overflowY={'auto'}
|
||||
overflowX={'hidden'}
|
||||
h={'100%'}
|
||||
flexDirection={'column'}
|
||||
>
|
||||
<Box {...cardStyles} boxShadow={2} px={[4, 8]} py={[4, 6]}>
|
||||
<MyRadio
|
||||
gridTemplateColumns={['repeat(1,1fr)', 'repeat(auto-fill, minmax(0, 400px))']}
|
||||
gridTemplateColumns={[
|
||||
'repeat(1,1fr)',
|
||||
'repeat(2, 1fr)',
|
||||
'repeat(3, 1fr)',
|
||||
'repeat(3, 1fr)',
|
||||
'repeat(4, 1fr)'
|
||||
]}
|
||||
iconSize={'20px'}
|
||||
list={publishList.current}
|
||||
value={linkType}
|
||||
onChange={(e) => setLinkType(e as PublishChannelEnum)}
|
||||
onChange={(e) => {
|
||||
const config = publishList.current.find((v) => v.value === e)!;
|
||||
if (!feConfigs.isPlus && config.isProFn) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('common:common.system.Commercial version function')
|
||||
});
|
||||
} else {
|
||||
setLinkType(e as PublishChannelEnum);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -63,15 +99,16 @@ const OutLink = () => {
|
||||
mt={4}
|
||||
px={[4, 8]}
|
||||
py={[4, 6]}
|
||||
flex={'1 0 0'}
|
||||
flex={1}
|
||||
>
|
||||
{linkType === PublishChannelEnum.share && (
|
||||
<Link appId={appId} type={PublishChannelEnum.share} />
|
||||
)}
|
||||
{linkType === PublishChannelEnum.apikey && <API appId={appId} />}
|
||||
{linkType === PublishChannelEnum.feishu && <FeiShu appId={appId} />}
|
||||
{linkType === PublishChannelEnum.wecom && <Wecom appId={appId} />}
|
||||
</Flex>
|
||||
</>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
@@ -6,7 +6,7 @@ import Edit from './Edit';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppContext, TabEnum } from '../context';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
@@ -29,10 +29,10 @@ const SimpleEdit = () => {
|
||||
{currentTab === TabEnum.appEdit ? (
|
||||
<Edit appForm={appForm} setAppForm={setAppForm} />
|
||||
) : (
|
||||
<Flex h={'100%'} flexDirection={'column'} mt={4}>
|
||||
<Box flex={'1 0 0'} h={0} mt={4}>
|
||||
{currentTab === TabEnum.publish && <PublishChannel />}
|
||||
{currentTab === TabEnum.logs && <Logs />}
|
||||
</Flex>
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
|
@@ -1,12 +1,12 @@
|
||||
import { getSystemPlugins } from '@/service/core/app/plugin';
|
||||
import { initSystemConfig } from '.';
|
||||
import { createDatasetTrainingMongoWatch } from '@/service/core/dataset/training/utils';
|
||||
import { getSystemPluginTemplates } from '@fastgpt/plugins/register';
|
||||
import { MongoSystemConfigs } from '@fastgpt/service/common/system/config/schema';
|
||||
import { MongoSystemPluginSchema } from '@fastgpt/service/core/app/plugin/systemPluginSchema';
|
||||
|
||||
export const startMongoWatch = async () => {
|
||||
reloadConfigWatch();
|
||||
refetchSystemPlugin();
|
||||
refetchSystemPlugins();
|
||||
createDatasetTrainingMongoWatch();
|
||||
};
|
||||
|
||||
@@ -23,12 +23,12 @@ const reloadConfigWatch = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const refetchSystemPlugin = () => {
|
||||
const refetchSystemPlugins = () => {
|
||||
const changeStream = MongoSystemPluginSchema.watch();
|
||||
|
||||
changeStream.on('change', async (change) => {
|
||||
try {
|
||||
getSystemPluginTemplates(true);
|
||||
getSystemPlugins(true);
|
||||
} catch (error) {}
|
||||
});
|
||||
};
|
||||
|
69
projects/app/src/service/core/app/plugin.ts
Normal file
69
projects/app/src/service/core/app/plugin.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { FastGPTProUrl, isProduction } from '@fastgpt/service/common/system/constants';
|
||||
import { cloneDeep } from 'lodash';
|
||||
import { getCommunityCb, getCommunityPlugins } from '@fastgpt/plugins/register';
|
||||
import { GET, POST } from '@fastgpt/service/common/api/plusRequest';
|
||||
import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { SystemPluginResponseType } from '@fastgpt/plugins/type';
|
||||
|
||||
/* Get plugins */
|
||||
const getCommercialPlugins = () => {
|
||||
return GET<SystemPluginTemplateItemType[]>('/core/app/plugin/getSystemPlugins');
|
||||
};
|
||||
export const getSystemPlugins = async (refresh = false) => {
|
||||
if (isProduction && global.systemPlugins && !refresh) return cloneDeep(global.systemPlugins);
|
||||
|
||||
try {
|
||||
if (!global.systemPlugins) {
|
||||
global.systemPlugins = [];
|
||||
}
|
||||
|
||||
global.systemPlugins = FastGPTProUrl ? await getCommercialPlugins() : getCommunityPlugins();
|
||||
|
||||
addLog.info(`Load system plugin successfully: ${global.systemPlugins.length}`);
|
||||
|
||||
return cloneDeep(global.systemPlugins);
|
||||
} catch (error) {
|
||||
//@ts-ignore
|
||||
global.systemPlugins = undefined;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
||||
/* Get plugin callback */
|
||||
const getCommercialCb = async () => {
|
||||
const plugins = await getSystemPlugins();
|
||||
const result = plugins.map((plugin) => {
|
||||
const name = plugin.id.split('-')[1];
|
||||
|
||||
return {
|
||||
name,
|
||||
cb: (e: any) =>
|
||||
POST<Record<string, any>>('/core/app/plugin/run', {
|
||||
pluginName: name,
|
||||
data: e
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
return result.reduce<Record<string, (e: any) => SystemPluginResponseType>>(
|
||||
(acc, { name, cb }) => {
|
||||
acc[name] = cb;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
};
|
||||
export const getSystemPluginCb = async () => {
|
||||
if (isProduction && global.systemPluginCb) return global.systemPluginCb;
|
||||
|
||||
try {
|
||||
global.systemPluginCb = {};
|
||||
global.systemPluginCb = FastGPTProUrl ? await getCommercialCb() : await getCommunityCb();
|
||||
return global.systemPluginCb;
|
||||
} catch (error) {
|
||||
//@ts-ignore
|
||||
global.systemPluginCb = undefined;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
@@ -12,6 +12,7 @@ import { startMongoWatch } from './common/system/volumnMongoWatch';
|
||||
import { startTrainingQueue } from './core/dataset/training/utils';
|
||||
import { systemStartCb } from '@fastgpt/service/common/system/tools';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { getSystemPluginCb } from './core/app/plugin';
|
||||
|
||||
/**
|
||||
* This function is equivalent to the entry to the service
|
||||
@@ -31,7 +32,7 @@ export function connectToDatabase() {
|
||||
systemStartCb();
|
||||
|
||||
//init system config;init vector database;init root user
|
||||
await Promise.all([getInitConfig(), initVectorStore(), initRootUser()]);
|
||||
await Promise.all([getInitConfig(), getSystemPluginCb(), initVectorStore(), initRootUser()]);
|
||||
|
||||
startMongoWatch();
|
||||
// cron
|
||||
|
@@ -1,113 +0,0 @@
|
||||
import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import { getAppChatConfig, getGuideModule } from '@fastgpt/global/core/workflow/utils';
|
||||
import { AppChatConfigType } from '@fastgpt/global/core/app/type';
|
||||
|
||||
type Props = {
|
||||
chatId: string;
|
||||
appId: string;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
nodes: StoreNodeItemType[];
|
||||
appChatConfig?: AppChatConfigType;
|
||||
variables?: Record<string, any>;
|
||||
isUpdateUseTime: boolean;
|
||||
newTitle: string;
|
||||
source: `${ChatSourceEnum}`;
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
content: [UserChatItemType & { dataId?: string }, AIChatItemType & { dataId?: string }];
|
||||
metadata?: Record<string, any>;
|
||||
};
|
||||
|
||||
export async function saveChat({
|
||||
chatId,
|
||||
appId,
|
||||
teamId,
|
||||
tmbId,
|
||||
nodes,
|
||||
appChatConfig,
|
||||
variables,
|
||||
isUpdateUseTime,
|
||||
newTitle,
|
||||
source,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
content,
|
||||
metadata = {}
|
||||
}: Props) {
|
||||
try {
|
||||
const chat = await MongoChat.findOne(
|
||||
{
|
||||
appId,
|
||||
chatId
|
||||
},
|
||||
'_id metadata'
|
||||
);
|
||||
|
||||
const metadataUpdate = {
|
||||
...chat?.metadata,
|
||||
...metadata
|
||||
};
|
||||
const { welcomeText, variables: variableList } = getAppChatConfig({
|
||||
chatConfig: appChatConfig,
|
||||
systemConfigNode: getGuideModule(nodes),
|
||||
isPublicFetch: false
|
||||
});
|
||||
|
||||
await mongoSessionRun(async (session) => {
|
||||
await MongoChatItem.insertMany(
|
||||
content.map((item) => ({
|
||||
chatId,
|
||||
teamId,
|
||||
tmbId,
|
||||
appId,
|
||||
...item
|
||||
})),
|
||||
{ session }
|
||||
);
|
||||
|
||||
await MongoChat.updateOne(
|
||||
{
|
||||
appId,
|
||||
chatId
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
teamId,
|
||||
tmbId,
|
||||
appId,
|
||||
chatId,
|
||||
variableList,
|
||||
welcomeText,
|
||||
variables: variables || {},
|
||||
title: newTitle,
|
||||
source,
|
||||
shareId,
|
||||
outLinkUid,
|
||||
metadata: metadataUpdate,
|
||||
updateTime: new Date()
|
||||
}
|
||||
},
|
||||
{
|
||||
session,
|
||||
upsert: true
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
if (isUpdateUseTime) {
|
||||
await MongoApp.findByIdAndUpdate(appId, {
|
||||
updateTime: new Date()
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
addLog.error(`update chat history error`, error);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { AppDetailType } from '@fastgpt/global/core/app/type.d';
|
||||
import type { FeishuType, OutLinkEditType } from '@fastgpt/global/support/outLink/type.d';
|
||||
import type { FeishuAppType, OutLinkEditType } from '@fastgpt/global/support/outLink/type.d';
|
||||
import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
|
||||
import { NullPermission } from '@fastgpt/global/support/permission/constant';
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
@@ -32,30 +32,12 @@ export const defaultOutLinkForm: OutLinkEditType = {
|
||||
}
|
||||
};
|
||||
|
||||
// export const defaultWecomOutLinkForm: OutLinkConfigEditType = {
|
||||
// name: '',
|
||||
// wecomConfig: {
|
||||
// ReplyLimit: false,
|
||||
// defaultResponse: '',
|
||||
// immediateResponse: false,
|
||||
// WXWORK_TOKEN: '',
|
||||
// WXWORK_AESKEY: '',
|
||||
// WXWORK_SECRET: '',
|
||||
// WXWORD_ID: ''
|
||||
// },
|
||||
// limit: {
|
||||
// QPM: 100,
|
||||
// maxUsagePoints: -1
|
||||
// }
|
||||
// };
|
||||
|
||||
export const defaultFeishuOutLinkForm: OutLinkEditType<FeishuType> = {
|
||||
export const defaultFeishuOutLinkForm: OutLinkEditType<FeishuAppType> = {
|
||||
name: '',
|
||||
limit: {
|
||||
QPM: 100,
|
||||
maxUsagePoints: -1
|
||||
},
|
||||
responseDetail: false
|
||||
}
|
||||
};
|
||||
|
||||
export enum TTSTypeEnum {
|
||||
|
@@ -1,5 +1,9 @@
|
||||
import { GET, POST, DELETE } from '@/web/common/api/request';
|
||||
import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type.d';
|
||||
import type {
|
||||
OutlinkAppType,
|
||||
OutLinkEditType,
|
||||
OutLinkSchema
|
||||
} from '@fastgpt/global/support/outLink/type.d';
|
||||
|
||||
// create a shareChat
|
||||
export function createShareChat<T>(
|
||||
@@ -15,7 +19,10 @@ export const putShareChat = (data: OutLinkEditType) =>
|
||||
POST<string>(`/support/outLink/update`, data);
|
||||
|
||||
// get shareChat
|
||||
export function getShareChatList<T>(data: { appId: string; type: OutLinkSchema<T>['type'] }) {
|
||||
export function getShareChatList<T extends OutlinkAppType>(data: {
|
||||
appId: string;
|
||||
type: OutLinkSchema<T>['type'];
|
||||
}) {
|
||||
return GET<OutLinkSchema<T>[]>(`/support/outLink/list`, data);
|
||||
}
|
||||
|
||||
@@ -25,7 +32,7 @@ export function delShareChatById(id: string) {
|
||||
}
|
||||
|
||||
// update a shareChat
|
||||
export function updateShareChat<T>(data: OutLinkEditType<T>) {
|
||||
export function updateShareChat<T extends OutlinkAppType>(data: OutLinkEditType<T>) {
|
||||
return POST<string>(`/support/outLink/update`, data);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user