diff --git a/docSite/assets/imgs/feishu-bot-1.png b/docSite/assets/imgs/feishu-bot-1.png new file mode 100644 index 000000000000..cb8029e01a8f Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-1.png differ diff --git a/docSite/assets/imgs/feishu-bot-10.jpg b/docSite/assets/imgs/feishu-bot-10.jpg new file mode 100644 index 000000000000..f6e6a32e389a Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-10.jpg differ diff --git a/docSite/assets/imgs/feishu-bot-11.jpg b/docSite/assets/imgs/feishu-bot-11.jpg new file mode 100644 index 000000000000..fee456442f63 Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-11.jpg differ diff --git a/docSite/assets/imgs/feishu-bot-12.jpg b/docSite/assets/imgs/feishu-bot-12.jpg new file mode 100644 index 000000000000..897be94b68e2 Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-12.jpg differ diff --git a/docSite/assets/imgs/feishu-bot-13.jpg b/docSite/assets/imgs/feishu-bot-13.jpg new file mode 100644 index 000000000000..47aa476dffde Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-13.jpg differ diff --git a/docSite/assets/imgs/feishu-bot-2.png b/docSite/assets/imgs/feishu-bot-2.png new file mode 100644 index 000000000000..c2f4fabb5dfa Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-2.png differ diff --git a/docSite/assets/imgs/feishu-bot-3.png b/docSite/assets/imgs/feishu-bot-3.png new file mode 100644 index 000000000000..5d91d0ce0ab0 Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-3.png differ diff --git a/docSite/assets/imgs/feishu-bot-4.png b/docSite/assets/imgs/feishu-bot-4.png new file mode 100644 index 000000000000..5c5f6d8c8b2a Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-4.png differ diff --git a/docSite/assets/imgs/feishu-bot-5.png b/docSite/assets/imgs/feishu-bot-5.png new file mode 100644 index 000000000000..3dafe3e64ca8 Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-5.png differ diff --git a/docSite/assets/imgs/feishu-bot-6.png b/docSite/assets/imgs/feishu-bot-6.png new file mode 100644 index 000000000000..de2d6ac9a6f7 Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-6.png differ diff --git a/docSite/assets/imgs/feishu-bot-7.png b/docSite/assets/imgs/feishu-bot-7.png new file mode 100644 index 000000000000..c5c7808470d4 Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-7.png differ diff --git a/docSite/assets/imgs/feishu-bot-8.png b/docSite/assets/imgs/feishu-bot-8.png new file mode 100644 index 000000000000..35eb40ff293e Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-8.png differ diff --git a/docSite/assets/imgs/feishu-bot-9.png b/docSite/assets/imgs/feishu-bot-9.png new file mode 100644 index 000000000000..f2602483a6fc Binary files /dev/null and b/docSite/assets/imgs/feishu-bot-9.png differ diff --git a/docSite/content/zh-cn/docs/development/upgrading/4810.md b/docSite/content/zh-cn/docs/development/upgrading/4810.md index f5ad166d5b28..5b59a889c6c0 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/4810.md +++ b/docSite/content/zh-cn/docs/development/upgrading/4810.md @@ -1,12 +1,20 @@ --- -title: 'V4.8. 10(进行中)' -description: 'FastGPT V4.8. 10 更新说明' +title: 'V4.8.10(进行中)' +description: 'FastGPT V4.8.10 更新说明' icon: 'upgrade' draft: false toc: true weight: 816 --- +## 更新指南 + +### 1. 做好数据备份 + +### 2. 更新商业版环境变量 + +商业版用户,需要给`fastgpt-pro`镜像,增加沙盒的环境变量:`SANDBOX_URL=http://fastgpt-sandbox.ns-hti44k5d.svc.cluster.local:3000` + ------- ## V4.8.10 更新说明 diff --git a/docSite/content/zh-cn/docs/use-cases/feishu.md b/docSite/content/zh-cn/docs/use-cases/feishu.md index d98acfeee226..fba2ea77d314 100644 --- a/docSite/content/zh-cn/docs/use-cases/feishu.md +++ b/docSite/content/zh-cn/docs/use-cases/feishu.md @@ -1,70 +1,85 @@ --- -title: " 接入飞书(社区文章)" +title: "教程 - 接入飞书机器人" description: "FastGPT 接入飞书机器人" icon: "chat" draft: false toc: true -weight: 503 +weight: 507 --- +## 1. 申请飞书应用 -# FastGPT 一分钟接入飞书 +开一个免费的测试企业更方便进行调试。 -[Feishu OpenAI GitHub 地址](https://github.com/ConnectAI-E/Feishu-OpenAI) +1. 在[飞书开放平台](https://open.feishu.cn/app)的开发者后台申请企业自建应用。 -[查看视频教程](https://www.bilibili.com/video/BV1Su4y1r7R3/?spm_id_from=333.999.list.card_archive.click) +![图片](/imgs/feishu-bot-1.png) -由于 FastGPT 的 API 接口和 OpenAI 的规范一致,可以无需变更第三方应用即可使用 FastGPT 上编排好的应用。API 使用可参考 [这篇文章](/docs/use-cases/openapi/)。编排示例,可参考 [高级编排介绍](/docs/workflow/intro) +添加一个**机器人**应用。 -## 1. 获取 FastGPT 的 OpenAPI 秘钥 +## 2. 在 FastGPT 新建发布渠道 -依次选择应用 -> 「API 访问」,然后点击「API 密钥」来创建密钥。 [参考这篇文章](/docs/use-cases/openapi/) +在fastgpt中选择想要接入的应用,在 发布渠道 页面,新建一个接入飞书机器人的发布渠道,填写好基础信息。 + +![图片](/imgs/feishu-bot-2.png) -![](/imgs/fastgpt-api.png) +## 3. 获取应用的 App ID, App Secret 两个凭证 -## 2. 部署飞书服务 +在飞书开放平台开发者后台,刚刚创建的企业自建应用中,找到 App ID 和 App Secret,填入 FastGPT 新建发布渠道的对话框里面。 + +![图片](/imgs/feishu-bot-3.png) -推荐使用 Railway 一键部署 +填入两个参数到 FastGPT 配置弹窗中。 -[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/template/10D-TF?referralCode=oMcVS2) +![图片](/imgs/feishu-bot-4.png) -参考环境变量配置: +(可选)在飞书开放平台开发者后台,点击事件与回调 -> 加密策略 获取 Encrypt Key,并填入飞书机器人接入的对话框里面 -![](/imgs/feishu-env.png) +![图片](/imgs/feishu-bot-5.png) -FastGPT 集成**重点参数:** +Encrypt Key 用于加密飞书服务器与 FastGPT 之间通信。 +建议如果使用 Https 协议,则不需要 Encrypt Key。如果使用 Http 协议通信,则建议使用 Encrypt Key +Verification Token 默认生成的这个 Token 用于校验来源。但我们使用飞书官方推荐的另一种更为安全的校验方式,因此可以忽略这个配置项。 +## 4. 配置回调地址 -```bash -#上一步FastGPT的OpenAPI 秘钥 -OPENAI_KEY=fastgpt-z51pkjqm9nrk03a1rx2funoy -#调用OpenAI的BaseUrl要换成FastGPT的 -API_URL=https://api.fastgpt.in/api/openapi -``` +新建好发布渠道后,点击**请求地址**,复制对应的请求地址。 -## 3. 创建飞书机器人 +在飞书控制台,点击左侧的 `事件与回调` ,点击`配置订阅方式`旁边的编辑 icon,粘贴刚刚复制的请求地址到输入框中。 + +| | | | +| --- | --- | --- | +| ![图片](/imgs/feishu-bot-10.jpg) | ![图片](/imgs/feishu-bot-11.jpg) | ![图片](/imgs/feishu-bot-6.png) | -1. 前往 [开发者平台](https://open.feishu.cn/app?lang=zh-CN) 创建应用 , 并获取到 APPID 和 Secret -2. 前往`应用功能-机器人`, 创建机器人 -3. 从 cpolar、serverless 或 Railway 获得公网地址,在飞书机器人后台的 `事件订阅` 板块填写。例如, - - `http://xxxx.r6.cpolar.top` 为 cpolar 暴露的公网地址 - - `/webhook/event` 为统一的应用路由 - - 最终的回调地址为 `http://xxxx.r6.cpolar.top/webhook/event` -4. 在飞书机器人后台的 `机器人` 板块,填写消息卡片请求网址。例如, - - `http://xxxx.r6.cpolar.top` 为 cpolar 暴露的公网地址 - - `/webhook/card` 为统一的应用路由 - - 最终的消息卡片请求网址为 `http://xxxx.r6.cpolar.top/webhook/card` -5. 在事件订阅板块,搜索三个词`机器人进群`、 `接收消息`、 `消息已读`, 把他们后面所有的权限全部勾选。 进入权限管理界面,搜索`图片`, 勾选`获取与上传图片或文件资源`。 最终会添加下列回调事件 - - im:resource(获取与上传图片或文件资源) - - im:message - - im:message.group_at_msg(获取群组中所有消息) - - im:message.group_at_msg:readonly(接收群聊中 @ 机器人消息事件) - - im:message.p2p_msg(获取用户发给机器人的单聊消息) - - im:message.p2p_msg:readonly(读取用户发给机器人的单聊消息) - - im:message:send_as_bot(获取用户在群组中 @ 机器人的消息) - - im:chat:readonly(获取群组信息) - - im:chat(获取与更新群组信息) +## 5. 配置机器人回调事件和权限 -## 4. 测试飞书机器人 +* 添加 `接收消息` 事件 + +在`事件与回调`页面,点击`添加事件`。 -私聊机器人,或者群里艾特它,就可以基于 FastGPT 的应用进行回答啦 +搜索`接收消息`,或者直接搜索 `im.message.receive_v1` ,找到`接收消息 v2.0`的时间,勾选上并点击`确认添加`。 -![](/imgs/feishu-res.png) \ No newline at end of file +添加事件后,增加两个权限:点击对应权限,会有弹窗提示添加权限,添加上图两个权限。 + +| | | +| --- | --- | +| ![图片](/imgs/feishu-bot-7.png) | ![图片](/imgs/feishu-bot-8.png) | + +不推荐启用上图中的两个“历史版本”,而是使用新版本的权限。 +- 若开启 “读取用户发给机器人的单聊消息”, 则单聊发送给机器人的消息将被送到 FastGPT +- 若开启 “接收群聊中@机器人消息事件”, 则群聊中@机器人的消息将被送到 FastGPT +- 若开启(不推荐开启)“获取群组中所有消息”,则群聊中所有消息都将被送到 FastGPT + +## 6. 配置回复消息权限 + +在飞书控制台,点击左侧的 `权限管理` ,搜索框中输入`发消息`,找到`以应用的身份发消息`的权限,点击开通权限。 + +![](/imgs/feishu-bot-13.jpg) + +## 6. 发布机器人 + +点击飞书控制台左侧的`版本管理与发布`,即可发布机器人。 + +![](/imgs/feishu-bot-12.jpg) + +然后就可以在工作台里找到你的机器人啦。接下来就是把机器人拉进群组,或者单独与它对话。 + +![图片](/imgs/feishu-bot-9.png) diff --git a/packages/global/common/fn/utils.ts b/packages/global/common/fn/utils.ts new file mode 100644 index 000000000000..ae21c208130a --- /dev/null +++ b/packages/global/common/fn/utils.ts @@ -0,0 +1,10 @@ +export const retryRun = (fn: () => T, retry = 2): T => { + try { + return fn(); + } catch (error) { + if (retry > 0) { + return retryRun(fn, retry - 1); + } + throw error; + } +}; diff --git a/packages/global/common/string/time.ts b/packages/global/common/string/time.ts index 407eff459834..97e215ff140f 100644 --- a/packages/global/common/string/time.ts +++ b/packages/global/common/string/time.ts @@ -26,7 +26,7 @@ export const formatTimeToChatTime = (time: Date) => { // 如果时间是今天,展示几时:几分 if (now.isSame(target, 'day')) { - return target.format('HH:mm'); + return target.format('HH : mm'); } // 如果是昨天,展示昨天 diff --git a/packages/global/core/chat/constants.ts b/packages/global/core/chat/constants.ts index 97e5db535bdb..59ff92e852ac 100644 --- a/packages/global/core/chat/constants.ts +++ b/packages/global/core/chat/constants.ts @@ -1,3 +1,5 @@ +import { i18nT } from '../../../web/i18n/utils'; + export enum ChatRoleEnum { System = 'System', Human = 'Human', @@ -30,23 +32,27 @@ export enum ChatSourceEnum { online = 'online', share = 'share', api = 'api', - team = 'team' + team = 'team', + feishu = 'feishu' } export const ChatSourceMap = { [ChatSourceEnum.test]: { - name: 'core.chat.logs.test' + name: i18nT('common:core.chat.logs.test') }, [ChatSourceEnum.online]: { - name: 'core.chat.logs.online' + name: i18nT('common:core.chat.logs.online') }, [ChatSourceEnum.share]: { - name: 'core.chat.logs.share' + name: i18nT('common:core.chat.logs.share') }, [ChatSourceEnum.api]: { - name: 'core.chat.logs.api' + name: i18nT('common:core.chat.logs.api') }, [ChatSourceEnum.team]: { - name: 'core.chat.logs.team' + name: i18nT('common:core.chat.logs.team') + }, + [ChatSourceEnum.feishu]: { + name: i18nT('common:core.chat.logs.feishu') } }; diff --git a/packages/global/core/workflow/template/system/textEditor.ts b/packages/global/core/workflow/template/system/textEditor.ts index fa157e439b95..6c4467cb6545 100644 --- a/packages/global/core/workflow/template/system/textEditor.ts +++ b/packages/global/core/workflow/template/system/textEditor.ts @@ -26,7 +26,7 @@ export const TextEditorNode: FlowNodeTemplateType = { inputs: [ { ...Input_Template_DynamicInput, - description: '可以引用其他节点的输出,作为文本拼接的变量,通过 {{字段名}} 来引用变量', + description: '可以引用其他节点的输出,作为文本拼接的变量,输入 / 唤起变量列表', customInputConfig: { selectValueTypeList: Object.values(WorkflowIOValueTypeEnum), showDescription: false, @@ -39,7 +39,7 @@ export const TextEditorNode: FlowNodeTemplateType = { valueType: WorkflowIOValueTypeEnum.string, required: true, label: '拼接文本', - placeholder: '可通过 {{字段名}} 来引用变量' + placeholder: '可输入 / 唤起变量列表' } ], outputs: [ diff --git a/packages/global/support/outLink/constant.ts b/packages/global/support/outLink/constant.ts index 7562997b9c5b..44224726c900 100644 --- a/packages/global/support/outLink/constant.ts +++ b/packages/global/support/outLink/constant.ts @@ -2,5 +2,6 @@ export enum PublishChannelEnum { share = 'share', iframe = 'iframe', apikey = 'apikey', - feishu = 'feishu' + feishu = 'feishu', + wecom = 'wecom' } diff --git a/packages/global/support/outLink/type.d.ts b/packages/global/support/outLink/type.d.ts index 9e37f44411e3..c947cdf699d6 100644 --- a/packages/global/support/outLink/type.d.ts +++ b/packages/global/support/outLink/type.d.ts @@ -1,8 +1,9 @@ -import { AppSchema } from 'core/app/type'; +import { AppSchema } from '../../core/app/type'; import { PublishChannelEnum } from './constant'; +import { RequireOnlyOne } from '../../common/type/utils'; // Feishu Config interface -export interface FeishuType { +export interface FeishuAppType { appId: string; appSecret: string; // Encrypt config @@ -10,29 +11,28 @@ export interface FeishuType { encryptKey?: string; // no secret if null // Token Verification // refer to: https://open.feishu.cn/document/server-docs/event-subscription-guide/event-subscription-configure-/encrypt-key-encryption-configuration-case - verificationToken: string; + verificationToken?: string; } -// TODO: Unused -export interface WecomType { - ReplyLimit: Boolean; - defaultResponse: string; - immediateResponse: boolean; - WXWORK_TOKEN: string; - WXWORK_AESKEY: string; - WXWORK_SECRET: string; - WXWORD_ID: string; +export interface WecomAppType { + AgentId: string; + CorpId: string; + SuiteSecret: string; + CallbackToken: string; + CallbackEncodingAesKey: string; } -export type OutLinkSchema = { +// TODO: unused +export interface WechatAppType {} + +export type OutlinkAppType = FeishuAppType | WecomAppType | undefined; + +export type OutLinkSchema = { _id: string; shareId: string; teamId: string; tmbId: string; appId: string; - // teamId: Schema.Types.ObjectId; - // tmbId: Schema.Types.ObjectId; - // appId: Schema.Types.ObjectId; name: string; usagePoints: number; lastTime: Date; @@ -55,7 +55,7 @@ export type OutLinkSchema = { hookUrl?: string; }; - app?: T; + app: T; }; // to handle MongoDB querying @@ -64,10 +64,10 @@ export type OutLinkWithAppType = Omit & { }; // Edit the Outlink -export type OutLinkEditType = { +export type OutLinkEditType = { _id?: string; name: string; - responseDetail: OutLinkSchema['responseDetail']; + responseDetail?: OutLinkSchema['responseDetail']; // response when request immediateResponse?: string; // response when error or other situation diff --git a/packages/global/support/tmpData/constant.ts b/packages/global/support/tmpData/constant.ts new file mode 100644 index 000000000000..f548dcc3bb9a --- /dev/null +++ b/packages/global/support/tmpData/constant.ts @@ -0,0 +1,31 @@ +export enum TmpDataEnum { + FeishuAccessToken = 'feishu_access_token', + WecomAccessToken = 'wecom_access_token' +} + +type _TmpDataMetadata = { + [TmpDataEnum.FeishuAccessToken]: { + FeishuAppId: string; + }; + [TmpDataEnum.WecomAccessToken]: { + CorpId: string; + AgentId: string; + }; +}; + +type _TmpDataType = { + [TmpDataEnum.FeishuAccessToken]: { + accessToken: string; + }; + [TmpDataEnum.WecomAccessToken]: { + accessToken: string; + }; +}; + +export const TmpDataExpireTime = { + [TmpDataEnum.FeishuAccessToken]: 1000 * 60 * 60 * 1.5, // 1.5 hours + [TmpDataEnum.WecomAccessToken]: 1000 * 60 * 60 * 2 // 2 hours +}; + +export type TmpDataMetadata = _TmpDataMetadata[T]; +export type TmpDataType = _TmpDataType[T]; diff --git a/packages/global/support/tmpData/type.d.ts b/packages/global/support/tmpData/type.d.ts new file mode 100644 index 000000000000..8a10c8dbc142 --- /dev/null +++ b/packages/global/support/tmpData/type.d.ts @@ -0,0 +1,5 @@ +export type TmpDataSchema = { + dataId: string; + data: T; + expireAt: Date; +}; diff --git a/packages/global/support/wallet/usage/api.d.ts b/packages/global/support/wallet/usage/api.d.ts index 2a9e71d5e619..9c6825b22a6a 100644 --- a/packages/global/support/wallet/usage/api.d.ts +++ b/packages/global/support/wallet/usage/api.d.ts @@ -21,6 +21,6 @@ export type CreateUsageProps = { appId?: string; pluginId?: string; totalPoints: number; - source: UsageSourceEnum; + source: `${UsageSourceEnum}`; list: UsageListItemType[]; }; diff --git a/packages/global/support/wallet/usage/constants.ts b/packages/global/support/wallet/usage/constants.ts index 9e7a1b92a578..f678d47501e0 100644 --- a/packages/global/support/wallet/usage/constants.ts +++ b/packages/global/support/wallet/usage/constants.ts @@ -5,7 +5,8 @@ export enum UsageSourceEnum { api = 'api', shareLink = 'shareLink', training = 'training', - cronJob = 'cronJob' + cronJob = 'cronJob', + feishu = 'feishu' } export const UsageSourceMap = { @@ -23,5 +24,8 @@ export const UsageSourceMap = { }, [UsageSourceEnum.cronJob]: { label: i18nT('common:cron_job_run_app') + }, + [UsageSourceEnum.feishu]: { + label: i18nT('user:usage.feishu') } }; diff --git a/packages/plugins/register.ts b/packages/plugins/register.ts index 648b23221092..91ba768eaeab 100644 --- a/packages/plugins/register.ts +++ b/packages/plugins/register.ts @@ -1,7 +1,5 @@ import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants'; import { SystemPluginResponseType } from './type'; -import { FastGPTProUrl, isProduction } from '../service/common/system/constants'; -import { GET, POST } from '@fastgpt/service/common/api/plusRequest'; import { SystemPluginTemplateItemType } from '@fastgpt/global/core/workflow/type'; import { cloneDeep } from 'lodash'; import { WorkerNameEnum, runWorker } from '@fastgpt/service/worker/utils'; @@ -27,7 +25,7 @@ const packagePluginList = [ 'duckduckgo/searchVideo' ]; -const list = [...staticPluginList, ...packagePluginList]; +export const list = [...staticPluginList, ...packagePluginList]; /* Get plugins */ export const getCommunityPlugins = () => { @@ -49,25 +47,10 @@ export const getCommunityPlugins = () => { }; }); }; -const getCommercialPlugins = () => { - return GET('/core/app/plugin/getSystemPlugins'); -}; -export const getSystemPluginTemplates = async (refresh = false) => { - if (isProduction && global.systemPlugins && !refresh) return cloneDeep(global.systemPlugins); - - try { - if (!global.systemPlugins) { - global.systemPlugins = []; - } - global.systemPlugins = FastGPTProUrl ? await getCommercialPlugins() : getCommunityPlugins(); - - return cloneDeep(global.systemPlugins); - } catch (error) { - //@ts-ignore - global.systemPlugins = undefined; - return Promise.reject(error); - } +export const getSystemPluginTemplates = () => { + const oldPlugins = global.communityPlugins ?? []; + return [...oldPlugins, ...cloneDeep(global.systemPlugins)]; }; export const getCommunityCb = async () => { @@ -107,39 +90,7 @@ export const getCommunityCb = async () => { {} ); }; -const getCommercialCb = async () => { - const plugins = await getSystemPluginTemplates(); - const result = plugins.map((plugin) => { - const name = plugin.id.split('-')[1]; - - return { - name, - cb: (e: any) => - POST>('/core/app/plugin/run', { - pluginName: name, - data: e - }) - }; - }); - return result.reduce 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); - } + return global.systemPluginCb; }; diff --git a/packages/plugins/src/feishu/template.json b/packages/plugins/src/feishu/template.json index 25ce0daa6220..8a01a3d5cdc1 100644 --- a/packages/plugins/src/feishu/template.json +++ b/packages/plugins/src/feishu/template.json @@ -2,7 +2,7 @@ "author": "", "version": "488", "name": "飞书机器人 webhook", - "avatar": "/imgs/app/templates/feishu.svg", + "avatar": "/appMarketTemplates/plugin-feishu/avatar.svg", "intro": "向飞书机器人发起 webhook 请求。", "inputExplanationUrl": "https://open.feishu.cn/document/client-docs/bot-v3/add-custom-bot#f62e72d5", "showStatus": false, diff --git a/packages/service/common/api/plusRequest.ts b/packages/service/common/api/plusRequest.ts index 2f2596b8c04d..de4f52f674d5 100644 --- a/packages/service/common/api/plusRequest.ts +++ b/packages/service/common/api/plusRequest.ts @@ -1,4 +1,9 @@ -import axios, { Method, InternalAxiosRequestConfig, AxiosResponse } from 'axios'; +import axios, { + Method, + InternalAxiosRequestConfig, + AxiosResponse, + AxiosRequestConfig +} from 'axios'; import { FastGPTProUrl } from '../system/constants'; interface ConfigType { @@ -118,3 +123,9 @@ export function PUT(url: string, data = {}, config: ConfigType = export function DELETE(url: string, data = {}, config: ConfigType = {}): Promise { return request(url, data, config, 'DELETE'); } + +export const plusRequest = (config: AxiosRequestConfig) => + instance.request({ + ...config, + baseURL: FastGPTProUrl + }); diff --git a/packages/service/core/app/plugin/controller.ts b/packages/service/core/app/plugin/controller.ts index 2b877c62d8fe..f386addfbae3 100644 --- a/packages/service/core/app/plugin/controller.ts +++ b/packages/service/core/app/plugin/controller.ts @@ -61,9 +61,7 @@ const getPluginTemplateById = async ( currentCost: 0 }; } else { - const item = [...global.communityPlugins, ...(await getSystemPluginTemplates())].find( - (plugin) => plugin.id === pluginId - ); + const item = getSystemPluginTemplates().find((plugin) => plugin.id === pluginId); if (!item) return Promise.reject('plugin not found'); return cloneDeep(item); diff --git a/projects/app/src/service/utils/chat/saveChat.ts b/packages/service/core/chat/saveChat.ts similarity index 87% rename from projects/app/src/service/utils/chat/saveChat.ts rename to packages/service/core/chat/saveChat.ts index 6d7b77151329..4e7d34d21d7a 100644 --- a/projects/app/src/service/utils/chat/saveChat.ts +++ b/packages/service/core/chat/saveChat.ts @@ -1,10 +1,10 @@ import type { AIChatItemType, UserChatItemType } from '@fastgpt/global/core/chat/type.d'; -import { MongoApp } from '@fastgpt/service/core/app/schema'; +import { MongoApp } from '../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 { MongoChatItem } from './chatItemSchema'; +import { MongoChat } from './chatSchema'; +import { addLog } from '../../common/system/log'; +import { mongoSessionRun } from '../../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'; diff --git a/packages/service/core/workflow/dispatch/code/run.ts b/packages/service/core/workflow/dispatch/code/run.ts index d961ff6249ef..154941060d12 100644 --- a/packages/service/core/workflow/dispatch/code/run.ts +++ b/packages/service/core/workflow/dispatch/code/run.ts @@ -21,6 +21,12 @@ export const dispatchRunCode = async (props: RunCodeType): Promise({ + shareId +}: { + shareId?: string; +}) { if (!shareId) { return Promise.reject(OutLinkErrEnum.linkUnInvalid); } - const shareChat = await MongoOutLink.findOne({ shareId }); + const shareChat = (await MongoOutLink.findOne({ shareId }).lean()) as OutLinkSchema; if (!shareChat) { return Promise.reject(OutLinkErrEnum.linkUnInvalid); diff --git a/packages/service/support/tmpData/controller.ts b/packages/service/support/tmpData/controller.ts new file mode 100644 index 000000000000..09558837dafd --- /dev/null +++ b/packages/service/support/tmpData/controller.ts @@ -0,0 +1,49 @@ +import { + TmpDataEnum, + TmpDataExpireTime, + TmpDataMetadata, + TmpDataType +} from '@fastgpt/global/support/tmpData/constant'; +import { MongoTmpData } from './schema'; +import { TmpDataSchema } from '@fastgpt/global/support/tmpData/type'; +import { addMilliseconds } from 'date-fns'; + +function getDataId(type: T, metadata: TmpDataMetadata) { + return `${type}--${Object.values(metadata).join('--')}`; +} + +export async function getTmpData({ + type, + metadata +}: { + type: T; + metadata: TmpDataMetadata; +}) { + return (await MongoTmpData.findOne({ + dataId: getDataId(type, metadata) + }).lean()) as TmpDataSchema> | null; +} + +export async function setTmpData({ + type, + metadata, + data +}: { + type: T; + metadata: TmpDataMetadata; + data: TmpDataType; +}) { + return await MongoTmpData.updateOne( + { + dataId: getDataId(type, metadata) + }, + { + dataId: getDataId(type, metadata), + data, + expireAt: addMilliseconds(Date.now(), TmpDataExpireTime[type]) + }, + { + upsert: true + } + ); +} diff --git a/packages/service/support/tmpData/schema.ts b/packages/service/support/tmpData/schema.ts new file mode 100644 index 000000000000..6a33f6086965 --- /dev/null +++ b/packages/service/support/tmpData/schema.ts @@ -0,0 +1,28 @@ +import { getMongoModel, Schema } from '../../common/mongo'; +import type { TmpDataSchema as SchemaType } from '@fastgpt/global/support/tmpData/type'; + +const collectionName = 'tmp_datas'; + +const TmpDataSchema = new Schema({ + dataId: { + type: String, + required: true, + unique: true + }, + data: { + type: Object + }, + expireAt: { + type: Date, + required: true + } +}); + +try { + TmpDataSchema.index({ dataId: -1 }); + TmpDataSchema.index({ expireAt: -1 }, { expireAfterSeconds: 5 }); +} catch (error) { + console.log(error); +} + +export const MongoTmpData = getMongoModel>(collectionName, TmpDataSchema); diff --git a/packages/web/components/common/Icon/constants.ts b/packages/web/components/common/Icon/constants.ts index 2cbb4b079a60..ab8a5bbdca51 100644 --- a/packages/web/components/common/Icon/constants.ts +++ b/packages/web/components/common/Icon/constants.ts @@ -76,6 +76,7 @@ export const iconPaths = { 'core/app/logsLight': () => import('./icons/core/app/logsLight.svg'), 'core/app/markLight': () => import('./icons/core/app/markLight.svg'), 'core/app/publish/lark': () => import('./icons/core/app/publish/lark.svg'), + 'core/app/publish/wecom': () => import('./icons/core/app/publish/wecom.svg'), 'core/app/questionGuide': () => import('./icons/core/app/questionGuide.svg'), 'core/app/schedulePlan': () => import('./icons/core/app/schedulePlan.svg'), 'core/app/simpleBot': () => import('./icons/core/app/simpleBot.svg'), diff --git a/packages/web/components/common/Icon/icons/core/app/publish/wecom.svg b/packages/web/components/common/Icon/icons/core/app/publish/wecom.svg new file mode 100644 index 000000000000..9be71fbe3003 --- /dev/null +++ b/packages/web/components/common/Icon/icons/core/app/publish/wecom.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/packages/web/components/common/Icon/icons/support/outlink/apikeyFill.svg b/packages/web/components/common/Icon/icons/support/outlink/apikeyFill.svg index b1fe9b169264..fd0be169d391 100644 --- a/packages/web/components/common/Icon/icons/support/outlink/apikeyFill.svg +++ b/packages/web/components/common/Icon/icons/support/outlink/apikeyFill.svg @@ -4,5 +4,5 @@ xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"> - \ No newline at end of file + p-id="4983"> + diff --git a/packages/web/components/common/MyModal/index.tsx b/packages/web/components/common/MyModal/index.tsx index 120e4eeae5aa..35625f7e05f1 100644 --- a/packages/web/components/common/MyModal/index.tsx +++ b/packages/web/components/common/MyModal/index.tsx @@ -6,8 +6,7 @@ import { ModalHeader, ModalCloseButton, ModalContentProps, - Box, - Image + Box } from '@chakra-ui/react'; import MyBox from '../MyBox'; import { useSystem } from '../../../hooks/useSystem'; @@ -18,13 +17,13 @@ export interface MyModalProps extends ModalContentProps { title?: any; isCentered?: boolean; isLoading?: boolean; - isOpen: boolean; + isOpen?: boolean; onClose?: () => void; closeOnOverlayClick?: boolean; } const MyModal = ({ - isOpen, + isOpen = true, onClose, iconSrc, title, diff --git a/packages/web/i18n/en/common.json b/packages/web/i18n/en/common.json index 5a237556ee6b..7bf2b0eee2e3 100644 --- a/packages/web/i18n/en/common.json +++ b/packages/web/i18n/en/common.json @@ -484,8 +484,10 @@ }, "logs": { "api": "API call", + "feishu": "Lark", "online": "Online use", "share": "External link call", + "team": "Team chat", "test": "Test" }, "markdown": { diff --git a/packages/web/i18n/en/user.json b/packages/web/i18n/en/user.json index 408bc0814a87..71691e21481e 100644 --- a/packages/web/i18n/en/user.json +++ b/packages/web/i18n/en/user.json @@ -72,5 +72,8 @@ "add_collaborator": "Add collaborators", "manage_collaborators": "Manage collaborators", "no_collaborators": "No collaborators yet" + }, + "usage": { + "feishu": "Lark" } } diff --git a/packages/web/i18n/zh/common.json b/packages/web/i18n/zh/common.json index 7f410bca7e9f..3acb0cd8a377 100644 --- a/packages/web/i18n/zh/common.json +++ b/packages/web/i18n/zh/common.json @@ -494,8 +494,10 @@ }, "logs": { "api": "API 调用", + "feishu": "飞书", "online": "在线使用", "share": "外部链接调用", + "team": "团队空间对话", "test": "测试" }, "markdown": { diff --git a/packages/web/i18n/zh/publish.json b/packages/web/i18n/zh/publish.json index 99bcae425365..d24a34530189 100644 --- a/packages/web/i18n/zh/publish.json +++ b/packages/web/i18n/zh/publish.json @@ -1,10 +1,13 @@ { + "publish_name": "名称", "create_api_key": "创建新 key", "create_link": "创建链接", "default_response": "默认回复", "edit_api_key": "编辑 key 信息", "edit_link": "编辑", "feishu_name": "飞书", + "new_feishu_bot": "新增飞书机器人", + "edit_feishu_bot": "编辑飞书机器人", "link_name": "分享链接的名字", "qpm_tips": "每个 IP 每分钟最多提问多少次", "qpm_is_empty": "QPM 不能为空", @@ -13,5 +16,20 @@ "key_tips": "你可以使用 API 秘钥访问一些特定的接口(无法访问应用,访问应用需使用应用内的 API key)", "token_auth": "身份验证", "token_auth_tips": "身份校验服务器地址,如填写该值,每次对话前都会向指定服务器发送一个请求,进行身份校验", - "token_auth_use_cases": "查看身份验证使用说明" -} \ No newline at end of file + "token_auth_use_cases": "查看身份验证使用说明", + "show_share_link_modal_title": "开始使用", + "request_address": "请求地址", + "basic_info": "基本信息", + "feishu_api": "飞书接口", + "feishu_bot": "飞书机器人", + "feishu_bot_desc": "通过 API 直接接入飞书机器人", + "copy_link_hint": "将下面链接复制到指定位置", + "wecom": { + "title": "发布到企业微信机器人", + "bot": "企业微信机器人", + "bot_desc": "通过 API 直接接入企业微信机器人", + "edit_modal_title": "编辑企微机器人", + "create_modal_title": "创建企微机器人", + "api": "企微 API" + } +} diff --git a/packages/web/i18n/zh/user.json b/packages/web/i18n/zh/user.json index 259457412f98..9badf918c8db 100644 --- a/packages/web/i18n/zh/user.json +++ b/packages/web/i18n/zh/user.json @@ -1,10 +1,47 @@ { "bind_inform_account_error": "绑定通知账号异常", "bind_inform_account_success": "绑定通知账号成功", + "delete": { + "admin_failed": "删除管理员失败", + "admin_success": "删除管理员成功" + }, + "has_chosen": "已选择", + "login": { + "error": "登录异常", + "failed": "登录失败", + "login_account": "登录 {{account}} 账号", + "login_error": "用户名或密码错误", + "password_condition": "密码最多 60 位", + "success": "登录成功", + "to_register": "没有账号,去注册" + }, + "name": "名称", "notification": { "Bind Notification Pipe Hint": "请绑定通知接收账号,以确保您能正常接收套餐过期提醒等通知,保障您的服务正常运行。", "remind_owner_bind": "请提醒创建者绑定通知账号" }, + "operations": "操作", + "password": { + "change_error": "修改密码异常", + "code_required": "验证码不能为空", + "code_send_error": "验证码发送异常", + "code_sended": "验证码已发送", + "confirm": "确认密码", + "email_phone": "邮箱/手机号", + "email_phone_error": "邮箱/手机号格式错误", + "email_phone_void": "邮箱/手机号不能为空", + "get_code": "获取验证码", + "get_code_again": "s后重新获取", + "new_password": "新密码(4~20位)", + "not_match": "两次密码不一致", + "password_condition": "密码最少 4 位最多 20 位", + "password_required": "密码不能为空", + "retrieve": "找回密码", + "retrieved": "密码已找回", + "retrieved_account": "找回 {{account}} 账号", + "to_login": "去登录", + "verification_code": "验证码" + }, "permission": { "Manage": "管理员", "Manage tip": "团队管理员,拥有全部权限", @@ -16,62 +53,28 @@ "team_read": "团队可访问", "team_write": "团队可编辑" }, - "team": { - "Add manager": "添加管理员", - "add_collaborator": "添加协作者", - "manage_collaborators": "管理协作者", - "no_collaborators": "暂无协作者" - }, - "search_user": "搜索用户名", - "has_chosen": "已选择", - "name": "名称", "permissions": "权限", - "operations": "操作", - "delete": { - "admin_success": "删除管理员成功", - "admin_failed": "删除管理员失败" - }, - "synchronization": { - "title": "填写标签同步链接,点击同步按钮即可同步", - "placeholder": "请输入同步标签", - "button": "立即同步" - }, - "password": { - "retrieve": "找回密码", - "retrieved": "密码已找回", - "change_error": "修改密码异常", - "retrieved_account": "找回 {{account}} 账号", - "email_phone": "邮箱/手机号", - "email_phone_void": "邮箱/手机号不能为空", - "email_phone_error": "邮箱/手机号格式错误", - "code_required": "验证码不能为空", - "new_password": "新密码(4~20位)", - "password_required": "密码不能为空", - "password_condition": "密码最少 4 位最多 20 位", - "verification_code": "验证码", - "confirm": "确认密码", - "not_match": "两次密码不一致", - "to_login": "去登录", - "get_code": "获取验证码", - "get_code_again": "s后重新获取", - "code_sended": "验证码已发送", - "code_send_error": "验证码发送异常" - }, "register": { - "success": "注册成功", - "failed": "注册失败", + "confirm": "确认注册", "error": "注册异常", + "failed": "注册失败", "register_account": "注册 {{account}} 账号", - "confirm": "确认注册", + "success": "注册成功", "to_login": "已有账号,去登录" }, - "login": { - "success": "登录成功", - "failed": "登录失败", - "error": "登录异常", - "login_account": "登录 {{account}} 账号", - "login_error": "用户名或密码错误", - "to_register": "没有账号,去注册", - "password_condition": "密码最多 60 位" + "search_user": "搜索用户名", + "synchronization": { + "button": "立即同步", + "placeholder": "请输入同步标签", + "title": "填写标签同步链接,点击同步按钮即可同步" + }, + "team": { + "Add manager": "添加管理员", + "add_collaborator": "添加协作者", + "manage_collaborators": "管理协作者", + "no_collaborators": "暂无协作者" + }, + "usage": { + "feishu": "飞书" } } diff --git a/packages/web/i18n/zh/workflow.json b/packages/web/i18n/zh/workflow.json index 18f1facc22c6..fc91bc748d58 100644 --- a/packages/web/i18n/zh/workflow.json +++ b/packages/web/i18n/zh/workflow.json @@ -20,7 +20,7 @@ "input_description": "字段描述", "only_the_reference_type_is_supported": "仅支持引用类型", "optional_value_type": "可选的数据类型", - "optional_value_type_tip": "可以指定 1 个或多个数据类型,用户在冬天添加字段时,仅可选择配置的类型", + "optional_value_type_tip": "可以指定 1 个或多个数据类型,用户在动态添加字段时,仅可选择配置的类型", "response": { "Code log": "Log 日志", "Custom inputs": "自定义输入", diff --git a/packages/web/types/i18next.d.ts b/packages/web/types/i18next.d.ts index 8b57cbc58498..3d2b7b68e55b 100644 --- a/packages/web/types/i18next.d.ts +++ b/packages/web/types/i18next.d.ts @@ -7,6 +7,7 @@ import publish from '../i18n/zh/publish.json'; import workflow from '../i18n/zh/workflow.json'; import user from '../i18n/zh/user.json'; import chat from '../i18n/zh/chat.json'; + export interface I18nNamespaces { common: typeof common; dataset: typeof dataset; diff --git a/projects/app/public/imgs/outlink/feishu-copylink-instruction.png b/projects/app/public/imgs/outlink/feishu-copylink-instruction.png new file mode 100644 index 000000000000..025c7d148cc0 Binary files /dev/null and b/projects/app/public/imgs/outlink/feishu-copylink-instruction.png differ diff --git a/projects/app/public/imgs/outlink/wecom-copylink-instruction.png b/projects/app/public/imgs/outlink/wecom-copylink-instruction.png new file mode 100644 index 000000000000..88f44df5d809 Binary files /dev/null and b/projects/app/public/imgs/outlink/wecom-copylink-instruction.png differ diff --git a/projects/app/src/components/common/MyRadio/index.tsx b/projects/app/src/components/common/MyRadio/index.tsx index 0100e77336b8..9cf8a6d01882 100644 --- a/projects/app/src/components/common/MyRadio/index.tsx +++ b/projects/app/src/components/common/MyRadio/index.tsx @@ -75,9 +75,9 @@ const MyRadio = ({ {!!item.icon && ( <> {item.icon.startsWith('/') ? ( - {''} + {''} ) : ( - + )} )} diff --git a/projects/app/src/components/support/apikey/Table.tsx b/projects/app/src/components/support/apikey/Table.tsx index ccc0f98b77ae..2f1459ab17a9 100644 --- a/projects/app/src/components/support/apikey/Table.tsx +++ b/projects/app/src/components/support/apikey/Table.tsx @@ -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 ( + return getSystemPlugins().then((res) => res // Just show the active plugins .filter((item) => item.isActive) diff --git a/projects/app/src/pages/api/core/app/plugin/path.ts b/projects/app/src/pages/api/core/app/plugin/path.ts index 77217d1ed270..4d0abf8f8e2a 100644 --- a/projects/app/src/pages/api/core/app/plugin/path.ts +++ b/projects/app/src/pages/api/core/app/plugin/path.ts @@ -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 []; diff --git a/projects/app/src/pages/api/support/outLink/feishu/[token].ts b/projects/app/src/pages/api/support/outLink/feishu/[token].ts new file mode 100644 index 000000000000..9fbdefdb0e6e --- /dev/null +++ b/projects/app/src/pages/api/support/outLink/feishu/[token].ts @@ -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, + res: ApiResponseType +): Promise { + // send to pro + const { token } = req.query; + const result = await POST(`support/outLink/feishu/${token}`, req.body, { + headers: req.headers as any + }); + res.json(result); +} + +export default handler; diff --git a/projects/app/src/pages/api/support/outLink/update.ts b/projects/app/src/pages/api/support/outLink/update.ts index d535615c0c99..6ddd51f6f4a3 100644 --- a/projects/app/src/pages/api/support/outLink/update.ts +++ b/projects/app/src/pages/api/support/outLink/update.ts @@ -13,7 +13,7 @@ export type OutLinkUpdateResponse = {}; async function handler( req: ApiRequestProps ): Promise { - 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 {}; } diff --git a/projects/app/src/pages/api/support/outLink/wecom/[token].ts b/projects/app/src/pages/api/support/outLink/wecom/[token].ts new file mode 100644 index 000000000000..fd2b6e89a15d --- /dev/null +++ b/projects/app/src/pages/api/support/outLink/wecom/[token].ts @@ -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, + res: ApiResponseType +): Promise { + 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; diff --git a/projects/app/src/pages/api/v1/chat/completions.ts b/projects/app/src/pages/api/v1/chat/completions.ts index 584566719c88..cdbfd00afc6e 100644 --- a/projects/app/src/pages/api/v1/chat/completions.ts +++ b/projects/app/src/pages/api/v1/chat/completions.ts @@ -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'; diff --git a/projects/app/src/pages/app/detail/components/Logs/index.tsx b/projects/app/src/pages/app/detail/components/Logs/index.tsx index 64f6293886ac..97fd2358b85a 100644 --- a/projects/app/src/pages/app/detail/components/Logs/index.tsx +++ b/projects/app/src/pages/app/detail/components/Logs/index.tsx @@ -71,35 +71,33 @@ const Logs = () => { const [detailLogsId, setDetailLogsId] = useState(); return ( - <> - - {isPc && ( - <> - - {appT('chat_logs')} + + {isPc && ( + + + {appT('chat_logs')} + + + {appT('chat_logs_tips')},{' '} + + {t('common:core.chat.Read Mark Description')} - - {appT('chat_logs_tips')},{' '} - - {t('common:core.chat.Read Mark Description')} - - - - )} - + + + )} {/* table */} { > {t('common:core.chat.Mark Description')} - + ); }; diff --git a/projects/app/src/pages/app/detail/components/Publish/FeiShu/FeiShuEditModal.tsx b/projects/app/src/pages/app/detail/components/Publish/FeiShu/FeiShuEditModal.tsx index 8f7f9d467c2a..cb94018ac0d5 100644 --- a/projects/app/src/pages/app/detail/components/Publish/FeiShu/FeiShuEditModal.tsx +++ b/projects/app/src/pages/app/detail/components/Publish/FeiShu/FeiShuEditModal.tsx @@ -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; + defaultData: OutLinkEditType; 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) => { + 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) => { - 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 ( - - - {t('common:Name')} - - - - - QPM - + + + + + + + {t('publish:feishu_api')} + {feConfigs?.docUrl && ( + + + + {t('common:common.Read document')} + + + )} - - - - - {t('common:support.outlink.Max usage points')} - + + + App ID + + - - - - - {t('common:common.Expired Time')} + + + App Secret + + - { - setValue('limit.expiredTime', new Date(e.target.value)); - }} - /> - - - - {t('common:default_reply')} + + Encrypt Key + - - - - - {t('common:reply_now')} + + + + + + - - - - {t('common:core.module.http.AppId')} - - - - {t('common:core.module.http.AppSecret' as any)} - - - - Encrypt Key - - - - Verification Token - - {/* */} - {/* */} - {/* 限制回复 */} - {/* */} - {/* */} - {/* */} - - - - ); }; diff --git a/projects/app/src/pages/app/detail/components/Publish/FeiShu/index.tsx b/projects/app/src/pages/app/detail/components/Publish/FeiShu/index.tsx index 39c64e980a29..018e4c343845 100644 --- a/projects/app/src/pages/app/detail/components/Publish/FeiShu/index.tsx +++ b/projects/app/src/pages/app/detail/components/Publish/FeiShu/index.tsx @@ -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>(); - const { toast } = useToast(); + const [editFeiShuLinkData, setEditFeiShuLinkData] = useState>(); + const [isEdit, setIsEdit] = useState(false); + + const baseUrl = useMemo( + () => feConfigs?.customApiDomain || `${location.origin}/api`, + [feConfigs?.customApiDomain] + ); + const { - isFetching, data: shareChatList = [], - refetch: refetchShareChatList - } = useQuery(['initShareChatList', appId], () => - getShareChatList({ appId, type: PublishChannelEnum.feishu }) + loading: isFetching, + runAsync: refetchShareChatList + } = useRequest2( + () => getShareChatList({ appId, type: PublishChannelEnum.feishu }), + { + manual: false + } ); + const { + onOpen: openShowShareLinkModal, + isOpen: showShareLinkModalOpen, + onClose: closeShowShareLinkModal + } = useDisclosure(); + + const [showShareLink, setShowShareLink] = useState(null); + return ( - + {t('common:core.app.publish.Fei shu bot publish')} @@ -112,11 +133,22 @@ const FeiShu = ({ appId }: { appId: string }) => { : t('common:common.Un used')} + { { 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 && ( { - 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 && ( )} + {showShareLinkModalOpen && ( + + )} ); }; diff --git a/projects/app/src/pages/app/detail/components/Publish/Wecom/WecomEditModal.tsx b/projects/app/src/pages/app/detail/components/Publish/Wecom/WecomEditModal.tsx new file mode 100644 index 000000000000..478c201a2bdb --- /dev/null +++ b/projects/app/src/pages/app/detail/components/Publish/Wecom/WecomEditModal.tsx @@ -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; + 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 ( + + + + + + + + {t('publish:wecom.api')} + {feConfigs?.docUrl && ( + + + + {t('common:common.Read document')} + + + )} + + + + Corp ID + + + + + + Agent ID + + + + + + Secret + + + + + + Token + + + + + + AES Key + + + + + + + + + + + + + + ); +}; + +export default WecomEditModal; diff --git a/projects/app/src/pages/app/detail/components/Publish/Wecom/index.tsx b/projects/app/src/pages/app/detail/components/Publish/Wecom/index.tsx new file mode 100644 index 000000000000..9a69cbed1ea4 --- /dev/null +++ b/projects/app/src/pages/app/detail/components/Publish/Wecom/index.tsx @@ -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>(); + const [isEdit, setIsEdit] = useState(false); + + const baseUrl = useMemo( + () => feConfigs?.customApiDomain || `${location.origin}/api`, + [feConfigs?.customApiDomain] + ); + + const { + data: shareChatList = [], + loading: isFetching, + runAsync: refetchShareChatList + } = useRequest2(() => getShareChatList({ appId, type: PublishChannelEnum.wecom }), { + manual: false + }); + + const { + onOpen: openShowShareLinkModal, + isOpen: showShareLinkModalOpen, + onClose: closeShowShareLinkModal + } = useDisclosure(); + + const [showShareLink, setShowShareLink] = useState(null); + + return ( + + + + {t('publish:wecom.title')} + + + + + + + + + + {feConfigs?.isPlus && ( + <> + + + + )} + + + + + + {shareChatList.map((item) => ( + + + + {feConfigs?.isPlus && ( + <> + + + + )} + + + + ))} + +
{t('common:common.Name')} {t('common:support.outlink.Usage points')} {t('common:core.app.share.Ip limit title')} {t('common:common.Expired Time')} {t('common:common.Last use time')}
{item.name} + {Math.round(item.usagePoints)} + {feConfigs?.isPlus + ? `${ + item.limit?.maxUsagePoints && item.limit.maxUsagePoints > -1 + ? ` / ${item.limit.maxUsagePoints}` + : ` / ${t('common:common.Unlimited')}` + }` + : ''} + {item?.limit?.QPM || '-'} + {item?.limit?.expiredTime + ? dayjs(item.limit?.expiredTime).format('YYYY/MM/DD\nHH:mm') + : '-'} + + {item.lastTime + ? t(formatTimeToChatTime(item.lastTime) as any) + : t('common:common.Un used')} + + + + } + 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); + } + } + ] + } + ]} + /> +
+
+ {editWecomData && ( + Promise.all([refetchShareChatList(), setEditWecomData(undefined)])} + onEdit={() => Promise.all([refetchShareChatList(), setEditWecomData(undefined)])} + onClose={() => setEditWecomData(undefined)} + isEdit={isEdit} + /> + )} + {shareChatList.length === 0 && !isFetching && ( + + )} + + {showShareLinkModalOpen && ( + + )} +
+ ); +}; + +export default React.memo(Wecom); diff --git a/projects/app/src/pages/app/detail/components/Publish/components/BasicInfo.tsx b/projects/app/src/pages/app/detail/components/Publish/components/BasicInfo.tsx new file mode 100644 index 000000000000..ce0b387c305b --- /dev/null +++ b/projects/app/src/pages/app/detail/components/Publish/components/BasicInfo.tsx @@ -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>; + setValue: UseFormSetValue>; + defaultData: OutLinkEditType; +}) { + const { t } = useTranslation(); + return ( + + {t('publish:basic_info')} + + + {t('common:Name')} + + + + + + QPM + + + + + + + {t('common:support.outlink.Max usage points')} + + + + + + + {t('common:common.Expired Time')} + + { + setValue('limit.expiredTime', new Date(e.target.value)); + }} + /> + + + ); +} + +export default BasicInfo; diff --git a/projects/app/src/pages/app/detail/components/Publish/components/showShareLinkModal.tsx b/projects/app/src/pages/app/detail/components/Publish/components/showShareLinkModal.tsx new file mode 100644 index 000000000000..5acee34f8e6f --- /dev/null +++ b/projects/app/src/pages/app/detail/components/Publish/components/showShareLinkModal.tsx @@ -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 ( + + + + + {t('publish:copy_link_hint')} + copyData(shareLink)} + /> + + + {shareLink} + + + + + + + + ); +} + +export default ShowShareLinkModal; diff --git a/projects/app/src/pages/app/detail/components/Publish/index.tsx b/projects/app/src/pages/app/detail/components/Publish/index.tsx index 600f95390f69..5f846cdf98fd 100644 --- a/projects/app/src/pages/app/detail/components/Publish/index.tsx +++ b/projects/app/src/pages/app/detail/components/Publish/index.tsx @@ -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.share); return ( - <> + 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); + } + }} /> @@ -63,15 +99,16 @@ const OutLink = () => { mt={4} px={[4, 8]} py={[4, 6]} - flex={'1 0 0'} + flex={1} > {linkType === PublishChannelEnum.share && ( )} {linkType === PublishChannelEnum.apikey && } {linkType === PublishChannelEnum.feishu && } + {linkType === PublishChannelEnum.wecom && } - + ); }; diff --git a/projects/app/src/pages/app/detail/components/SimpleApp/index.tsx b/projects/app/src/pages/app/detail/components/SimpleApp/index.tsx index 451d48c3b77f..ad6ba12b8e7d 100644 --- a/projects/app/src/pages/app/detail/components/SimpleApp/index.tsx +++ b/projects/app/src/pages/app/detail/components/SimpleApp/index.tsx @@ -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 ? ( ) : ( - + {currentTab === TabEnum.publish && } {currentTab === TabEnum.logs && } - +
)} ); diff --git a/projects/app/src/service/common/system/volumnMongoWatch.ts b/projects/app/src/service/common/system/volumnMongoWatch.ts index c2b7133145f1..04ce1d034a2f 100644 --- a/projects/app/src/service/common/system/volumnMongoWatch.ts +++ b/projects/app/src/service/common/system/volumnMongoWatch.ts @@ -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) {} }); }; diff --git a/projects/app/src/service/core/app/plugin.ts b/projects/app/src/service/core/app/plugin.ts new file mode 100644 index 000000000000..b6d5d75ac877 --- /dev/null +++ b/projects/app/src/service/core/app/plugin.ts @@ -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('/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>('/core/app/plugin/run', { + pluginName: name, + data: e + }) + }; + }); + + return result.reduce 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); + } +}; diff --git a/projects/app/src/service/mongo.ts b/projects/app/src/service/mongo.ts index 4ba33369b735..b0e13a4f01f3 100644 --- a/projects/app/src/service/mongo.ts +++ b/projects/app/src/service/mongo.ts @@ -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 diff --git a/projects/app/src/web/core/app/constants.ts b/projects/app/src/web/core/app/constants.ts index 22423b8392d8..8b016aafff05 100644 --- a/projects/app/src/web/core/app/constants.ts +++ b/projects/app/src/web/core/app/constants.ts @@ -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 = { +export const defaultFeishuOutLinkForm: OutLinkEditType = { name: '', limit: { QPM: 100, maxUsagePoints: -1 - }, - responseDetail: false + } }; export enum TTSTypeEnum { diff --git a/projects/app/src/web/support/outLink/api.ts b/projects/app/src/web/support/outLink/api.ts index 1bbf8b0114ca..3f5f52ef5a9a 100644 --- a/projects/app/src/web/support/outLink/api.ts +++ b/projects/app/src/web/support/outLink/api.ts @@ -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( @@ -15,7 +19,10 @@ export const putShareChat = (data: OutLinkEditType) => POST(`/support/outLink/update`, data); // get shareChat -export function getShareChatList(data: { appId: string; type: OutLinkSchema['type'] }) { +export function getShareChatList(data: { + appId: string; + type: OutLinkSchema['type']; +}) { return GET[]>(`/support/outLink/list`, data); } @@ -25,7 +32,7 @@ export function delShareChatById(id: string) { } // update a shareChat -export function updateShareChat(data: OutLinkEditType) { +export function updateShareChat(data: OutLinkEditType) { return POST(`/support/outLink/update`, data); }