4.6.4-alpha (#569)
This commit is contained in:
parent
71afe71192
commit
e01c38efe0
@ -1,94 +1,175 @@
|
|||||||
---
|
---
|
||||||
title: '分享链接鉴权'
|
title: '分享链接身份鉴权'
|
||||||
description: 'FastGPT 分享链接鉴权'
|
description: 'FastGPT 分享链接身份鉴权'
|
||||||
icon: 'share'
|
icon: 'share'
|
||||||
draft: false
|
draft: false
|
||||||
toc: true
|
toc: true
|
||||||
weight: 860
|
weight: 860
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 介绍
|
||||||
|
|
||||||
|
在 FastGPT V4.6.4 中,我们修改了分享链接的数据读取方式,为每个用户生成一个 localId,用于标识用户,从云端拉取对话记录。但是这种方式仅能保障用户在同一设备同一浏览器中使用,如果切换设备或者清空浏览器缓存则会丢失这些记录。这种方式存在一定的风险,因此我们仅允许用户拉取近`30天`的`20条`记录。
|
||||||
|
|
||||||
|
分享链接身份鉴权设计的目的在于,将 FastGPT 的对话框快速、安全的接入到你现有的系统中,仅需 2 个接口即可实现。
|
||||||
|
|
||||||
## 使用说明
|
## 使用说明
|
||||||
|
|
||||||
分享链接鉴权设计的目的在于,将 FastGPT 的对话框安全的接入你现有的系统中。
|
免登录链接配置中,你可以选择填写`身份验证`栏。这是一个`POST`请求的根地址。在填写该地址后,分享链接的初始化、开始对话以及对话结束都会向该地址的特定接口发送一条请求。下面以`host`来表示`凭身份验证根地址`。服务器接口仅需返回是否校验成功即可,不需要返回其他数据,格式如下:
|
||||||
|
|
||||||
免登录链接配置中,增加了`凭证校验服务器`后,使用分享链接时会向服务器发起请求,校验链接是否可用,并在每次对话结束后,向服务器发送对话结果。下面以`host`来表示`凭证校验服务器`。服务器接口仅需返回是否校验成功即可,不需要返回其他数据,格式如下:
|
### 接口统一响应格式
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"success": true,
|
"success": true,
|
||||||
"message": "错误提示",
|
"message": "错误提示",
|
||||||
"msg": "同message, 错误提示"
|
"msg": "同message, 错误提示",
|
||||||
|
"uid": "用户唯一凭证"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`FastGPT` 将会判断`success`是否为`true`决定是允许用户继续操作。`message`与`msg`是等同的,你可以选择返回其中一个,当`success`不为`true`时,将会提示这个错误。
|
||||||
|
|
||||||
|
`uid`是用户的唯一凭证,将会用于拉取对话记录以及保存对话记录。可参考下方实践案例。
|
||||||
|
|
||||||
|
### 触发流程
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 配置校验地址和校验token
|
## 配置教程
|
||||||
|
### 1. 配置身份校验地址
|
||||||
### 1. 配置校验地址的`BaseURL`、
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
配置校验地址后,在每次分享链接使用时,都会向对应的地址发起校验和上报请求。
|
配置校验地址后,在每次分享链接使用时,都会向对应的地址发起校验和上报请求。
|
||||||
|
|
||||||
|
{{% alert icon="🤖" %}}
|
||||||
|
这里仅需配置根地址,无需具体到完整请求路径。
|
||||||
|
{{% /alert %}}
|
||||||
|
|
||||||
### 2. 分享链接中增加额外 query
|
### 2. 分享链接中增加额外 query
|
||||||
|
|
||||||
在分享链接的地址中,增加一个额外的参数: authToken。例如:
|
在分享链接的地址中,增加一个额外的参数: authToken。例如:
|
||||||
|
|
||||||
原始的链接:https://fastgpt.run/chat/share?shareId=648aaf5ae121349a16d62192
|
原始的链接:`https://fastgpt.run/chat/share?shareId=648aaf5ae121349a16d62192`
|
||||||
完整链接: https://fastgpt.run/chat/share?shareId=648aaf5ae121349a16d62192&authToken=userid12345
|
|
||||||
|
|
||||||
这个`token`通常是你系统生成的,在发出校验请求时,FastGPT 会在`body`中携带 token={{authToken}} 的参数。
|
完整链接: `https://fastgpt.run/chat/share?shareId=648aaf5ae121349a16d62192&authToken=userid12345`
|
||||||
|
|
||||||
## 聊天初始化校验
|
这个`authToken`通常是你系统生成的用户唯一凭证(Token之类的)。FastGPT 会在鉴权接口的`body`中携带 token={{authToken}} 的参数。
|
||||||
|
|
||||||
**FastGPT 发出的请求**
|
### 3. 编写聊天初始化校验接口
|
||||||
|
|
||||||
|
{{< tabs tabTotal="3" >}}
|
||||||
|
{{< tab tabName="请求示例" >}}
|
||||||
|
{{< markdownify >}}
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl --location --request POST '{{host}}/shareAuth/init' \
|
curl --location --request POST '{{host}}/shareAuth/init' \
|
||||||
--header 'Content-Type: application/json' \
|
--header 'Content-Type: application/json' \
|
||||||
--data-raw '{
|
--data-raw '{
|
||||||
"token": "sintdolore"
|
"token": "{{authToken}}"
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
**响应示例**
|
{{< /markdownify >}}
|
||||||
|
{{< /tab >}}
|
||||||
|
|
||||||
|
{{< tab tabName="鉴权成功" >}}
|
||||||
|
{{< markdownify >}}
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"uid": "username123",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
系统会拉取该分享链接下,uid 为 username123 的对话记录。
|
||||||
|
|
||||||
|
{{< /markdownify >}}
|
||||||
|
{{< /tab >}}
|
||||||
|
|
||||||
|
{{< tab tabName="鉴权失败" >}}
|
||||||
|
{{< markdownify >}}
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"success": false,
|
"success": false,
|
||||||
"message": "分享链接无效",
|
"message": "身份错误",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 对话前校验
|
{{< /markdownify >}}
|
||||||
|
{{< /tab >}}
|
||||||
|
{{< /tabs >}}
|
||||||
|
|
||||||
**FastGPT 发出的请求**
|
|
||||||
|
|
||||||
|
### 4. 编写对话前校验接口
|
||||||
|
|
||||||
|
{{< tabs tabTotal="3" >}}
|
||||||
|
{{< tab tabName="请求示例" >}}
|
||||||
|
{{< markdownify >}}
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl --location --request POST '{{host}}/shareAuth/start' \
|
curl --location --request POST '{{host}}/shareAuth/start' \
|
||||||
--header 'Content-Type: application/json' \
|
--header 'Content-Type: application/json' \
|
||||||
--data-raw '{
|
--data-raw '{
|
||||||
"token": "sintdolore",
|
"token": "{{authToken}}",
|
||||||
"question": "用户问题",
|
"question": "用户问题",
|
||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
**响应示例**
|
{{< /markdownify >}}
|
||||||
|
{{< /tab >}}
|
||||||
|
|
||||||
|
{{< tab tabName="鉴权成功" >}}
|
||||||
|
{{< markdownify >}}
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"success": true
|
"success": true,
|
||||||
|
"uid": "username123",
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## 对话结果上报
|
{{< /markdownify >}}
|
||||||
|
{{< /tab >}}
|
||||||
|
|
||||||
|
{{< tab tabName="鉴权失败" >}}
|
||||||
|
{{< markdownify >}}
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"message": "身份验证失败",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"message": "存在违规词",
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{{< /markdownify >}}
|
||||||
|
{{< /tab >}}
|
||||||
|
{{< /tabs >}}
|
||||||
|
|
||||||
|
### 5. 编写对话结果上报接口(可选)
|
||||||
|
|
||||||
|
该接口无规定返回值。
|
||||||
|
|
||||||
|
响应值与[chat 接口格式相同](/docs/development/openapi/chat/#响应),仅多了一个`token`。
|
||||||
|
|
||||||
|
可以重点关注`responseData`里的`price`值,`price`与实际价格的倍率为`100000`,即 100000=1元。
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl --location --request POST '{{host}}/shareAuth/finish' \
|
curl --location --request POST '{{host}}/shareAuth/finish' \
|
||||||
--header 'Content-Type: application/json' \
|
--header 'Content-Type: application/json' \
|
||||||
--data-raw '{
|
--data-raw '{
|
||||||
"token": "sint dolore",
|
"token": "{{authToken}}",
|
||||||
"responseData": [
|
"responseData": [
|
||||||
{
|
{
|
||||||
"moduleName": "KB Search",
|
"moduleName": "KB Search",
|
||||||
@ -156,18 +237,18 @@ curl --location --request POST '{{host}}/shareAuth/finish' \
|
|||||||
}'
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
响应值与 chat 接口相同,增加了一个 token。可以重点关注`responseData`里的值,price 与实际价格的倍率为`100000`。
|
|
||||||
|
|
||||||
**此接口无需响应值**
|
|
||||||
|
|
||||||
## 使用示例
|
## 实践案例
|
||||||
|
|
||||||
我们以[Laf作为服务器为例](https://laf.dev/),展示这 3 个接口的使用方式。
|
我们以[Laf作为服务器为例](https://laf.dev/),简单展示这 3 个接口的使用方式。
|
||||||
|
|
||||||
### 1. 创建3个Laf接口
|
### 1. 创建3个Laf接口
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{{< tabs tabTotal="3" >}}
|
{{< tabs tabTotal="3" >}}
|
||||||
{{< tab tabName="/shareAuth/init" >}}
|
{{< tab tabName="/shareAuth/init" >}}
|
||||||
{{< markdownify >}}
|
{{< markdownify >}}
|
||||||
@ -179,13 +260,15 @@ import cloud from '@lafjs/cloud'
|
|||||||
|
|
||||||
export default async function (ctx: FunctionContext) {
|
export default async function (ctx: FunctionContext) {
|
||||||
const { token } = ctx.body
|
const { token } = ctx.body
|
||||||
|
|
||||||
|
// 此处省略 token 解码过程
|
||||||
if (token === 'fastgpt') {
|
if (token === 'fastgpt') {
|
||||||
return { success: true }
|
return { success: true, data: { uid: "user1" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: false,message: "身份错误" }
|
return { success: false,message:"身份错误" }
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
{{< /markdownify >}}
|
{{< /markdownify >}}
|
||||||
@ -201,8 +284,8 @@ import cloud from '@lafjs/cloud'
|
|||||||
|
|
||||||
export default async function (ctx: FunctionContext) {
|
export default async function (ctx: FunctionContext) {
|
||||||
const { token, question } = ctx.body
|
const { token, question } = ctx.body
|
||||||
console.log(token, question, 'start')
|
|
||||||
|
|
||||||
|
// 此处省略 token 解码过程
|
||||||
if (token !== 'fastgpt') {
|
if (token !== 'fastgpt') {
|
||||||
return { success: false, message: "身份错误" }
|
return { success: false, message: "身份错误" }
|
||||||
|
|
||||||
@ -212,8 +295,9 @@ export default async function (ctx: FunctionContext) {
|
|||||||
return { success: false, message: "内容不合规" }
|
return { success: false, message: "内容不合规" }
|
||||||
}
|
}
|
||||||
|
|
||||||
return { success: true }
|
return { success: true, data: { uid: "user1" } }
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
{{< /markdownify >}}
|
{{< /markdownify >}}
|
||||||
@ -229,7 +313,12 @@ import cloud from '@lafjs/cloud'
|
|||||||
|
|
||||||
export default async function (ctx: FunctionContext) {
|
export default async function (ctx: FunctionContext) {
|
||||||
const { token, responseData } = ctx.body
|
const { token, responseData } = ctx.body
|
||||||
console.log(token,responseData,'=====')
|
|
||||||
|
const total = responseData.reduce((sum,item) => sum + item.price,0)
|
||||||
|
const amount = total / 100000
|
||||||
|
|
||||||
|
// 省略数据库操作
|
||||||
|
|
||||||
return { }
|
return { }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -241,17 +330,24 @@ export default async function (ctx: FunctionContext) {
|
|||||||
|
|
||||||
### 2. 配置校验地址
|
### 2. 配置校验地址
|
||||||
|
|
||||||
我们随便复制3个地址中一个接口:https://d8dns0.laf.dev/shareAuth/finish , 去除 /shareAuth/finish 后填入 FastGPT 中: https://d8dns0.laf.dev
|
我们随便复制3个地址中一个接口: `https://d8dns0.laf.dev/shareAuth/finish`, 去除`/shareAuth/finish`后填入`身份校验`:`https://d8dns0.laf.dev`
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
### 3. 修改分享链接参数
|
### 3. 修改分享链接参数
|
||||||
|
|
||||||
源分享链接:[https://fastgpt.run/chat/share?shareId=64be36376a438af0311e599c](https://fastgpt.run/chat/share?shareId=64be36376a438af0311e599c)
|
源分享链接:`https://fastgpt.run/chat/share?shareId=64be36376a438af0311e599c`
|
||||||
|
|
||||||
修改后:[https://fastgpt.run/chat/share?shareId=64be36376a438af0311e599c&authToken=fastgpt](https://fastgpt.run/chat/share?shareId=64be36376a438af0311e599c&authToken=fastgpt)
|
修改后:`https://fastgpt.run/chat/share?shareId=64be36376a438af0311e599c&authToken=fastgpt`
|
||||||
|
|
||||||
### 4. 测试效果
|
### 4. 测试效果
|
||||||
|
|
||||||
1. 打开源链接或者`authToken`不等于 `fastgpt`的链接会提示身份错误。
|
1. 打开源链接或者`authToken`不等于`fastgpt`的链接会提示身份错误。
|
||||||
2. 发送内容中包含你字,会提示内容不合规。
|
2. 发送内容中包含你字,会提示内容不合规。
|
||||||
|
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
这个鉴权方式通常是帮助你直接嵌入`分享链接`到你的应用中,在你的应用打开分享链接前,应做`authToken`的拼接后再打开。
|
||||||
|
|
||||||
|
除了对接已有系统的用户外,你还可以对接`余额`功能,通过`结果上报`接口扣除用户余额,通过`对话前校验`接口检查用户的余额。
|
||||||
@ -4,7 +4,9 @@ import { ErrType } from '../errorCode';
|
|||||||
export enum OutLinkErrEnum {
|
export enum OutLinkErrEnum {
|
||||||
unExist = 'unExist',
|
unExist = 'unExist',
|
||||||
unAuthLink = 'unAuthLink',
|
unAuthLink = 'unAuthLink',
|
||||||
linkUnInvalid = 'linkUnInvalid'
|
linkUnInvalid = 'linkUnInvalid',
|
||||||
|
|
||||||
|
unAuthUser = 'unAuthUser'
|
||||||
}
|
}
|
||||||
const errList = [
|
const errList = [
|
||||||
{
|
{
|
||||||
@ -19,6 +21,10 @@ const errList = [
|
|||||||
code: 501,
|
code: 501,
|
||||||
statusText: OutLinkErrEnum.linkUnInvalid,
|
statusText: OutLinkErrEnum.linkUnInvalid,
|
||||||
message: '分享链接无效'
|
message: '分享链接无效'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusText: OutLinkErrEnum.unAuthUser,
|
||||||
|
message: '身份校验失败'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
export default errList.reduce((acc, cur, index) => {
|
export default errList.reduce((acc, cur, index) => {
|
||||||
|
|||||||
33
packages/global/core/chat/api.d.ts
vendored
33
packages/global/core/chat/api.d.ts
vendored
@ -1,33 +0,0 @@
|
|||||||
import { ModuleItemType } from '../module/type';
|
|
||||||
import { AdminFbkType, ChatItemType, moduleDispatchResType } from './type';
|
|
||||||
|
|
||||||
export type UpdateHistoryProps = {
|
|
||||||
chatId: string;
|
|
||||||
customTitle?: string;
|
|
||||||
top?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AdminUpdateFeedbackParams = AdminFbkType & {
|
|
||||||
chatItemId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InitChatResponse = {
|
|
||||||
chatId: string;
|
|
||||||
appId: string;
|
|
||||||
app: {
|
|
||||||
userGuideModule?: ModuleItemType;
|
|
||||||
chatModels?: string[];
|
|
||||||
name: string;
|
|
||||||
avatar: string;
|
|
||||||
intro: string;
|
|
||||||
canUse?: boolean;
|
|
||||||
};
|
|
||||||
title: string;
|
|
||||||
variables: Record<string, any>;
|
|
||||||
history: ChatItemType[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ChatHistoryItemResType = moduleDispatchResType & {
|
|
||||||
moduleType: `${FlowNodeTypeEnum}`;
|
|
||||||
moduleName: string;
|
|
||||||
};
|
|
||||||
@ -44,6 +44,12 @@ export const ChatSourceMap = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum ChatStatusEnum {
|
||||||
|
loading = 'loading',
|
||||||
|
running = 'running',
|
||||||
|
finish = 'finish'
|
||||||
|
}
|
||||||
|
|
||||||
export const HUMAN_ICON = `/icon/human.svg`;
|
export const HUMAN_ICON = `/icon/human.svg`;
|
||||||
export const LOGO_ICON = `/icon/logo.svg`;
|
export const LOGO_ICON = `/icon/logo.svg`;
|
||||||
|
|
||||||
|
|||||||
17
packages/global/core/chat/type.d.ts
vendored
17
packages/global/core/chat/type.d.ts
vendored
@ -1,6 +1,6 @@
|
|||||||
import { ClassifyQuestionAgentItemType } from '../module/type';
|
import { ClassifyQuestionAgentItemType } from '../module/type';
|
||||||
import { SearchDataResponseItemType } from '../dataset/type';
|
import { SearchDataResponseItemType } from '../dataset/type';
|
||||||
import { ChatRoleEnum, ChatSourceEnum } from './constants';
|
import { ChatRoleEnum, ChatSourceEnum, ChatStatusEnum } from './constants';
|
||||||
import { FlowNodeTypeEnum } from '../module/node/constant';
|
import { FlowNodeTypeEnum } from '../module/node/constant';
|
||||||
import { ModuleOutputKeyEnum } from '../module/constants';
|
import { ModuleOutputKeyEnum } from '../module/constants';
|
||||||
import { AppSchema } from '../app/type';
|
import { AppSchema } from '../app/type';
|
||||||
@ -20,7 +20,7 @@ export type ChatSchema = {
|
|||||||
variables: Record<string, any>;
|
variables: Record<string, any>;
|
||||||
source: `${ChatSourceEnum}`;
|
source: `${ChatSourceEnum}`;
|
||||||
shareId?: string;
|
shareId?: string;
|
||||||
isInit: boolean;
|
outLinkUid?: string;
|
||||||
content: ChatItemType[];
|
content: ChatItemType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -51,6 +51,7 @@ export type AdminFbkType = {
|
|||||||
a?: string;
|
a?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* --------- chat item ---------- */
|
||||||
export type ChatItemType = {
|
export type ChatItemType = {
|
||||||
dataId?: string;
|
dataId?: string;
|
||||||
obj: ChatItemSchema['obj'];
|
obj: ChatItemSchema['obj'];
|
||||||
@ -61,11 +62,12 @@ export type ChatItemType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ChatSiteItemType = ChatItemType & {
|
export type ChatSiteItemType = ChatItemType & {
|
||||||
status: 'loading' | 'running' | 'finish';
|
status: `${ChatStatusEnum}`;
|
||||||
moduleName?: string;
|
moduleName?: string;
|
||||||
ttsBuffer?: Uint8Array;
|
ttsBuffer?: Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ---------- history ------------- */
|
||||||
export type HistoryItemType = {
|
export type HistoryItemType = {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
updateTime: Date;
|
updateTime: Date;
|
||||||
@ -77,10 +79,10 @@ export type ChatHistoryItemType = HistoryItemType & {
|
|||||||
top: boolean;
|
top: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
// response data
|
/* ------- response data ------------ */
|
||||||
export type moduleDispatchResType = {
|
export type moduleDispatchResType = {
|
||||||
moduleLogo?: string;
|
moduleLogo?: string;
|
||||||
price: number;
|
price?: number;
|
||||||
runningTime?: number;
|
runningTime?: number;
|
||||||
tokens?: number;
|
tokens?: number;
|
||||||
model?: string;
|
model?: string;
|
||||||
@ -112,3 +114,8 @@ export type moduleDispatchResType = {
|
|||||||
// plugin output
|
// plugin output
|
||||||
pluginOutput?: Record<string, any>;
|
pluginOutput?: Record<string, any>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ChatHistoryItemResType = moduleDispatchResType & {
|
||||||
|
moduleType: `${FlowNodeTypeEnum}`;
|
||||||
|
moduleName: string;
|
||||||
|
};
|
||||||
|
|||||||
2
packages/global/core/dataset/api.d.ts
vendored
2
packages/global/core/dataset/api.d.ts
vendored
@ -6,9 +6,9 @@ import type { LLMModelItemType } from '../ai/model.d';
|
|||||||
export type DatasetUpdateBody = {
|
export type DatasetUpdateBody = {
|
||||||
id: string;
|
id: string;
|
||||||
parentId?: string;
|
parentId?: string;
|
||||||
tags?: string[];
|
|
||||||
name?: string;
|
name?: string;
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
|
intro?: string;
|
||||||
permission?: DatasetSchemaType['permission'];
|
permission?: DatasetSchemaType['permission'];
|
||||||
agentModel?: LLMModelItemType;
|
agentModel?: LLMModelItemType;
|
||||||
websiteConfig?: DatasetSchemaType['websiteConfig'];
|
websiteConfig?: DatasetSchemaType['websiteConfig'];
|
||||||
|
|||||||
29
packages/global/support/outLink/api.d.ts
vendored
29
packages/global/support/outLink/api.d.ts
vendored
@ -1,27 +1,12 @@
|
|||||||
import type { HistoryItemType, ChatSiteItemType } from '../../core/chat/type.d';
|
import type { HistoryItemType, ChatSiteItemType } from '../../core/chat/type.d';
|
||||||
import type { InitChatResponse } from '../../core/chat/api.d';
|
|
||||||
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
||||||
|
|
||||||
export type InitShareChatResponse = {
|
export type AuthOutLinkInitProps = {
|
||||||
userAvatar: string;
|
outLinkUid: string;
|
||||||
app: InitChatResponse['app'];
|
|
||||||
};
|
|
||||||
|
|
||||||
/* one page type */
|
|
||||||
export type ShareChatType = InitShareChatResponse & {
|
|
||||||
history: ShareChatHistoryItemType;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* history list item type */
|
|
||||||
export type ShareChatHistoryItemType = HistoryItemType & {
|
|
||||||
shareId: string;
|
|
||||||
variables?: Record<string, any>;
|
|
||||||
chats: ChatSiteItemType[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AuthLinkChatProps = { ip?: string | null; authToken?: string; question: string };
|
|
||||||
export type AuthLinkLimitProps = AuthLinkChatProps & { outLink: OutLinkSchema };
|
|
||||||
export type AuthShareChatInitProps = {
|
|
||||||
authToken?: string;
|
|
||||||
tokenUrl?: string;
|
tokenUrl?: string;
|
||||||
};
|
};
|
||||||
|
export type AuthOutLinkChatProps = { ip?: string | null; outLinkUid: string; question: string };
|
||||||
|
export type AuthOutLinkLimitProps = AuthOutLinkChatProps & { outLink: OutLinkSchema };
|
||||||
|
export type AuthOutLinkResponse = {
|
||||||
|
uid: string;
|
||||||
|
};
|
||||||
|
|||||||
1
packages/global/support/outLink/type.d.ts
vendored
1
packages/global/support/outLink/type.d.ts
vendored
@ -3,7 +3,6 @@ import { OutLinkTypeEnum } from './constant';
|
|||||||
export type OutLinkSchema = {
|
export type OutLinkSchema = {
|
||||||
_id: string;
|
_id: string;
|
||||||
shareId: string;
|
shareId: string;
|
||||||
userId: string;
|
|
||||||
teamId: string;
|
teamId: string;
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
appId: string;
|
appId: string;
|
||||||
|
|||||||
@ -45,4 +45,4 @@ export const TeamMemberStatusMap = {
|
|||||||
color: 'red.600'
|
color: 'red.600'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
export const leaveStatus = { $ne: TeamMemberStatusEnum.leave };
|
export const notLeaveStatus = { $ne: TeamMemberStatusEnum.leave };
|
||||||
|
|||||||
14
packages/global/support/user/team/type.d.ts
vendored
14
packages/global/support/user/team/type.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
import { UserModelSchema } from '../type';
|
import type { UserModelSchema } from '../type';
|
||||||
import { TeamMemberRoleEnum, TeamMemberStatusEnum } from './constant';
|
import type { TeamMemberRoleEnum, TeamMemberStatusEnum } from './constant';
|
||||||
|
|
||||||
export type TeamSchema = {
|
export type TeamSchema = {
|
||||||
_id: string;
|
_id: string;
|
||||||
@ -22,6 +22,16 @@ export type TeamMemberSchema = {
|
|||||||
defaultTeam: boolean;
|
defaultTeam: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type TeamMemberWithUserSchema = TeamMemberSchema & {
|
||||||
|
userId: UserModelSchema;
|
||||||
|
};
|
||||||
|
export type TeamMemberWithTeamSchema = TeamMemberSchema & {
|
||||||
|
teamId: TeamSchema;
|
||||||
|
};
|
||||||
|
export type TeamMemberWithTeamAndUserSchema = TeamMemberWithTeamSchema & {
|
||||||
|
userId: UserModelSchema;
|
||||||
|
};
|
||||||
|
|
||||||
export type TeamItemType = {
|
export type TeamItemType = {
|
||||||
userId: string;
|
userId: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
|
|||||||
@ -26,9 +26,13 @@ export async function connectMongo({
|
|||||||
bufferCommands: true,
|
bufferCommands: true,
|
||||||
maxConnecting: Number(process.env.DB_MAX_LINK || 5),
|
maxConnecting: Number(process.env.DB_MAX_LINK || 5),
|
||||||
maxPoolSize: Number(process.env.DB_MAX_LINK || 5),
|
maxPoolSize: Number(process.env.DB_MAX_LINK || 5),
|
||||||
minPoolSize: Number(process.env.DB_MAX_LINK || 10) * 0.5,
|
minPoolSize: Math.min(10, Number(process.env.DB_MAX_LINK || 10)),
|
||||||
connectTimeoutMS: 60000,
|
connectTimeoutMS: 60000,
|
||||||
waitQueueTimeoutMS: 60000
|
waitQueueTimeoutMS: 60000,
|
||||||
|
socketTimeoutMS: 60000,
|
||||||
|
maxIdleTimeMS: 300000,
|
||||||
|
retryWrites: true,
|
||||||
|
retryReads: true
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('mongo connected');
|
console.log('mongo connected');
|
||||||
|
|||||||
@ -50,10 +50,6 @@ const ChatSchema = new Schema({
|
|||||||
top: {
|
top: {
|
||||||
type: Boolean
|
type: Boolean
|
||||||
},
|
},
|
||||||
variables: {
|
|
||||||
type: Object,
|
|
||||||
default: {}
|
|
||||||
},
|
|
||||||
source: {
|
source: {
|
||||||
type: String,
|
type: String,
|
||||||
enum: Object.keys(ChatSourceMap),
|
enum: Object.keys(ChatSourceMap),
|
||||||
@ -62,9 +58,17 @@ const ChatSchema = new Schema({
|
|||||||
shareId: {
|
shareId: {
|
||||||
type: String
|
type: String
|
||||||
},
|
},
|
||||||
isInit: {
|
outLinkUid: {
|
||||||
type: Boolean,
|
type: String
|
||||||
default: false
|
},
|
||||||
|
variables: {
|
||||||
|
type: Object,
|
||||||
|
default: {}
|
||||||
|
},
|
||||||
|
metadata: {
|
||||||
|
//For special storage
|
||||||
|
type: Object,
|
||||||
|
default: {}
|
||||||
},
|
},
|
||||||
content: {
|
content: {
|
||||||
type: [
|
type: [
|
||||||
@ -89,9 +93,10 @@ const ChatSchema = new Schema({
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ChatSchema.index({ tmbId: 1 });
|
|
||||||
ChatSchema.index({ updateTime: -1 });
|
|
||||||
ChatSchema.index({ appId: 1 });
|
ChatSchema.index({ appId: 1 });
|
||||||
|
ChatSchema.index({ tmbId: 1 });
|
||||||
|
ChatSchema.index({ shareId: 1 });
|
||||||
|
ChatSchema.index({ updateTime: -1 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
|
|||||||
22
packages/service/core/chat/controller.ts
Normal file
22
packages/service/core/chat/controller.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||||
|
import { MongoChatItem } from './chatItemSchema';
|
||||||
|
|
||||||
|
export async function getChatItems({
|
||||||
|
chatId,
|
||||||
|
limit = 30,
|
||||||
|
field
|
||||||
|
}: {
|
||||||
|
chatId?: string;
|
||||||
|
limit?: number;
|
||||||
|
field: string;
|
||||||
|
}): Promise<{ history: ChatItemType[] }> {
|
||||||
|
if (!chatId) {
|
||||||
|
return { history: [] };
|
||||||
|
}
|
||||||
|
|
||||||
|
const history = await MongoChatItem.find({ chatId }, field).sort({ _id: -1 }).limit(limit);
|
||||||
|
|
||||||
|
history.reverse();
|
||||||
|
|
||||||
|
return { history };
|
||||||
|
}
|
||||||
@ -12,10 +12,6 @@ const OutLinkSchema = new Schema({
|
|||||||
type: String,
|
type: String,
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
userId: {
|
|
||||||
type: Schema.Types.ObjectId,
|
|
||||||
ref: 'user'
|
|
||||||
},
|
|
||||||
teamId: {
|
teamId: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: TeamCollectionName,
|
ref: TeamCollectionName,
|
||||||
|
|||||||
@ -22,15 +22,15 @@ export const updateOutLinkUsage = async ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const pushResult2Remote = async ({
|
export const pushResult2Remote = async ({
|
||||||
authToken,
|
outLinkUid,
|
||||||
shareId,
|
shareId,
|
||||||
responseData
|
responseData
|
||||||
}: {
|
}: {
|
||||||
authToken?: string;
|
outLinkUid?: string; // raw id, not parse
|
||||||
shareId?: string;
|
shareId?: string;
|
||||||
responseData?: any[];
|
responseData?: any[];
|
||||||
}) => {
|
}) => {
|
||||||
if (!shareId || !authToken || !global.systemEnv.pluginBaseUrl) return;
|
if (!shareId || !outLinkUid || !global.systemEnv.pluginBaseUrl) return;
|
||||||
try {
|
try {
|
||||||
const outLink = await MongoOutLink.findOne({
|
const outLink = await MongoOutLink.findOne({
|
||||||
shareId
|
shareId
|
||||||
@ -42,7 +42,7 @@ export const pushResult2Remote = async ({
|
|||||||
baseURL: outLink.limit.hookUrl,
|
baseURL: outLink.limit.hookUrl,
|
||||||
url: '/shareAuth/finish',
|
url: '/shareAuth/finish',
|
||||||
data: {
|
data: {
|
||||||
token: authToken,
|
token: outLinkUid,
|
||||||
responseData
|
responseData
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -19,6 +19,7 @@ export async function authApp({
|
|||||||
AuthResponseType & {
|
AuthResponseType & {
|
||||||
teamOwner: boolean;
|
teamOwner: boolean;
|
||||||
app: AppDetailType;
|
app: AppDetailType;
|
||||||
|
role: `${TeamMemberRoleEnum}`;
|
||||||
}
|
}
|
||||||
> {
|
> {
|
||||||
const result = await parseHeaderCert(props);
|
const result = await parseHeaderCert(props);
|
||||||
@ -65,6 +66,7 @@ export async function authApp({
|
|||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
app,
|
app,
|
||||||
|
role,
|
||||||
isOwner,
|
isOwner,
|
||||||
canWrite,
|
canWrite,
|
||||||
teamOwner: role === TeamMemberRoleEnum.owner
|
teamOwner: role === TeamMemberRoleEnum.owner
|
||||||
|
|||||||
@ -1,67 +0,0 @@
|
|||||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
|
||||||
import { AuthModeType } from '../type';
|
|
||||||
import type { ChatWithAppSchema } from '@fastgpt/global/core/chat/type';
|
|
||||||
import { parseHeaderCert } from '../controller';
|
|
||||||
import { MongoChat } from '../../../core/chat/chatSchema';
|
|
||||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
|
||||||
import { getTeamInfoByTmbId } from '../../user/team/controller';
|
|
||||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
|
||||||
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
|
|
||||||
|
|
||||||
export async function authChat({
|
|
||||||
chatId,
|
|
||||||
per = 'owner',
|
|
||||||
...props
|
|
||||||
}: AuthModeType & {
|
|
||||||
chatId: string;
|
|
||||||
}): Promise<
|
|
||||||
AuthResponseType & {
|
|
||||||
chat: ChatWithAppSchema;
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
const { userId, teamId, tmbId } = await parseHeaderCert(props);
|
|
||||||
const { role } = await getTeamInfoByTmbId({ tmbId });
|
|
||||||
|
|
||||||
const { chat, isOwner, canWrite } = await (async () => {
|
|
||||||
// get chat
|
|
||||||
const chat = (await MongoChat.findOne({ chatId, teamId })
|
|
||||||
.populate('appId')
|
|
||||||
.lean()) as ChatWithAppSchema;
|
|
||||||
|
|
||||||
if (!chat) {
|
|
||||||
return Promise.reject('Chat is not exists');
|
|
||||||
}
|
|
||||||
|
|
||||||
const isOwner = role === TeamMemberRoleEnum.owner || String(chat.tmbId) === tmbId;
|
|
||||||
const canWrite = isOwner;
|
|
||||||
|
|
||||||
if (
|
|
||||||
per === 'r' &&
|
|
||||||
role !== TeamMemberRoleEnum.owner &&
|
|
||||||
chat.appId.permission !== PermissionTypeEnum.public
|
|
||||||
) {
|
|
||||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
|
||||||
}
|
|
||||||
if (per === 'w' && !canWrite) {
|
|
||||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
|
||||||
}
|
|
||||||
if (per === 'owner' && !isOwner) {
|
|
||||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
chat,
|
|
||||||
isOwner,
|
|
||||||
canWrite
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
return {
|
|
||||||
userId,
|
|
||||||
teamId,
|
|
||||||
tmbId,
|
|
||||||
chat,
|
|
||||||
isOwner,
|
|
||||||
canWrite
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -20,11 +20,11 @@ export async function authCertAndShareId({
|
|||||||
return authCert(props);
|
return authCert(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { app } = await authOutLinkValid({ shareId });
|
const { shareChat } = await authOutLinkValid({ shareId });
|
||||||
|
|
||||||
return {
|
return {
|
||||||
teamId: String(app.teamId),
|
teamId: String(shareChat.teamId),
|
||||||
tmbId: String(app.tmbId),
|
tmbId: String(shareChat.tmbId),
|
||||||
authType: AuthUserTypeEnum.outLink,
|
authType: AuthUserTypeEnum.outLink,
|
||||||
apikey: '',
|
apikey: '',
|
||||||
isOwner: false,
|
isOwner: false,
|
||||||
|
|||||||
@ -78,21 +78,19 @@ export async function authOutLinkCrud({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* outLink exist and it app exist */
|
||||||
export async function authOutLinkValid({ shareId }: { shareId?: string }) {
|
export async function authOutLinkValid({ shareId }: { shareId?: string }) {
|
||||||
|
if (!shareId) {
|
||||||
|
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||||
|
}
|
||||||
const shareChat = await MongoOutLink.findOne({ shareId });
|
const shareChat = await MongoOutLink.findOne({ shareId });
|
||||||
|
|
||||||
if (!shareChat) {
|
if (!shareChat) {
|
||||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
const app = await MongoApp.findById(shareChat.appId);
|
|
||||||
|
|
||||||
if (!app) {
|
|
||||||
return Promise.reject(AppErrEnum.unExist);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
app,
|
appId: shareChat.appId,
|
||||||
shareChat
|
shareChat
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,8 @@
|
|||||||
|
import { UserType } from '@fastgpt/global/support/user/type';
|
||||||
import { MongoUser } from './schema';
|
import { MongoUser } from './schema';
|
||||||
|
import { getTeamInfoByTmbId, getUserDefaultTeam } from './team/controller';
|
||||||
|
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||||
|
import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
|
||||||
|
|
||||||
export async function authUserExist({ userId, username }: { userId?: string; username?: string }) {
|
export async function authUserExist({ userId, username }: { userId?: string; username?: string }) {
|
||||||
if (userId) {
|
if (userId) {
|
||||||
@ -9,3 +13,56 @@ export async function authUserExist({ userId, username }: { userId?: string; use
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getUserDetail({
|
||||||
|
tmbId,
|
||||||
|
userId
|
||||||
|
}: {
|
||||||
|
tmbId?: string;
|
||||||
|
userId?: string;
|
||||||
|
}): Promise<UserType> {
|
||||||
|
const team = await (async () => {
|
||||||
|
if (tmbId) {
|
||||||
|
return getTeamInfoByTmbId({ tmbId });
|
||||||
|
}
|
||||||
|
if (userId) {
|
||||||
|
return getUserDefaultTeam({ userId });
|
||||||
|
}
|
||||||
|
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||||
|
})();
|
||||||
|
const user = await MongoUser.findById(team.userId);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
_id: user._id,
|
||||||
|
username: user.username,
|
||||||
|
avatar: user.avatar,
|
||||||
|
balance: user.balance,
|
||||||
|
timezone: user.timezone,
|
||||||
|
promotionRate: user.promotionRate,
|
||||||
|
openaiAccount: user.openaiAccount,
|
||||||
|
team
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUserAndAuthBalance({
|
||||||
|
tmbId,
|
||||||
|
minBalance
|
||||||
|
}: {
|
||||||
|
tmbId: string;
|
||||||
|
minBalance?: number;
|
||||||
|
}) {
|
||||||
|
const user = await getUserDetail({ tmbId });
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return Promise.reject(UserErrEnum.unAuthUser);
|
||||||
|
}
|
||||||
|
if (minBalance !== undefined && user.team.balance < minBalance) {
|
||||||
|
return Promise.reject(UserErrEnum.balanceNotEnough);
|
||||||
|
}
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,35 +1,15 @@
|
|||||||
import { TeamItemType } from '@fastgpt/global/support/user/team/type';
|
import { TeamItemType, TeamMemberWithTeamSchema } from '@fastgpt/global/support/user/team/type';
|
||||||
import { connectionMongo, Types } from '../../../common/mongo';
|
import { Types } from '../../../common/mongo';
|
||||||
import {
|
import {
|
||||||
TeamMemberRoleEnum,
|
TeamMemberRoleEnum,
|
||||||
TeamMemberStatusEnum,
|
TeamMemberStatusEnum,
|
||||||
TeamCollectionName,
|
notLeaveStatus
|
||||||
TeamMemberCollectionName,
|
|
||||||
leaveStatus
|
|
||||||
} from '@fastgpt/global/support/user/team/constant';
|
} from '@fastgpt/global/support/user/team/constant';
|
||||||
|
import { MongoTeamMember } from './teamMemberSchema';
|
||||||
|
import { MongoTeam } from './teamSchema';
|
||||||
|
|
||||||
async function getTeam(match: Record<string, any>): Promise<TeamItemType> {
|
async function getTeam(match: Record<string, any>): Promise<TeamItemType> {
|
||||||
const db = connectionMongo?.connection?.db;
|
const tmb = (await MongoTeamMember.findOne(match).populate('teamId')) as TeamMemberWithTeamSchema;
|
||||||
|
|
||||||
const TeamMember = db.collection(TeamMemberCollectionName);
|
|
||||||
|
|
||||||
const results = await TeamMember.aggregate([
|
|
||||||
{
|
|
||||||
$match: match
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$lookup: {
|
|
||||||
from: TeamCollectionName,
|
|
||||||
localField: 'teamId',
|
|
||||||
foreignField: '_id',
|
|
||||||
as: 'team'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$unwind: '$team'
|
|
||||||
}
|
|
||||||
]).toArray();
|
|
||||||
const tmb = results[0];
|
|
||||||
|
|
||||||
if (!tmb) {
|
if (!tmb) {
|
||||||
return Promise.reject('member not exist');
|
return Promise.reject('member not exist');
|
||||||
@ -37,17 +17,17 @@ async function getTeam(match: Record<string, any>): Promise<TeamItemType> {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
userId: String(tmb.userId),
|
userId: String(tmb.userId),
|
||||||
teamId: String(tmb.teamId),
|
teamId: String(tmb.teamId._id),
|
||||||
teamName: tmb.team.name,
|
teamName: tmb.teamId.name,
|
||||||
memberName: tmb.name,
|
memberName: tmb.name,
|
||||||
avatar: tmb.team.avatar,
|
avatar: tmb.teamId.avatar,
|
||||||
balance: tmb.team.balance,
|
balance: tmb.teamId.balance,
|
||||||
tmbId: String(tmb._id),
|
tmbId: String(tmb._id),
|
||||||
role: tmb.role,
|
role: tmb.role,
|
||||||
status: tmb.status,
|
status: tmb.status,
|
||||||
defaultTeam: tmb.defaultTeam,
|
defaultTeam: tmb.defaultTeam,
|
||||||
canWrite: tmb.role !== TeamMemberRoleEnum.visitor,
|
canWrite: tmb.role !== TeamMemberRoleEnum.visitor,
|
||||||
maxSize: tmb.team.maxSize
|
maxSize: tmb.teamId.maxSize
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +37,7 @@ export async function getTeamInfoByTmbId({ tmbId }: { tmbId: string }) {
|
|||||||
}
|
}
|
||||||
return getTeam({
|
return getTeam({
|
||||||
_id: new Types.ObjectId(tmbId),
|
_id: new Types.ObjectId(tmbId),
|
||||||
status: leaveStatus
|
status: notLeaveStatus
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,12 +63,8 @@ export async function createDefaultTeam({
|
|||||||
balance?: number;
|
balance?: number;
|
||||||
maxSize?: number;
|
maxSize?: number;
|
||||||
}) {
|
}) {
|
||||||
const db = connectionMongo.connection.db;
|
|
||||||
const Team = db.collection(TeamCollectionName);
|
|
||||||
const TeamMember = db.collection(TeamMemberCollectionName);
|
|
||||||
|
|
||||||
// auth default team
|
// auth default team
|
||||||
const tmb = await TeamMember.findOne({
|
const tmb = await MongoTeamMember.findOne({
|
||||||
userId: new Types.ObjectId(userId),
|
userId: new Types.ObjectId(userId),
|
||||||
defaultTeam: true
|
defaultTeam: true
|
||||||
});
|
});
|
||||||
@ -97,7 +73,7 @@ export async function createDefaultTeam({
|
|||||||
console.log('create default team', userId);
|
console.log('create default team', userId);
|
||||||
|
|
||||||
// create
|
// create
|
||||||
const { insertedId } = await Team.insertOne({
|
const { _id: insertedId } = await MongoTeam.create({
|
||||||
ownerId: userId,
|
ownerId: userId,
|
||||||
name: teamName,
|
name: teamName,
|
||||||
avatar,
|
avatar,
|
||||||
@ -105,7 +81,7 @@ export async function createDefaultTeam({
|
|||||||
maxSize,
|
maxSize,
|
||||||
createTime: new Date()
|
createTime: new Date()
|
||||||
});
|
});
|
||||||
await TeamMember.insertOne({
|
await MongoTeamMember.create({
|
||||||
teamId: insertedId,
|
teamId: insertedId,
|
||||||
userId,
|
userId,
|
||||||
name: 'Owner',
|
name: 'Owner',
|
||||||
@ -116,16 +92,11 @@ export async function createDefaultTeam({
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
console.log('default team exist', userId);
|
console.log('default team exist', userId);
|
||||||
await Team.updateOne(
|
await MongoTeam.findByIdAndUpdate(tmb.teamId, {
|
||||||
{
|
$set: {
|
||||||
_id: new Types.ObjectId(tmb.teamId)
|
...(balance !== undefined && { balance }),
|
||||||
},
|
maxSize
|
||||||
{
|
|
||||||
$set: {
|
|
||||||
...(balance !== undefined && { balance }),
|
|
||||||
maxSize
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
packages/service/support/user/team/teamMemberSchema.ts
Normal file
51
packages/service/support/user/team/teamMemberSchema.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { connectionMongo, type Model } from '../../../common/mongo';
|
||||||
|
const { Schema, model, models } = connectionMongo;
|
||||||
|
import { TeamMemberSchema as TeamMemberType } from '@fastgpt/global/support/user/team/type.d';
|
||||||
|
import { userCollectionName } from '../../user/schema';
|
||||||
|
import {
|
||||||
|
TeamMemberRoleMap,
|
||||||
|
TeamMemberStatusMap,
|
||||||
|
TeamMemberCollectionName,
|
||||||
|
TeamCollectionName
|
||||||
|
} from '@fastgpt/global/support/user/team/constant';
|
||||||
|
|
||||||
|
const TeamMemberSchema = new Schema({
|
||||||
|
teamId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: TeamCollectionName,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
userId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: userCollectionName,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
default: 'Member'
|
||||||
|
},
|
||||||
|
role: {
|
||||||
|
type: String,
|
||||||
|
enum: Object.keys(TeamMemberRoleMap)
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
type: String,
|
||||||
|
enum: Object.keys(TeamMemberStatusMap)
|
||||||
|
},
|
||||||
|
createTime: {
|
||||||
|
type: Date,
|
||||||
|
default: () => new Date()
|
||||||
|
},
|
||||||
|
defaultTeam: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MongoTeamMember: Model<TeamMemberType> =
|
||||||
|
models[TeamMemberCollectionName] || model(TeamMemberCollectionName, TeamMemberSchema);
|
||||||
41
packages/service/support/user/team/teamSchema.ts
Normal file
41
packages/service/support/user/team/teamSchema.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { connectionMongo, type Model } from '../../../common/mongo';
|
||||||
|
const { Schema, model, models } = connectionMongo;
|
||||||
|
import { TeamSchema as TeamType } from '@fastgpt/global/support/user/team/type.d';
|
||||||
|
import { userCollectionName } from '../../user/schema';
|
||||||
|
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||||
|
import { PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants';
|
||||||
|
|
||||||
|
const TeamSchema = new Schema({
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
ownerId: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: userCollectionName
|
||||||
|
},
|
||||||
|
avatar: {
|
||||||
|
type: String,
|
||||||
|
default: '/icon/logo.svg'
|
||||||
|
},
|
||||||
|
createTime: {
|
||||||
|
type: Date,
|
||||||
|
default: () => Date.now()
|
||||||
|
},
|
||||||
|
balance: {
|
||||||
|
type: Number,
|
||||||
|
default: 2 * PRICE_SCALE
|
||||||
|
},
|
||||||
|
maxSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 5
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MongoTeam: Model<TeamType> =
|
||||||
|
models[TeamCollectionName] || model(TeamCollectionName, TeamSchema);
|
||||||
@ -327,12 +327,12 @@
|
|||||||
"QA Prompt": "QA Prompt",
|
"QA Prompt": "QA Prompt",
|
||||||
"Start Sync Tip": "Are you sure to start synchronizing data? The old data will be deleted and then re-acquired, please confirm!",
|
"Start Sync Tip": "Are you sure to start synchronizing data? The old data will be deleted and then re-acquired, please confirm!",
|
||||||
"Sync": "Data Sync",
|
"Sync": "Data Sync",
|
||||||
|
"Sync Collection": "Data Sync",
|
||||||
"Website Create Success": "Created successfully, data is being synchronized",
|
"Website Create Success": "Created successfully, data is being synchronized",
|
||||||
"Website Empty Tip": "No associated website yet",
|
"Website Empty Tip": "No associated website yet",
|
||||||
"Website Link": "Website Link",
|
"Website Link": "Website Link",
|
||||||
"Website Sync": "Website",
|
"Website Sync": "Website",
|
||||||
"id": "Id",
|
"id": "Id",
|
||||||
"Sync Collection": "Data Sync",
|
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"Chunk Size": "Chunk Size",
|
"Chunk Size": "Chunk Size",
|
||||||
"Createtime": "Create Time",
|
"Createtime": "Create Time",
|
||||||
@ -510,6 +510,10 @@
|
|||||||
"variable options": "Options"
|
"variable options": "Options"
|
||||||
},
|
},
|
||||||
"variable add option": "Add Option"
|
"variable add option": "Add Option"
|
||||||
|
},
|
||||||
|
"shareChat": {
|
||||||
|
"Init Error": "Init Chat Error",
|
||||||
|
"Init History Error": "Init History Error"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dataset": {
|
"dataset": {
|
||||||
|
|||||||
@ -316,7 +316,7 @@
|
|||||||
"Search Top K": "单次搜索数量",
|
"Search Top K": "单次搜索数量",
|
||||||
"Set Empty Result Tip": ",未搜索到内容时回复指定内容",
|
"Set Empty Result Tip": ",未搜索到内容时回复指定内容",
|
||||||
"Set Website Config": "开始配置网站信息",
|
"Set Website Config": "开始配置网站信息",
|
||||||
"Similarity": "相似度",
|
"Similarity": "相关度",
|
||||||
"Sync Time": "最后更新时间",
|
"Sync Time": "最后更新时间",
|
||||||
"Virtual File": "虚拟文件",
|
"Virtual File": "虚拟文件",
|
||||||
"Website Dataset": "Web 站点同步",
|
"Website Dataset": "Web 站点同步",
|
||||||
@ -327,12 +327,12 @@
|
|||||||
"QA Prompt": "QA 拆分引导词",
|
"QA Prompt": "QA 拆分引导词",
|
||||||
"Start Sync Tip": "确认开始同步数据?将会删除旧数据后重新获取,请确认!",
|
"Start Sync Tip": "确认开始同步数据?将会删除旧数据后重新获取,请确认!",
|
||||||
"Sync": "同步数据",
|
"Sync": "同步数据",
|
||||||
|
"Sync Collection": "数据同步",
|
||||||
"Website Create Success": "创建成功,正在同步数据",
|
"Website Create Success": "创建成功,正在同步数据",
|
||||||
"Website Empty Tip": "还没有关联网站",
|
"Website Empty Tip": "还没有关联网站",
|
||||||
"Website Link": "Web 站点地址",
|
"Website Link": "Web 站点地址",
|
||||||
"Website Sync": "Web 站点同步",
|
"Website Sync": "Web 站点同步",
|
||||||
"id": "集合ID",
|
"id": "集合ID",
|
||||||
"Sync Collection": "数据同步",
|
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"Chunk Size": "分割大小",
|
"Chunk Size": "分割大小",
|
||||||
"Createtime": "创建时间",
|
"Createtime": "创建时间",
|
||||||
@ -405,17 +405,17 @@
|
|||||||
"search": {
|
"search": {
|
||||||
"Empty result response": "空搜索回复",
|
"Empty result response": "空搜索回复",
|
||||||
"Empty result response Tips": "若填写该内容,没有搜索到合适内容时,将直接回复填写的内容。",
|
"Empty result response Tips": "若填写该内容,没有搜索到合适内容时,将直接回复填写的内容。",
|
||||||
"Min Similarity": "最低相似度",
|
"Min Similarity": "最低相关度",
|
||||||
"Min Similarity Tips": "不同索引模型的相似度有区别,请通过搜索测试来选择合适的数值",
|
"Min Similarity Tips": "不同索引模型的相关度有区别,请通过搜索测试来选择合适的数值,使用 ReRank 时,相关度可能会很低。",
|
||||||
"Params Setting": "搜索参数设置",
|
"Params Setting": "搜索参数设置",
|
||||||
"Top K": "单次搜索上限",
|
"Top K": "单次搜索上限",
|
||||||
"mode": {
|
"mode": {
|
||||||
"embFullTextReRank": "混合检索",
|
"embFullTextReRank": "混合检索",
|
||||||
"embFullTextReRank desc": "使用向量检索与全文检索混合结果进行 Rerank 进行重排,通常效果最佳",
|
"embFullTextReRank desc": "使用向量检索与全文检索混合结果进行 Rerank 进行重排,相关度通常差异明显,推荐。",
|
||||||
"embedding": "语义检索",
|
"embedding": "语义检索",
|
||||||
"embedding desc": "直接进行向量 topk 相关性查询",
|
"embedding desc": "直接进行向量 topk 相关性查询",
|
||||||
"embeddingReRank": "增强语义检索",
|
"embeddingReRank": "增强语义检索",
|
||||||
"embeddingReRank desc": "超额进行向量 topk 查询后再使用 Rerank 进行排序"
|
"embeddingReRank desc": "超额进行向量 topk 查询后再使用 Rerank 进行排序,相关度通常差异明显。"
|
||||||
},
|
},
|
||||||
"search mode": "检索模式"
|
"search mode": "检索模式"
|
||||||
},
|
},
|
||||||
@ -510,6 +510,10 @@
|
|||||||
"variable options": "选项"
|
"variable options": "选项"
|
||||||
},
|
},
|
||||||
"variable add option": "添加选项"
|
"variable add option": "添加选项"
|
||||||
|
},
|
||||||
|
"shareChat": {
|
||||||
|
"Init Error": "初始化对话框失败",
|
||||||
|
"Init History Error": "初始化聊天记录失败"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dataset": {
|
"dataset": {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
|
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
|
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||||
import { Flex, BoxProps, useDisclosure, Image, useTheme, Box } from '@chakra-ui/react';
|
import { Flex, BoxProps, useDisclosure, Image, useTheme, Box } from '@chakra-ui/react';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { Box, useTheme, Flex, Image } from '@chakra-ui/react';
|
import { Box, useTheme, Flex, Image } from '@chakra-ui/react';
|
||||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
|
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { moduleTemplatesFlat } from '@/web/core/modules/template/system';
|
import { moduleTemplatesFlat } from '@/web/core/modules/template/system';
|
||||||
import Tabs from '../Tabs';
|
import Tabs from '../Tabs';
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import Script from 'next/script';
|
|||||||
import { throttle } from 'lodash';
|
import { throttle } from 'lodash';
|
||||||
import type { ExportChatType } from '@/types/chat.d';
|
import type { ExportChatType } from '@/types/chat.d';
|
||||||
import type { ChatItemType, ChatSiteItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { ChatItemType, ChatSiteItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
|
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { useToast } from '@/web/common/hooks/useToast';
|
import { useToast } from '@/web/common/hooks/useToast';
|
||||||
import { useAudioPlay } from '@/web/common/utils/voice';
|
import { useAudioPlay } from '@/web/common/utils/voice';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
@ -80,7 +80,7 @@ export type StartChatFnProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ComponentRef = {
|
export type ComponentRef = {
|
||||||
getChatHistory: () => ChatSiteItemType[];
|
getChatHistories: () => ChatSiteItemType[];
|
||||||
resetVariables: (data?: Record<string, any>) => void;
|
resetVariables: (data?: Record<string, any>) => void;
|
||||||
resetHistory: (history: ChatSiteItemType[]) => void;
|
resetHistory: (history: ChatSiteItemType[]) => void;
|
||||||
scrollToBottom: (behavior?: 'smooth' | 'auto') => void;
|
scrollToBottom: (behavior?: 'smooth' | 'auto') => void;
|
||||||
@ -134,7 +134,7 @@ const ChatBox = (
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { isPc } = useSystemStore();
|
const { isPc, setLoading } = useSystemStore();
|
||||||
const TextareaDom = useRef<HTMLTextAreaElement>(null);
|
const TextareaDom = useRef<HTMLTextAreaElement>(null);
|
||||||
const chatController = useRef(new AbortController());
|
const chatController = useRef(new AbortController());
|
||||||
const questionGuideController = useRef(new AbortController());
|
const questionGuideController = useRef(new AbortController());
|
||||||
@ -415,15 +415,20 @@ const ChatBox = (
|
|||||||
async (index: number) => {
|
async (index: number) => {
|
||||||
if (!onDelMessage) return;
|
if (!onDelMessage) return;
|
||||||
const delHistory = chatHistory.slice(index);
|
const delHistory = chatHistory.slice(index);
|
||||||
setChatHistory((state) => (index === 0 ? [] : state.slice(0, index)));
|
|
||||||
|
|
||||||
await Promise.all(
|
setLoading(true);
|
||||||
delHistory.map((item, i) => onDelMessage({ contentId: item.dataId, index: index + i }))
|
|
||||||
);
|
|
||||||
|
|
||||||
sendPrompt(variables, delHistory[0].value, chatHistory.slice(0, index));
|
try {
|
||||||
|
await Promise.all(
|
||||||
|
delHistory.map((item, i) => onDelMessage({ contentId: item.dataId, index: index + i }))
|
||||||
|
);
|
||||||
|
setChatHistory((state) => (index === 0 ? [] : state.slice(0, index)));
|
||||||
|
|
||||||
|
sendPrompt(variables, delHistory[0].value, chatHistory.slice(0, index));
|
||||||
|
} catch (error) {}
|
||||||
|
setLoading(false);
|
||||||
},
|
},
|
||||||
[chatHistory, onDelMessage, sendPrompt, variables]
|
[chatHistory, onDelMessage, sendPrompt, setLoading, variables]
|
||||||
);
|
);
|
||||||
// delete one message
|
// delete one message
|
||||||
const delOneMessage = useCallback(
|
const delOneMessage = useCallback(
|
||||||
@ -439,7 +444,7 @@ const ChatBox = (
|
|||||||
|
|
||||||
// output data
|
// output data
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
getChatHistory: () => chatHistory,
|
getChatHistories: () => chatHistory,
|
||||||
resetVariables(e) {
|
resetVariables(e) {
|
||||||
const defaultVal: Record<string, any> = {};
|
const defaultVal: Record<string, any> = {};
|
||||||
variableModules?.forEach((item) => {
|
variableModules?.forEach((item) => {
|
||||||
|
|||||||
@ -86,7 +86,7 @@ const DatasetParamsModal = ({
|
|||||||
min={0}
|
min={0}
|
||||||
max={1}
|
max={1}
|
||||||
step={0.01}
|
step={0.01}
|
||||||
value={getValues(ModuleInputKeyEnum.datasetSimilarity) || 0.5}
|
value={getValues(ModuleInputKeyEnum.datasetSimilarity) ?? 0.5}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
setValue(ModuleInputKeyEnum.datasetSimilarity, val);
|
setValue(ModuleInputKeyEnum.datasetSimilarity, val);
|
||||||
setRefresh(!refresh);
|
setRefresh(!refresh);
|
||||||
@ -107,7 +107,7 @@ const DatasetParamsModal = ({
|
|||||||
]}
|
]}
|
||||||
min={1}
|
min={1}
|
||||||
max={30}
|
max={30}
|
||||||
value={getValues(ModuleInputKeyEnum.datasetLimit) || 5}
|
value={getValues(ModuleInputKeyEnum.datasetLimit) ?? 5}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
setValue(ModuleInputKeyEnum.datasetLimit, val);
|
setValue(ModuleInputKeyEnum.datasetLimit, val);
|
||||||
setRefresh(!refresh);
|
setRefresh(!refresh);
|
||||||
|
|||||||
68
projects/app/src/global/core/chat/api.d.ts
vendored
68
projects/app/src/global/core/chat/api.d.ts
vendored
@ -1,7 +1,75 @@
|
|||||||
import type { AppTTSConfigType } from '@fastgpt/global/core/module/type.d';
|
import type { AppTTSConfigType } from '@fastgpt/global/core/module/type.d';
|
||||||
|
import { ModuleItemType } from '../module/type';
|
||||||
|
import { AdminFbkType, ChatItemType, moduleDispatchResType } from '@fastgpt/global/core/chat/type';
|
||||||
|
|
||||||
export type GetChatSpeechProps = {
|
export type GetChatSpeechProps = {
|
||||||
ttsConfig: AppTTSConfigType;
|
ttsConfig: AppTTSConfigType;
|
||||||
input: string;
|
input: string;
|
||||||
shareId?: string;
|
shareId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* ---------- chat ----------- */
|
||||||
|
export type InitChatProps = {
|
||||||
|
appId?: string;
|
||||||
|
chatId?: string;
|
||||||
|
};
|
||||||
|
export type InitOutLinkChatProps = {
|
||||||
|
chatId?: string;
|
||||||
|
shareId?: string;
|
||||||
|
outLinkUid?: string;
|
||||||
|
};
|
||||||
|
export type InitChatResponse = {
|
||||||
|
chatId?: string;
|
||||||
|
appId: string;
|
||||||
|
userAvatar?: string;
|
||||||
|
title: string;
|
||||||
|
variables: Record<string, any>;
|
||||||
|
history: ChatItemType[];
|
||||||
|
app: {
|
||||||
|
userGuideModule?: ModuleItemType;
|
||||||
|
chatModels?: string[];
|
||||||
|
name: string;
|
||||||
|
avatar: string;
|
||||||
|
intro: string;
|
||||||
|
canUse?: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ---------- history ----------- */
|
||||||
|
export type getHistoriesProps = {
|
||||||
|
appId?: string;
|
||||||
|
// share chat
|
||||||
|
shareId?: string;
|
||||||
|
outLinkUid?: string; // authToken/uid
|
||||||
|
};
|
||||||
|
|
||||||
|
export type UpdateHistoryProps = {
|
||||||
|
chatId: string;
|
||||||
|
customTitle?: string;
|
||||||
|
top?: boolean;
|
||||||
|
shareId?: string;
|
||||||
|
outLinkUid?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type DelHistoryProps = {
|
||||||
|
chatId: string;
|
||||||
|
shareId?: string;
|
||||||
|
outLinkUid?: string;
|
||||||
|
};
|
||||||
|
export type ClearHistoriesProps = {
|
||||||
|
appId?: string;
|
||||||
|
shareId?: string;
|
||||||
|
outLinkUid?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* -------- chat item ---------- */
|
||||||
|
export type DeleteChatItemProps = {
|
||||||
|
chatId: string;
|
||||||
|
contentId: string;
|
||||||
|
shareId?: string;
|
||||||
|
outLinkUid?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AdminUpdateFeedbackParams = AdminFbkType & {
|
||||||
|
chatItemId: string;
|
||||||
|
};
|
||||||
|
|||||||
15
projects/app/src/global/core/chat/constants.ts
Normal file
15
projects/app/src/global/core/chat/constants.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { InitChatResponse } from './api';
|
||||||
|
|
||||||
|
export const defaultChatData: InitChatResponse = {
|
||||||
|
chatId: '',
|
||||||
|
appId: '',
|
||||||
|
app: {
|
||||||
|
name: 'Loading',
|
||||||
|
avatar: '/icon/logo.svg',
|
||||||
|
intro: '',
|
||||||
|
canUse: false
|
||||||
|
},
|
||||||
|
title: '新对话',
|
||||||
|
variables: {},
|
||||||
|
history: []
|
||||||
|
};
|
||||||
@ -33,9 +33,8 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
|||||||
"""
|
"""
|
||||||
回答要求:
|
回答要求:
|
||||||
1. 优先使用知识库内容回答问题。
|
1. 优先使用知识库内容回答问题。
|
||||||
2. 你可以回答我不知道。
|
2. 不要提及你是从知识库获取的知识。
|
||||||
3. 不要提及你是从知识库获取的知识。
|
3. 知识库包含 markdown 内容时,按 markdown 格式返回。
|
||||||
4. 知识库包含 markdown 内容时,按 markdown 格式返回。
|
|
||||||
我的问题是:"{{question}}"`
|
我的问题是:"{{question}}"`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -47,9 +46,8 @@ export const Prompt_QuotePromptList: PromptTemplateItem[] = [
|
|||||||
"""
|
"""
|
||||||
回答要求:
|
回答要求:
|
||||||
1. 优先使用知识库内容回答问题,其中 instruction 是相关介绍,output 是预期回答或补充。
|
1. 优先使用知识库内容回答问题,其中 instruction 是相关介绍,output 是预期回答或补充。
|
||||||
2. 你可以回答我不知道。
|
2. 不要提及你是从知识库获取的知识。
|
||||||
3. 不要提及你是从知识库获取的知识。
|
3. 知识库包含 markdown 内容时,按 markdown 格式返回。
|
||||||
4. 知识库包含 markdown 内容时,按 markdown 格式返回。
|
|
||||||
我的问题是:"{{question}}"`
|
我的问题是:"{{question}}"`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -54,7 +54,7 @@ const PayModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
onSuccess(res) {
|
onSuccess(res) {
|
||||||
if (!res) return;
|
if (!res) return;
|
||||||
toast({
|
toast({
|
||||||
title: '充值成功',
|
title: res,
|
||||||
status: 'success'
|
status: 'success'
|
||||||
});
|
});
|
||||||
router.reload();
|
router.reload();
|
||||||
|
|||||||
@ -1,32 +1,14 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
import { jsonRes } from '@fastgpt/service/common/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
|
||||||
import { startQueue } from '@/service/utils/tools';
|
import { startQueue } from '@/service/utils/tools';
|
||||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
const { userId } = await authCert({ req, authToken: true });
|
await authCert({ req, authToken: true });
|
||||||
await unlockTask(userId);
|
startQueue();
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
jsonRes(res);
|
jsonRes(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function unlockTask(userId: string) {
|
|
||||||
try {
|
|
||||||
await MongoDatasetTraining.updateMany(
|
|
||||||
{
|
|
||||||
userId
|
|
||||||
},
|
|
||||||
{
|
|
||||||
lockTime: new Date('2000/1/1')
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
startQueue();
|
|
||||||
} catch (error) {
|
|
||||||
unlockTask(userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
|||||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||||
|
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||||
|
|
||||||
/* 获取我的模型 */
|
/* 获取我的模型 */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
@ -20,6 +21,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
await authApp({ req, authToken: true, appId, per: 'owner' });
|
await authApp({ req, authToken: true, appId, per: 'owner' });
|
||||||
|
|
||||||
// 删除对应的聊天
|
// 删除对应的聊天
|
||||||
|
await MongoChatItem.deleteMany({
|
||||||
|
appId
|
||||||
|
});
|
||||||
await MongoChat.deleteMany({
|
await MongoChat.deleteMany({
|
||||||
appId
|
appId
|
||||||
});
|
});
|
||||||
|
|||||||
@ -381,7 +381,7 @@ function datasetTemplate({
|
|||||||
key: 'similarity',
|
key: 'similarity',
|
||||||
value: 0.4,
|
value: 0.4,
|
||||||
type: FlowNodeInputTypeEnum.slider,
|
type: FlowNodeInputTypeEnum.slider,
|
||||||
label: '相似度',
|
label: '相关度',
|
||||||
connected: true
|
connected: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -289,7 +289,7 @@ function datasetTemplate(formData: AppSimpleEditFormType): ModuleItemType[] {
|
|||||||
key: 'similarity',
|
key: 'similarity',
|
||||||
value: formData.dataset.similarity,
|
value: formData.dataset.similarity,
|
||||||
type: FlowNodeInputTypeEnum.slider,
|
type: FlowNodeInputTypeEnum.slider,
|
||||||
label: '相似度',
|
label: '相关度',
|
||||||
connected: false
|
connected: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,8 +8,9 @@ import { pushChatBill } from '@/service/support/wallet/bill/push';
|
|||||||
import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
import { BillSourceEnum } from '@fastgpt/global/support/wallet/bill/constants';
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
|
import type { ChatItemType } from '@fastgpt/global/core/chat/type';
|
||||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||||
import { authUser } from '@/service/support/permission/auth/user';
|
|
||||||
import { dispatchModules } from '@/service/moduleDispatch';
|
import { dispatchModules } from '@/service/moduleDispatch';
|
||||||
|
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||||
|
import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller';
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
history: ChatItemType[];
|
history: ChatItemType[];
|
||||||
@ -40,15 +41,20 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* user auth */
|
/* user auth */
|
||||||
const [{ teamId, tmbId }, { user }] = await Promise.all([
|
const [_, { teamId, tmbId }] = await Promise.all([
|
||||||
authApp({ req, authToken: true, appId, per: 'r' }),
|
authApp({ req, authToken: true, appId, per: 'r' }),
|
||||||
authUser({
|
authCert({
|
||||||
req,
|
req,
|
||||||
authToken: true,
|
authToken: true
|
||||||
minBalance: 0
|
|
||||||
})
|
})
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// auth balance
|
||||||
|
const user = await getUserAndAuthBalance({
|
||||||
|
tmbId,
|
||||||
|
minBalance: 0
|
||||||
|
});
|
||||||
|
|
||||||
/* start process */
|
/* start process */
|
||||||
const { responseData } = await dispatchModules({
|
const { responseData } = await dispatchModules({
|
||||||
res,
|
res,
|
||||||
|
|||||||
58
projects/app/src/pages/api/core/chat/clearHistories.ts
Normal file
58
projects/app/src/pages/api/core/chat/clearHistories.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { jsonRes } from '@fastgpt/service/common/response';
|
||||||
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
|
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||||
|
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||||
|
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||||
|
import { ClearHistoriesProps } from '@/global/core/chat/api';
|
||||||
|
import { authOutLink } from '@/service/support/permission/auth/outLink';
|
||||||
|
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
|
|
||||||
|
/* clear chat history */
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
try {
|
||||||
|
await connectToDatabase();
|
||||||
|
const { appId, shareId, outLinkUid } = req.query as ClearHistoriesProps;
|
||||||
|
|
||||||
|
const match = await (async () => {
|
||||||
|
if (shareId && outLinkUid) {
|
||||||
|
const { uid } = await authOutLink({ shareId, outLinkUid });
|
||||||
|
|
||||||
|
return {
|
||||||
|
shareId,
|
||||||
|
outLinkUid: uid
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (appId) {
|
||||||
|
const { tmbId } = await authCert({ req, authToken: true });
|
||||||
|
|
||||||
|
return {
|
||||||
|
appId,
|
||||||
|
tmbId,
|
||||||
|
source: ChatSourceEnum.online
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject('Param are error');
|
||||||
|
})();
|
||||||
|
console.log(match);
|
||||||
|
|
||||||
|
// find chatIds
|
||||||
|
const list = await MongoChat.find(match, 'chatId').lean();
|
||||||
|
const idList = list.map((item) => item.chatId);
|
||||||
|
|
||||||
|
await MongoChatItem.deleteMany({
|
||||||
|
chatId: { $in: idList }
|
||||||
|
});
|
||||||
|
await MongoChat.deleteMany({
|
||||||
|
chatId: { $in: idList }
|
||||||
|
});
|
||||||
|
|
||||||
|
jsonRes(res);
|
||||||
|
} catch (err) {
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
38
projects/app/src/pages/api/core/chat/delHistory.ts
Normal file
38
projects/app/src/pages/api/core/chat/delHistory.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { jsonRes } from '@fastgpt/service/common/response';
|
||||||
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
|
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||||
|
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||||
|
import { DelHistoryProps } from '@/global/core/chat/api';
|
||||||
|
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||||
|
|
||||||
|
/* clear chat history */
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
try {
|
||||||
|
await connectToDatabase();
|
||||||
|
const { chatId, shareId, outLinkUid } = req.query as DelHistoryProps;
|
||||||
|
|
||||||
|
await autChatCrud({
|
||||||
|
req,
|
||||||
|
authToken: true,
|
||||||
|
chatId,
|
||||||
|
shareId,
|
||||||
|
outLinkUid,
|
||||||
|
per: 'w'
|
||||||
|
});
|
||||||
|
|
||||||
|
await MongoChatItem.deleteMany({
|
||||||
|
chatId
|
||||||
|
});
|
||||||
|
await MongoChat.findOneAndRemove({
|
||||||
|
chatId
|
||||||
|
});
|
||||||
|
|
||||||
|
jsonRes(res);
|
||||||
|
} catch (err) {
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,54 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
|
||||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
|
||||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
|
||||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
|
||||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
chatId?: string;
|
|
||||||
appId?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* clear chat history */
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
try {
|
|
||||||
await connectToDatabase();
|
|
||||||
const { chatId, appId } = req.query as Props;
|
|
||||||
|
|
||||||
const { tmbId } = await authCert({ req, authToken: true });
|
|
||||||
|
|
||||||
if (chatId) {
|
|
||||||
await MongoChatItem.deleteMany({
|
|
||||||
chatId,
|
|
||||||
tmbId
|
|
||||||
});
|
|
||||||
await MongoChat.findOneAndRemove({
|
|
||||||
chatId,
|
|
||||||
tmbId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (appId) {
|
|
||||||
const chats = await MongoChat.find({
|
|
||||||
appId,
|
|
||||||
tmbId,
|
|
||||||
source: ChatSourceEnum.online
|
|
||||||
}).select('_id');
|
|
||||||
const chatIds = chats.map((chat) => chat._id);
|
|
||||||
await MongoChatItem.deleteMany({
|
|
||||||
chatId: { $in: chatIds }
|
|
||||||
});
|
|
||||||
await MongoChat.deleteMany({
|
|
||||||
_id: { $in: chatIds }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonRes(res);
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
import { jsonRes } from '@fastgpt/service/common/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||||
import type { AdminUpdateFeedbackParams } from '@fastgpt/global/core/chat/api.d';
|
import type { AdminUpdateFeedbackParams } from '@/global/core/chat/api.d';
|
||||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||||
|
|
||||||
/* 初始化我的聊天框,需要身份验证 */
|
/* 初始化我的聊天框,需要身份验证 */
|
||||||
|
|||||||
62
projects/app/src/pages/api/core/chat/getHistories.ts
Normal file
62
projects/app/src/pages/api/core/chat/getHistories.ts
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { jsonRes } from '@fastgpt/service/common/response';
|
||||||
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
|
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||||
|
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
|
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
|
import { getHistoriesProps } from '@/global/core/chat/api';
|
||||||
|
import { authOutLink } from '@/service/support/permission/auth/outLink';
|
||||||
|
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||||
|
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
try {
|
||||||
|
await connectToDatabase();
|
||||||
|
const { appId, shareId, outLinkUid } = req.body as getHistoriesProps;
|
||||||
|
|
||||||
|
const limit = shareId && outLinkUid ? 20 : 30;
|
||||||
|
|
||||||
|
const match = await (async () => {
|
||||||
|
if (shareId && outLinkUid) {
|
||||||
|
const { uid } = await authOutLink({ shareId, outLinkUid });
|
||||||
|
|
||||||
|
return {
|
||||||
|
shareId,
|
||||||
|
outLinkUid: uid,
|
||||||
|
source: ChatSourceEnum.share,
|
||||||
|
updateTime: {
|
||||||
|
$gte: new Date(new Date().setDate(new Date().getDate() - 30))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (appId) {
|
||||||
|
const { tmbId } = await authCert({ req, authToken: true });
|
||||||
|
return {
|
||||||
|
appId,
|
||||||
|
tmbId,
|
||||||
|
source: ChatSourceEnum.online
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return Promise.reject('Params are error');
|
||||||
|
})();
|
||||||
|
|
||||||
|
const data = await MongoChat.find(match, 'chatId title top customTitle appId updateTime')
|
||||||
|
.sort({ top: -1, updateTime: -1 })
|
||||||
|
.limit(limit);
|
||||||
|
|
||||||
|
jsonRes<ChatHistoryItemType[]>(res, {
|
||||||
|
data: data.map((item) => ({
|
||||||
|
chatId: item.chatId,
|
||||||
|
updateTime: item.updateTime,
|
||||||
|
appId: item.appId,
|
||||||
|
customTitle: item.customTitle,
|
||||||
|
title: item.title,
|
||||||
|
top: item.top
|
||||||
|
}))
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,24 +1,20 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
import { jsonRes } from '@fastgpt/service/common/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
|
||||||
import type { InitChatResponse } from '@fastgpt/global/core/chat/api.d';
|
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
||||||
import { getGuideModule } from '@fastgpt/global/core/module/utils';
|
import { getGuideModule } from '@fastgpt/global/core/module/utils';
|
||||||
import { getChatModelNameListByModules } from '@/service/core/app/module';
|
import { getChatModelNameListByModules } from '@/service/core/app/module';
|
||||||
import { authChat } from '@fastgpt/service/support/permission/auth/chat';
|
|
||||||
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||||
|
import type { InitChatProps, InitChatResponse } from '@/global/core/chat/api.d';
|
||||||
|
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||||
|
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||||
|
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||||
|
|
||||||
/* 初始化我的聊天框,需要身份验证 */
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
|
|
||||||
let { appId, chatId } = req.query as {
|
let { appId, chatId } = req.query as InitChatProps;
|
||||||
appId: string;
|
|
||||||
chatId: '' | string;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!appId) {
|
if (!appId) {
|
||||||
return jsonRes(res, {
|
return jsonRes(res, {
|
||||||
@ -27,57 +23,44 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验使用权限
|
// auth app permission
|
||||||
const [{ app }, autChatResult] = await Promise.all([
|
const [{ app, tmbId }, chat] = await Promise.all([
|
||||||
authApp({
|
authApp({
|
||||||
req,
|
req,
|
||||||
authToken: true,
|
authToken: true,
|
||||||
appId,
|
appId,
|
||||||
per: 'r'
|
per: 'r'
|
||||||
}),
|
}),
|
||||||
chatId
|
chatId ? MongoChat.findOne({ chatId }) : undefined
|
||||||
? authChat({
|
|
||||||
req,
|
|
||||||
authToken: true,
|
|
||||||
chatId,
|
|
||||||
per: 'r'
|
|
||||||
})
|
|
||||||
: undefined
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// get app and history
|
// auth chat permission
|
||||||
const { history = [] }: { history?: ChatItemType[] } = await (async () => {
|
if (!app.canWrite && String(tmbId) !== String(chat?.tmbId)) {
|
||||||
if (chatId) {
|
throw new Error(ChatErrEnum.unAuthChat);
|
||||||
// auth chatId
|
}
|
||||||
const history = await MongoChatItem.find(
|
|
||||||
{
|
|
||||||
chatId
|
|
||||||
},
|
|
||||||
`dataId obj value adminFeedback userFeedback ${ModuleOutputKeyEnum.responseData}`
|
|
||||||
)
|
|
||||||
.sort({ _id: -1 })
|
|
||||||
.limit(30);
|
|
||||||
|
|
||||||
history.reverse();
|
// get app and history
|
||||||
return { history };
|
const { history } = await getChatItems({
|
||||||
}
|
chatId,
|
||||||
return {};
|
limit: 30,
|
||||||
})();
|
field: `dataId obj value adminFeedback userFeedback ${ModuleOutputKeyEnum.responseData}`
|
||||||
|
});
|
||||||
|
|
||||||
jsonRes<InitChatResponse>(res, {
|
jsonRes<InitChatResponse>(res, {
|
||||||
data: {
|
data: {
|
||||||
chatId,
|
chatId,
|
||||||
appId,
|
appId,
|
||||||
|
title: chat?.title || '新对话',
|
||||||
|
userAvatar: undefined,
|
||||||
|
variables: chat?.variables || {},
|
||||||
|
history,
|
||||||
app: {
|
app: {
|
||||||
userGuideModule: getGuideModule(app.modules),
|
userGuideModule: getGuideModule(app.modules),
|
||||||
chatModels: getChatModelNameListByModules(app.modules),
|
chatModels: getChatModelNameListByModules(app.modules),
|
||||||
name: app.name,
|
name: app.name,
|
||||||
avatar: app.avatar,
|
avatar: app.avatar,
|
||||||
intro: app.intro
|
intro: app.intro
|
||||||
},
|
}
|
||||||
title: autChatResult?.chat?.title || '新对话',
|
|
||||||
variables: autChatResult?.chat?.variables || {},
|
|
||||||
history
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { jsonRes } from '@fastgpt/service/common/response';
|
||||||
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
|
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||||
|
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
|
|
||||||
|
/* clear chat history */
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
try {
|
||||||
|
await connectToDatabase();
|
||||||
|
const { outLinkUid, chatIds } = req.body as {
|
||||||
|
outLinkUid: string;
|
||||||
|
chatIds: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!outLinkUid) {
|
||||||
|
throw new Error('shareId or outLinkUid is required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const sliceIds = chatIds.slice(0, 50);
|
||||||
|
|
||||||
|
await MongoChat.updateMany(
|
||||||
|
{
|
||||||
|
chatId: { $in: sliceIds },
|
||||||
|
source: ChatSourceEnum.share,
|
||||||
|
outLinkUid: { $exists: false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
outLinkUid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
jsonRes(res);
|
||||||
|
} catch (err) {
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,14 +2,22 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
import { jsonRes } from '@fastgpt/service/common/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||||
import { authChat } from '@fastgpt/service/support/permission/auth/chat';
|
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||||
|
import type { DeleteChatItemProps } from '@/global/core/chat/api.d';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
const { chatId, contentId } = req.query as { chatId: string; contentId: string };
|
const { chatId, contentId, shareId, outLinkUid } = req.query as DeleteChatItemProps;
|
||||||
|
|
||||||
await authChat({ req, authToken: true, chatId, per: 'w' });
|
await autChatCrud({
|
||||||
|
req,
|
||||||
|
authToken: true,
|
||||||
|
chatId,
|
||||||
|
shareId,
|
||||||
|
outLinkUid,
|
||||||
|
per: 'w'
|
||||||
|
});
|
||||||
|
|
||||||
await MongoChatItem.deleteOne({
|
await MongoChatItem.deleteOne({
|
||||||
dataId: contentId,
|
dataId: contentId,
|
||||||
|
|||||||
@ -1,43 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
|
||||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
|
||||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
|
||||||
import { authApp } from '@fastgpt/service/support/permission/auth/app';
|
|
||||||
|
|
||||||
/* 获取历史记录 */
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
try {
|
|
||||||
await connectToDatabase();
|
|
||||||
const { appId } = req.body as { appId: string };
|
|
||||||
const { tmbId } = await authApp({ req, authToken: true, appId, per: 'r' });
|
|
||||||
|
|
||||||
const data = await MongoChat.find(
|
|
||||||
{
|
|
||||||
appId,
|
|
||||||
tmbId,
|
|
||||||
source: ChatSourceEnum.online
|
|
||||||
},
|
|
||||||
'chatId title top customTitle appId updateTime'
|
|
||||||
)
|
|
||||||
.sort({ top: -1, updateTime: -1 })
|
|
||||||
.limit(20);
|
|
||||||
|
|
||||||
jsonRes<ChatHistoryItemType[]>(res, {
|
|
||||||
data: data.map((item) => ({
|
|
||||||
chatId: item.chatId,
|
|
||||||
updateTime: item.updateTime,
|
|
||||||
appId: item.appId,
|
|
||||||
customTitle: item.customTitle,
|
|
||||||
title: item.title,
|
|
||||||
top: item.top
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
85
projects/app/src/pages/api/core/chat/outLink/init.ts
Normal file
85
projects/app/src/pages/api/core/chat/outLink/init.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
import { jsonRes } from '@fastgpt/service/common/response';
|
||||||
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
|
import type { InitChatResponse, InitOutLinkChatProps } from '@/global/core/chat/api.d';
|
||||||
|
import { getGuideModule } from '@fastgpt/global/core/module/utils';
|
||||||
|
import { getChatModelNameListByModules } from '@/service/core/app/module';
|
||||||
|
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||||
|
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||||
|
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
||||||
|
import { authOutLink } from '@/service/support/permission/auth/outLink';
|
||||||
|
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||||
|
import { selectShareResponse } from '@/utils/service/core/chat';
|
||||||
|
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||||
|
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||||
|
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||||
|
|
||||||
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
try {
|
||||||
|
await connectToDatabase();
|
||||||
|
|
||||||
|
let { chatId, shareId, outLinkUid } = req.query as InitOutLinkChatProps;
|
||||||
|
|
||||||
|
// auth link permission
|
||||||
|
const { shareChat, uid, appId } = await authOutLink({ shareId, outLinkUid });
|
||||||
|
|
||||||
|
// auth app permission
|
||||||
|
const [tmb, chat, app] = await Promise.all([
|
||||||
|
MongoTeamMember.findById(shareChat.tmbId, '_id userId').populate('userId', 'avatar').lean(),
|
||||||
|
MongoChat.findOne({ chatId, shareId }).lean(),
|
||||||
|
MongoApp.findById(appId).lean()
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!app) {
|
||||||
|
throw new Error(AppErrEnum.unExist);
|
||||||
|
}
|
||||||
|
|
||||||
|
// auth chat permission
|
||||||
|
if (chat && chat.outLinkUid !== uid) {
|
||||||
|
throw new Error(ChatErrEnum.unAuthChat);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { history } = await getChatItems({
|
||||||
|
chatId,
|
||||||
|
limit: 30,
|
||||||
|
field: `dataId obj value userFeedback ${
|
||||||
|
shareChat.responseDetail ? `adminFeedback ${ModuleOutputKeyEnum.responseData}` : ''
|
||||||
|
} `
|
||||||
|
});
|
||||||
|
|
||||||
|
// pick share response field
|
||||||
|
history.forEach((item) => {
|
||||||
|
item.responseData = selectShareResponse({ responseData: item.responseData });
|
||||||
|
});
|
||||||
|
|
||||||
|
jsonRes<InitChatResponse>(res, {
|
||||||
|
data: {
|
||||||
|
chatId,
|
||||||
|
appId: app._id,
|
||||||
|
title: chat?.title || '新对话',
|
||||||
|
//@ts-ignore
|
||||||
|
userAvatar: tmb?.userId?.avatar,
|
||||||
|
variables: chat?.variables || {},
|
||||||
|
history,
|
||||||
|
app: {
|
||||||
|
userGuideModule: getGuideModule(app.modules),
|
||||||
|
chatModels: getChatModelNameListByModules(app.modules),
|
||||||
|
name: app.name,
|
||||||
|
avatar: app.avatar,
|
||||||
|
intro: app.intro
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
jsonRes(res, {
|
||||||
|
code: 500,
|
||||||
|
error: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
api: {
|
||||||
|
responseLimit: '10mb'
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -1,17 +1,24 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
import { jsonRes } from '@fastgpt/service/common/response';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { UpdateHistoryProps } from '@fastgpt/global/core/chat/api.d';
|
import { UpdateHistoryProps } from '@/global/core/chat/api.d';
|
||||||
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||||
import { authChat } from '@fastgpt/service/support/permission/auth/chat';
|
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||||
|
|
||||||
/* 更新聊天标题 */
|
/* update chat top, custom title */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
const { chatId, customTitle, top } = req.body as UpdateHistoryProps;
|
const { chatId, shareId, outLinkUid, customTitle, top } = req.body as UpdateHistoryProps;
|
||||||
|
|
||||||
await authChat({ req, authToken: true, chatId });
|
await autChatCrud({
|
||||||
|
req,
|
||||||
|
authToken: true,
|
||||||
|
chatId,
|
||||||
|
shareId,
|
||||||
|
outLinkUid,
|
||||||
|
per: 'w'
|
||||||
|
});
|
||||||
|
|
||||||
await MongoChat.findOneAndUpdate(
|
await MongoChat.findOneAndUpdate(
|
||||||
{ chatId },
|
{ chatId },
|
||||||
@ -8,7 +8,7 @@ import { authDataset } from '@fastgpt/service/support/permission/auth/dataset';
|
|||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
await connectToDatabase();
|
await connectToDatabase();
|
||||||
const { id, parentId, name, avatar, tags, permission, agentModel, websiteConfig, status } =
|
const { id, parentId, name, avatar, intro, permission, agentModel, websiteConfig, status } =
|
||||||
req.body as DatasetUpdateBody;
|
req.body as DatasetUpdateBody;
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
@ -26,11 +26,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
...(parentId !== undefined && { parentId: parentId || null }),
|
...(parentId !== undefined && { parentId: parentId || null }),
|
||||||
...(name && { name }),
|
...(name && { name }),
|
||||||
...(avatar && { avatar }),
|
...(avatar && { avatar }),
|
||||||
...(tags && { tags }),
|
|
||||||
...(permission && { permission }),
|
...(permission && { permission }),
|
||||||
...(agentModel && { agentModel: agentModel.model }),
|
...(agentModel && { agentModel: agentModel.model }),
|
||||||
...(websiteConfig && { websiteConfig }),
|
...(websiteConfig && { websiteConfig }),
|
||||||
...(status && { status })
|
...(status && { status }),
|
||||||
|
...(intro && { intro })
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -1,51 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
|
||||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
|
||||||
import type { InitShareChatResponse } from '@fastgpt/global/support/outLink/api.d';
|
|
||||||
import { HUMAN_ICON } from '@fastgpt/global/core/chat/constants';
|
|
||||||
import { getGuideModule } from '@fastgpt/global/core/module/utils';
|
|
||||||
import { authShareChatInit } from '@/service/support/outLink/auth';
|
|
||||||
import { getChatModelNameListByModules } from '@/service/core/app/module';
|
|
||||||
import { authOutLinkValid } from '@fastgpt/service/support/permission/auth/outLink';
|
|
||||||
|
|
||||||
/* init share chat window */
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
try {
|
|
||||||
await connectToDatabase();
|
|
||||||
let { shareId, authToken } = req.query as {
|
|
||||||
shareId: string;
|
|
||||||
authToken?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
// get shareChat
|
|
||||||
const { app, shareChat } = await authOutLinkValid({ shareId });
|
|
||||||
|
|
||||||
// 校验使用权限
|
|
||||||
const [user] = await Promise.all([
|
|
||||||
MongoUser.findById(shareChat.userId, 'avatar'),
|
|
||||||
authShareChatInit({
|
|
||||||
authToken,
|
|
||||||
tokenUrl: shareChat.limit?.hookUrl
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
|
|
||||||
jsonRes<InitShareChatResponse>(res, {
|
|
||||||
data: {
|
|
||||||
userAvatar: user?.avatar || HUMAN_ICON,
|
|
||||||
app: {
|
|
||||||
userGuideModule: getGuideModule(app.modules),
|
|
||||||
chatModels: getChatModelNameListByModules(app.modules),
|
|
||||||
name: app.name,
|
|
||||||
avatar: app.avatar,
|
|
||||||
intro: app.intro
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,7 +3,7 @@ import { jsonRes } from '@fastgpt/service/common/response';
|
|||||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
||||||
import { createJWT, setCookie } from '@fastgpt/service/support/permission/controller';
|
import { createJWT, setCookie } from '@fastgpt/service/support/permission/controller';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { getUserDetail } from '@/service/support/user/controller';
|
import { getUserDetail } from '@fastgpt/service/support/user/controller';
|
||||||
import type { PostLoginProps } from '@fastgpt/global/support/user/api.d';
|
import type { PostLoginProps } from '@fastgpt/global/support/user/api.d';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
|
|||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
import { jsonRes } from '@fastgpt/service/common/response';
|
||||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { getUserDetail } from '@/service/support/user/controller';
|
import { getUserDetail } from '@fastgpt/service/support/user/controller';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
import { jsonRes } from '@fastgpt/service/common/response';
|
||||||
import { authCert, authCertAndShareId } from '@fastgpt/service/support/permission/auth/common';
|
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||||
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
import { withNextCors } from '@fastgpt/service/common/middle/cors';
|
||||||
import { getUploadModel } from '@fastgpt/service/common/file/upload/multer';
|
import { getUploadModel } from '@fastgpt/service/common/file/upload/multer';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
|||||||
@ -10,11 +10,11 @@ import { dispatchModules } from '@/service/moduleDispatch';
|
|||||||
import type { ChatCompletionCreateParams } from '@fastgpt/global/core/ai/type.d';
|
import type { ChatCompletionCreateParams } from '@fastgpt/global/core/ai/type.d';
|
||||||
import type { ChatMessageItemType } from '@fastgpt/global/core/ai/type.d';
|
import type { ChatMessageItemType } from '@fastgpt/global/core/ai/type.d';
|
||||||
import { gptMessage2ChatType, textAdaptGptResponse } from '@/utils/adapt';
|
import { gptMessage2ChatType, textAdaptGptResponse } from '@/utils/adapt';
|
||||||
import { getChatHistory } from './getHistory';
|
import { getChatItems } from '@fastgpt/service/core/chat/controller';
|
||||||
import { saveChat } from '@/service/utils/chat/saveChat';
|
import { saveChat } from '@/service/utils/chat/saveChat';
|
||||||
import { responseWrite } from '@fastgpt/service/common/response';
|
import { responseWrite } from '@fastgpt/service/common/response';
|
||||||
import { pushChatBill } from '@/service/support/wallet/bill/push';
|
import { pushChatBill } from '@/service/support/wallet/bill/push';
|
||||||
import { authOutLinkChat } from '@/service/support/permission/auth/outLink';
|
import { authOutLinkChatStart } from '@/service/support/permission/auth/outLink';
|
||||||
import { pushResult2Remote, updateOutLinkUsage } from '@fastgpt/service/support/outLink/tools';
|
import { pushResult2Remote, updateOutLinkUsage } from '@fastgpt/service/support/outLink/tools';
|
||||||
import requestIp from 'request-ip';
|
import requestIp from 'request-ip';
|
||||||
import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools';
|
import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/tools';
|
||||||
@ -22,9 +22,10 @@ import { getBillSourceByAuthType } from '@fastgpt/global/support/wallet/bill/too
|
|||||||
import { selectShareResponse } from '@/utils/service/core/chat';
|
import { selectShareResponse } from '@/utils/service/core/chat';
|
||||||
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
|
import { updateApiKeyUsage } from '@fastgpt/service/support/openapi/tools';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { getUserAndAuthBalance } from '@/service/support/permission/auth/user';
|
import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller';
|
||||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||||
|
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||||
|
|
||||||
type FastGptWebChatProps = {
|
type FastGptWebChatProps = {
|
||||||
chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history
|
chatId?: string; // undefined: nonuse history, '': new chat, 'xxxxx': use history
|
||||||
@ -32,7 +33,7 @@ type FastGptWebChatProps = {
|
|||||||
};
|
};
|
||||||
type FastGptShareChatProps = {
|
type FastGptShareChatProps = {
|
||||||
shareId?: string;
|
shareId?: string;
|
||||||
authToken?: string;
|
outLinkUid?: string;
|
||||||
};
|
};
|
||||||
export type Props = ChatCompletionCreateParams &
|
export type Props = ChatCompletionCreateParams &
|
||||||
FastGptWebChatProps &
|
FastGptWebChatProps &
|
||||||
@ -56,11 +57,11 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
|
|
||||||
let {
|
const {
|
||||||
chatId,
|
chatId,
|
||||||
appId,
|
appId,
|
||||||
shareId,
|
shareId,
|
||||||
authToken,
|
outLinkUid,
|
||||||
stream = false,
|
stream = false,
|
||||||
detail = false,
|
detail = false,
|
||||||
messages = [],
|
messages = [],
|
||||||
@ -93,22 +94,29 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
throw new Error('Question is empty');
|
throw new Error('Question is empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* auth app permission */
|
/* auth app permission */
|
||||||
const { user, app, responseDetail, authType, apikey, canWrite } = await (async () => {
|
const { user, app, responseDetail, authType, apikey, canWrite, uid } = await (async () => {
|
||||||
if (shareId) {
|
if (shareId && outLinkUid) {
|
||||||
const { user, app, authType, responseDetail } = await authOutLinkChat({
|
const { user, appId, authType, responseDetail, uid } = await authOutLinkChatStart({
|
||||||
shareId,
|
shareId,
|
||||||
ip: requestIp.getClientIp(req),
|
ip: requestIp.getClientIp(req),
|
||||||
authToken,
|
outLinkUid,
|
||||||
question: question.value
|
question: question.value
|
||||||
});
|
});
|
||||||
|
const app = await MongoApp.findById(appId);
|
||||||
|
|
||||||
|
if (!app) {
|
||||||
|
return Promise.reject('app is empty');
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
user,
|
user,
|
||||||
app,
|
app,
|
||||||
responseDetail,
|
responseDetail,
|
||||||
apikey: '',
|
apikey: '',
|
||||||
authType,
|
authType,
|
||||||
canWrite: false
|
canWrite: false,
|
||||||
|
uid
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,11 +154,10 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// token auth
|
||||||
if (!appId) {
|
if (!appId) {
|
||||||
return Promise.reject('appId is empty');
|
return Promise.reject('appId is empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
// token
|
|
||||||
const { app, canWrite } = await authApp({
|
const { app, canWrite } = await authApp({
|
||||||
req,
|
req,
|
||||||
authToken: true,
|
authToken: true,
|
||||||
@ -168,12 +175,19 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// auth app, get history
|
// auth chat permission
|
||||||
const { history } = await getChatHistory({ chatId, tmbId: user.team.tmbId });
|
await autChatCrud({
|
||||||
|
req,
|
||||||
|
authToken: true,
|
||||||
|
authApiKey: true,
|
||||||
|
chatId,
|
||||||
|
shareId,
|
||||||
|
outLinkUid,
|
||||||
|
per: 'w'
|
||||||
|
});
|
||||||
|
|
||||||
const isAppOwner = !shareId && String(user.team.tmbId) === String(app.tmbId);
|
// get and concat history
|
||||||
|
const { history } = await getChatItems({ chatId, limit: 30, field: `dataId obj value` });
|
||||||
/* format prompts */
|
|
||||||
const concatHistory = history.concat(chatMessages);
|
const concatHistory = history.concat(chatMessages);
|
||||||
|
|
||||||
/* start flow controller */
|
/* start flow controller */
|
||||||
@ -202,8 +216,9 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
teamId: user.team.teamId,
|
teamId: user.team.teamId,
|
||||||
tmbId: user.team.tmbId,
|
tmbId: user.team.tmbId,
|
||||||
variables,
|
variables,
|
||||||
updateUseTime: isAppOwner, // owner update use time
|
updateUseTime: !shareId && String(user.team.tmbId) === String(app.tmbId), // owner update use time
|
||||||
shareId,
|
shareId,
|
||||||
|
outLinkUid: uid,
|
||||||
source: (() => {
|
source: (() => {
|
||||||
if (shareId) {
|
if (shareId) {
|
||||||
return ChatSourceEnum.share;
|
return ChatSourceEnum.share;
|
||||||
@ -281,7 +296,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (shareId) {
|
if (shareId) {
|
||||||
pushResult2Remote({ authToken, shareId, responseData });
|
pushResult2Remote({ outLinkUid, shareId, responseData });
|
||||||
updateOutLinkUsage({
|
updateOutLinkUsage({
|
||||||
shareId,
|
shareId,
|
||||||
total
|
total
|
||||||
|
|||||||
@ -1,73 +0,0 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
|
||||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
|
||||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
|
||||||
import { Types } from '@fastgpt/service/common/mongo';
|
|
||||||
import type { ChatItemType } from '@fastgpt/global/core/chat/type.d';
|
|
||||||
|
|
||||||
export type Props = {
|
|
||||||
appId?: string;
|
|
||||||
chatId?: string;
|
|
||||||
limit?: number;
|
|
||||||
};
|
|
||||||
export type Response = { history: ChatItemType[] };
|
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|
||||||
try {
|
|
||||||
await connectToDatabase();
|
|
||||||
const { tmbId } = await authCert({ req, authToken: true });
|
|
||||||
const { chatId, limit } = req.body as Props;
|
|
||||||
|
|
||||||
jsonRes<Response>(res, {
|
|
||||||
data: await getChatHistory({
|
|
||||||
chatId,
|
|
||||||
tmbId,
|
|
||||||
limit
|
|
||||||
})
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getChatHistory({
|
|
||||||
chatId,
|
|
||||||
tmbId,
|
|
||||||
limit = 30
|
|
||||||
}: Props & { tmbId: string }): Promise<Response> {
|
|
||||||
if (!chatId) {
|
|
||||||
return { history: [] };
|
|
||||||
}
|
|
||||||
|
|
||||||
const history = await MongoChatItem.aggregate([
|
|
||||||
{
|
|
||||||
$match: {
|
|
||||||
chatId,
|
|
||||||
tmbId: new Types.ObjectId(tmbId)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$sort: {
|
|
||||||
_id: -1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$limit: limit
|
|
||||||
},
|
|
||||||
{
|
|
||||||
$project: {
|
|
||||||
dataId: 1,
|
|
||||||
obj: 1,
|
|
||||||
value: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
|
|
||||||
history.reverse();
|
|
||||||
|
|
||||||
return { history };
|
|
||||||
}
|
|
||||||
@ -23,7 +23,7 @@ import { AppLogsListItemType } from '@/types/app';
|
|||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import ChatBox, { type ComponentRef } from '@/components/ChatBox';
|
import ChatBox, { type ComponentRef } from '@/components/ChatBox';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { getInitChatSiteInfo } from '@/web/core/chat/api';
|
import { getInitChatInfo } from '@/web/core/chat/api';
|
||||||
import Tag from '@/components/Tag';
|
import Tag from '@/components/Tag';
|
||||||
import MyModal from '@/components/MyModal';
|
import MyModal from '@/components/MyModal';
|
||||||
import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker';
|
import DateRangePicker, { type DateRangeType } from '@/components/DateRangePicker';
|
||||||
@ -199,7 +199,7 @@ function DetailLogsModal({
|
|||||||
|
|
||||||
const { data: chat } = useQuery(
|
const { data: chat } = useQuery(
|
||||||
['getChatDetail', chatId],
|
['getChatDetail', chatId],
|
||||||
() => getInitChatSiteInfo({ appId, chatId }),
|
() => getInitChatInfo({ appId, chatId }),
|
||||||
{
|
{
|
||||||
onSuccess(res) {
|
onSuccess(res) {
|
||||||
const history = res.history.map((item) => ({
|
const history = res.history.map((item) => ({
|
||||||
|
|||||||
@ -104,7 +104,7 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
|
|||||||
>
|
>
|
||||||
<Flex mb={4} alignItems={'center'}>
|
<Flex mb={4} alignItems={'center'}>
|
||||||
<Avatar src={appDetail.avatar} w={'34px'} borderRadius={'lg'} />
|
<Avatar src={appDetail.avatar} w={'34px'} borderRadius={'lg'} />
|
||||||
<Box ml={2} fontWeight={'bold'}>
|
<Box ml={2} fontWeight={'bold'} fontSize={'sm'}>
|
||||||
{appDetail.name}
|
{appDetail.name}
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -55,7 +55,7 @@ const ChatHistorySlider = ({
|
|||||||
history: HistoryItemType[];
|
history: HistoryItemType[];
|
||||||
activeChatId: string;
|
activeChatId: string;
|
||||||
onChangeChat: (chatId?: string) => void;
|
onChangeChat: (chatId?: string) => void;
|
||||||
onDelHistory: (chatId: string) => void;
|
onDelHistory: (e: { chatId: string }) => void;
|
||||||
onClearHistory: () => void;
|
onClearHistory: () => void;
|
||||||
onSetHistoryTop?: (e: { chatId: string; top: boolean }) => void;
|
onSetHistoryTop?: (e: { chatId: string; top: boolean }) => void;
|
||||||
onSetCustomTitle?: (e: { chatId: string; title: string }) => void;
|
onSetCustomTitle?: (e: { chatId: string; title: string }) => void;
|
||||||
@ -261,7 +261,7 @@ const ChatHistorySlider = ({
|
|||||||
_hover={{ color: 'red.500' }}
|
_hover={{ color: 'red.500' }}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
onDelHistory(item.id);
|
onDelHistory({ chatId: item.id });
|
||||||
if (item.id === activeChatId) {
|
if (item.id === activeChatId) {
|
||||||
onChangeChat();
|
onChangeChat();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useCallback, useRef } from 'react';
|
import React, { useCallback, useRef } from 'react';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { getInitChatSiteInfo, delChatRecordById, putChatHistory } from '@/web/core/chat/api';
|
import { getInitChatInfo, putChatHistory } from '@/web/core/chat/api';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Flex,
|
Flex,
|
||||||
@ -34,6 +34,7 @@ import { serviceSideProps } from '@/web/common/utils/i18n';
|
|||||||
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
import { useAppStore } from '@/web/core/app/store/useAppStore';
|
||||||
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
||||||
import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils';
|
import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils';
|
||||||
|
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
|
|
||||||
const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@ -49,13 +50,15 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
setLastChatAppId,
|
setLastChatAppId,
|
||||||
lastChatId,
|
lastChatId,
|
||||||
setLastChatId,
|
setLastChatId,
|
||||||
history,
|
histories,
|
||||||
loadHistory,
|
loadHistories,
|
||||||
|
pushHistory,
|
||||||
updateHistory,
|
updateHistory,
|
||||||
delHistory,
|
delOneHistory,
|
||||||
clearHistory,
|
clearHistories,
|
||||||
chatData,
|
chatData,
|
||||||
setChatData
|
setChatData,
|
||||||
|
delOneHistoryItem
|
||||||
} = useChatStore();
|
} = useChatStore();
|
||||||
const { myApps, loadMyApps } = useAppStore();
|
const { myApps, loadMyApps } = useAppStore();
|
||||||
const { userInfo } = useUserStore();
|
const { userInfo } = useUserStore();
|
||||||
@ -85,7 +88,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
prompts[1]?.value?.slice(0, 20) ||
|
prompts[1]?.value?.slice(0, 20) ||
|
||||||
'新对话';
|
'新对话';
|
||||||
|
|
||||||
// update history
|
// new chat
|
||||||
if (completionChatId !== chatId) {
|
if (completionChatId !== chatId) {
|
||||||
const newHistory: ChatHistoryItemType = {
|
const newHistory: ChatHistoryItemType = {
|
||||||
chatId: completionChatId,
|
chatId: completionChatId,
|
||||||
@ -94,7 +97,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
appId,
|
appId,
|
||||||
top: false
|
top: false
|
||||||
};
|
};
|
||||||
updateHistory(newHistory);
|
pushHistory(newHistory);
|
||||||
if (controller.signal.reason !== 'leave') {
|
if (controller.signal.reason !== 'leave') {
|
||||||
forbidRefresh.current = true;
|
forbidRefresh.current = true;
|
||||||
router.replace({
|
router.replace({
|
||||||
@ -105,7 +108,8 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const currentChat = history.find((item) => item.chatId === chatId);
|
// update chat
|
||||||
|
const currentChat = histories.find((item) => item.chatId === chatId);
|
||||||
currentChat &&
|
currentChat &&
|
||||||
updateHistory({
|
updateHistory({
|
||||||
...currentChat,
|
...currentChat,
|
||||||
@ -117,30 +121,12 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
setChatData((state) => ({
|
setChatData((state) => ({
|
||||||
...state,
|
...state,
|
||||||
title: newTitle,
|
title: newTitle,
|
||||||
history: ChatBoxRef.current?.getChatHistory() || state.history
|
history: ChatBoxRef.current?.getChatHistories() || state.history
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
||||||
},
|
},
|
||||||
[appId, chatId, history, router, setChatData, updateHistory]
|
[appId, chatId, histories, pushHistory, router, setChatData, updateHistory]
|
||||||
);
|
|
||||||
|
|
||||||
// del one chat content
|
|
||||||
const delOneHistoryItem = useCallback(
|
|
||||||
async ({ contentId, index }: { contentId?: string; index: number }) => {
|
|
||||||
if (!chatId || !contentId) return;
|
|
||||||
|
|
||||||
try {
|
|
||||||
setChatData((state) => ({
|
|
||||||
...state,
|
|
||||||
history: state.history.filter((_, i) => i !== index)
|
|
||||||
}));
|
|
||||||
await delChatRecordById({ chatId, contentId });
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[chatId, setChatData]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// get chat app info
|
// get chat app info
|
||||||
@ -156,10 +142,10 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
loading && setIsLoading(true);
|
loading && setIsLoading(true);
|
||||||
const res = await getInitChatSiteInfo({ appId, chatId });
|
const res = await getInitChatInfo({ appId, chatId });
|
||||||
const history = res.history.map((item) => ({
|
const history = res.history.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
status: 'finish' as any
|
status: ChatStatusEnum.finish
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setChatData({
|
setChatData({
|
||||||
@ -185,8 +171,13 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
});
|
});
|
||||||
if (e?.code === 501) {
|
if (e?.code === 501) {
|
||||||
router.replace('/app/list');
|
router.replace('/app/list');
|
||||||
} else {
|
} else if (chatId) {
|
||||||
router.replace('/chat');
|
router.replace({
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
chatId: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
@ -250,7 +241,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
useQuery(['loadHistory', appId], () => (appId ? loadHistory({ appId }) : null));
|
useQuery(['loadHistories', appId], () => (appId ? loadHistories({ appId }) : null));
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex h={'100%'}>
|
<Flex h={'100%'}>
|
||||||
@ -289,7 +280,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
appAvatar={chatData.app.avatar}
|
appAvatar={chatData.app.avatar}
|
||||||
activeChatId={chatId}
|
activeChatId={chatId}
|
||||||
onClose={onCloseSlider}
|
onClose={onCloseSlider}
|
||||||
history={history.map((item, i) => ({
|
history={histories.map((item, i) => ({
|
||||||
id: item.chatId,
|
id: item.chatId,
|
||||||
title: item.title,
|
title: item.title,
|
||||||
customTitle: item.customTitle,
|
customTitle: item.customTitle,
|
||||||
@ -306,39 +297,24 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
onCloseSlider();
|
onCloseSlider();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onDelHistory={delHistory}
|
onDelHistory={delOneHistory}
|
||||||
onClearHistory={() => {
|
onClearHistory={() => {
|
||||||
clearHistory(appId);
|
clearHistories({ appId });
|
||||||
router.replace({
|
router.replace({
|
||||||
query: {
|
query: {
|
||||||
appId
|
appId
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onSetHistoryTop={async (e) => {
|
onSetHistoryTop={(e) => {
|
||||||
try {
|
updateHistory(e);
|
||||||
await putChatHistory(e);
|
|
||||||
const historyItem = history.find((item) => item.chatId === e.chatId);
|
|
||||||
if (!historyItem) return;
|
|
||||||
updateHistory({
|
|
||||||
...historyItem,
|
|
||||||
top: e.top
|
|
||||||
});
|
|
||||||
} catch (error) {}
|
|
||||||
}}
|
}}
|
||||||
onSetCustomTitle={async (e) => {
|
onSetCustomTitle={async (e) => {
|
||||||
try {
|
updateHistory({
|
||||||
putChatHistory({
|
chatId: e.chatId,
|
||||||
chatId: e.chatId,
|
title: e.title,
|
||||||
customTitle: e.title
|
customTitle: e.title
|
||||||
});
|
});
|
||||||
const historyItem = history.find((item) => item.chatId === e.chatId);
|
|
||||||
if (!historyItem) return;
|
|
||||||
updateHistory({
|
|
||||||
...historyItem,
|
|
||||||
customTitle: e.title
|
|
||||||
});
|
|
||||||
} catch (error) {}
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -372,7 +348,7 @@ const Chat = ({ appId, chatId }: { appId: string; chatId: string }) => {
|
|||||||
feedbackType={'user'}
|
feedbackType={'user'}
|
||||||
onUpdateVariable={(e) => {}}
|
onUpdateVariable={(e) => {}}
|
||||||
onStartChat={startChat}
|
onStartChat={startChat}
|
||||||
onDelMessage={delOneHistoryItem}
|
onDelMessage={(e) => delOneHistoryItem({ ...e, chatId })}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -1,17 +1,16 @@
|
|||||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { initShareChatInfo } from '@/web/support/outLink/api';
|
|
||||||
import { Box, Flex, useDisclosure, Drawer, DrawerOverlay, DrawerContent } from '@chakra-ui/react';
|
import { Box, Flex, useDisclosure, Drawer, DrawerOverlay, DrawerContent } from '@chakra-ui/react';
|
||||||
import { useToast } from '@/web/common/hooks/useToast';
|
import { useToast } from '@/web/common/hooks/useToast';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { streamFetch } from '@/web/common/api/fetch';
|
import { streamFetch } from '@/web/common/api/fetch';
|
||||||
import { useShareChatStore, defaultHistory } from '@/web/core/chat/storeShareChat';
|
import { useShareChatStore } from '@/web/core/chat/storeShareChat';
|
||||||
import SideBar from '@/components/SideBar';
|
import SideBar from '@/components/SideBar';
|
||||||
import { gptMessage2ChatType } from '@/utils/adapt';
|
import { gptMessage2ChatType } from '@/utils/adapt';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
import type { ChatSiteItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { ChatHistoryItemType, ChatSiteItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { customAlphabet } from 'nanoid';
|
import { customAlphabet } from 'nanoid';
|
||||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
|
||||||
|
|
||||||
@ -21,6 +20,13 @@ import ChatHeader from './components/ChatHeader';
|
|||||||
import ChatHistorySlider from './components/ChatHistorySlider';
|
import ChatHistorySlider from './components/ChatHistorySlider';
|
||||||
import { serviceSideProps } from '@/web/common/utils/i18n';
|
import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||||
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
import { checkChatSupportSelectFileByChatModels } from '@/web/core/chat/utils';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import { getInitOutLinkChatInfo } from '@/web/core/chat/api';
|
||||||
|
import { POST } from '@/web/common/api/request';
|
||||||
|
import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils';
|
||||||
|
import { useChatStore } from '@/web/core/chat/storeChat';
|
||||||
|
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||||
|
import { OutLinkErrEnum } from '@fastgpt/global/common/error/code/outLink';
|
||||||
|
|
||||||
const OutLink = ({
|
const OutLink = ({
|
||||||
shareId,
|
shareId,
|
||||||
@ -33,6 +39,7 @@ const OutLink = ({
|
|||||||
showHistory: '0' | '1';
|
showHistory: '0' | '1';
|
||||||
authToken?: string;
|
authToken?: string;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
||||||
@ -43,18 +50,23 @@ const OutLink = ({
|
|||||||
const ChatBoxRef = useRef<ComponentRef>(null);
|
const ChatBoxRef = useRef<ComponentRef>(null);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
shareChatData,
|
localUId,
|
||||||
setShareChatData,
|
shareChatHistory, // abandon
|
||||||
shareChatHistory,
|
clearLocalHistory // abandon
|
||||||
saveChatResponse,
|
|
||||||
delShareChatHistoryItemById,
|
|
||||||
delOneShareHistoryByChatId,
|
|
||||||
delManyShareChatHistoryByShareId
|
|
||||||
} = useShareChatStore();
|
} = useShareChatStore();
|
||||||
const history = useMemo(
|
const {
|
||||||
() => shareChatHistory.filter((item) => item.shareId === shareId),
|
histories,
|
||||||
[shareChatHistory, shareId]
|
loadHistories,
|
||||||
);
|
pushHistory,
|
||||||
|
updateHistory,
|
||||||
|
delOneHistory,
|
||||||
|
chatData,
|
||||||
|
setChatData,
|
||||||
|
delOneHistoryItem,
|
||||||
|
clearHistories
|
||||||
|
} = useChatStore();
|
||||||
|
const appId = chatData.appId;
|
||||||
|
const outLinkUid: string = authToken || localUId;
|
||||||
|
|
||||||
const startChat = useCallback(
|
const startChat = useCallback(
|
||||||
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
|
async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => {
|
||||||
@ -67,12 +79,55 @@ const OutLink = ({
|
|||||||
variables,
|
variables,
|
||||||
shareId,
|
shareId,
|
||||||
chatId: completionChatId,
|
chatId: completionChatId,
|
||||||
authToken
|
outLinkUid
|
||||||
},
|
},
|
||||||
onMessage: generatingMessage,
|
onMessage: generatingMessage,
|
||||||
abortSignal: controller
|
abortSignal: controller
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const newTitle =
|
||||||
|
chatContentReplaceBlock(prompts[0].content).slice(0, 20) ||
|
||||||
|
prompts[1]?.value?.slice(0, 20) ||
|
||||||
|
'新对话';
|
||||||
|
|
||||||
|
// new chat
|
||||||
|
if (completionChatId !== chatId) {
|
||||||
|
const newHistory: ChatHistoryItemType = {
|
||||||
|
chatId: completionChatId,
|
||||||
|
updateTime: new Date(),
|
||||||
|
title: newTitle,
|
||||||
|
appId,
|
||||||
|
top: false
|
||||||
|
};
|
||||||
|
pushHistory(newHistory);
|
||||||
|
if (controller.signal.reason !== 'leave') {
|
||||||
|
forbidRefresh.current = true;
|
||||||
|
router.replace({
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
chatId: completionChatId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// update chat
|
||||||
|
const currentChat = histories.find((item) => item.chatId === chatId);
|
||||||
|
currentChat &&
|
||||||
|
updateHistory({
|
||||||
|
...currentChat,
|
||||||
|
updateTime: new Date(),
|
||||||
|
title: newTitle
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// update chat window
|
||||||
|
setChatData((state) => ({
|
||||||
|
...state,
|
||||||
|
title: newTitle,
|
||||||
|
history: ChatBoxRef.current?.getChatHistories() || state.history
|
||||||
|
}));
|
||||||
|
|
||||||
|
/* post message to report result */
|
||||||
const result: ChatSiteItemType[] = gptMessage2ChatType(prompts).map((item) => ({
|
const result: ChatSiteItemType[] = gptMessage2ChatType(prompts).map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
status: 'finish'
|
status: 'finish'
|
||||||
@ -80,24 +135,6 @@ const OutLink = ({
|
|||||||
result[1].value = responseText;
|
result[1].value = responseText;
|
||||||
result[1].responseData = responseData;
|
result[1].responseData = responseData;
|
||||||
|
|
||||||
/* save chat */
|
|
||||||
saveChatResponse({
|
|
||||||
chatId: completionChatId,
|
|
||||||
prompts: result,
|
|
||||||
variables,
|
|
||||||
shareId
|
|
||||||
});
|
|
||||||
|
|
||||||
if (completionChatId !== chatId && controller.signal.reason !== 'leave') {
|
|
||||||
forbidRefresh.current = true;
|
|
||||||
router.replace({
|
|
||||||
query: {
|
|
||||||
...router.query,
|
|
||||||
chatId: completionChatId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.top?.postMessage(
|
window.top?.postMessage(
|
||||||
{
|
{
|
||||||
type: 'shareChatFinish',
|
type: 'shareChatFinish',
|
||||||
@ -111,61 +148,80 @@ const OutLink = ({
|
|||||||
|
|
||||||
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
return { responseText, responseData, isNewChat: forbidRefresh.current };
|
||||||
},
|
},
|
||||||
[authToken, chatId, router, saveChatResponse, shareId]
|
[chatId, shareId, outLinkUid, setChatData, appId, updateHistory, router, histories]
|
||||||
);
|
);
|
||||||
|
|
||||||
const loadAppInfo = useCallback(
|
const loadChatInfo = useCallback(
|
||||||
async (shareId: string, chatId: string, authToken?: string) => {
|
async (shareId: string, chatId: string) => {
|
||||||
if (!shareId) return null;
|
if (!shareId) return null;
|
||||||
const history = shareChatHistory.find((item) => item.chatId === chatId) || defaultHistory;
|
|
||||||
|
|
||||||
ChatBoxRef.current?.resetHistory(history.chats);
|
|
||||||
ChatBoxRef.current?.resetVariables(history.variables);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const chatData = await (async () => {
|
const res = await getInitOutLinkChatInfo({
|
||||||
if (shareChatData.app.name === '') {
|
chatId,
|
||||||
return initShareChatInfo({
|
shareId,
|
||||||
shareId,
|
outLinkUid: authToken || localUId
|
||||||
authToken
|
});
|
||||||
});
|
const history = res.history.map((item) => ({
|
||||||
}
|
...item,
|
||||||
return shareChatData;
|
status: ChatStatusEnum.finish
|
||||||
})();
|
}));
|
||||||
|
|
||||||
setShareChatData({
|
setChatData({
|
||||||
...chatData,
|
...res,
|
||||||
history
|
history
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ChatBoxRef.current?.resetHistory(history);
|
||||||
|
ChatBoxRef.current?.resetVariables(res.variables);
|
||||||
|
|
||||||
|
if (res.history.length > 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
ChatBoxRef.current?.scrollToBottom('auto');
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
toast({
|
toast({
|
||||||
status: 'error',
|
status: 'error',
|
||||||
title: getErrText(e, '获取应用失败')
|
title: getErrText(e, t('core.shareChat.Init Error'))
|
||||||
});
|
});
|
||||||
if (e?.code === 501) {
|
if (chatId) {
|
||||||
delManyShareChatHistoryByShareId(shareId);
|
router.replace({
|
||||||
|
query: {
|
||||||
|
...router.query,
|
||||||
|
chatId: ''
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (e?.statusText === OutLinkErrEnum.linkUnInvalid) {
|
||||||
|
router.replace('/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (history.chats.length > 0) {
|
return null;
|
||||||
setTimeout(() => {
|
|
||||||
ChatBoxRef.current?.scrollToBottom('auto');
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
return history;
|
|
||||||
},
|
},
|
||||||
[delManyShareChatHistoryByShareId, setShareChatData, shareChatData, shareChatHistory, toast]
|
[authToken, localUId, router, setChatData, t, toast]
|
||||||
);
|
);
|
||||||
|
useQuery(['init', shareId, chatId], () => {
|
||||||
useQuery(['init', shareId, chatId, authToken], () => {
|
|
||||||
if (forbidRefresh.current) {
|
if (forbidRefresh.current) {
|
||||||
forbidRefresh.current = false;
|
forbidRefresh.current = false;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return loadAppInfo(shareId, chatId, authToken);
|
|
||||||
|
return loadChatInfo(shareId, chatId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// load histories
|
||||||
|
useQuery(['loadHistories', outLinkUid, shareId], () => {
|
||||||
|
if (shareId && outLinkUid) {
|
||||||
|
return loadHistories({
|
||||||
|
shareId,
|
||||||
|
outLinkUid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// check is embed
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (window !== top) {
|
if (window !== top) {
|
||||||
window.top?.postMessage({ type: 'shareChatReady' }, '*');
|
window.top?.postMessage({ type: 'shareChatReady' }, '*');
|
||||||
@ -173,10 +229,32 @@ const OutLink = ({
|
|||||||
setIdEmbed(window !== top);
|
setIdEmbed(window !== top);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// todo:4.6.4 init: update local chat history, add outLinkUid
|
||||||
|
useEffect(() => {
|
||||||
|
const activeHistory = shareChatHistory.filter((item) => !item.delete);
|
||||||
|
if (!localUId || !shareId || activeHistory.length === 0) return;
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
await POST('/core/chat/initLocalShareHistoryV464', {
|
||||||
|
shareId,
|
||||||
|
outLinkUid: localUId,
|
||||||
|
chatIds: shareChatHistory.map((item) => item.chatId)
|
||||||
|
});
|
||||||
|
clearLocalHistory();
|
||||||
|
// router.reload();
|
||||||
|
} catch (error) {
|
||||||
|
toast({
|
||||||
|
status: 'warning',
|
||||||
|
title: getErrText(error, t('core.shareChat.Init Error'))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, [clearLocalHistory, localUId, router, shareChatHistory, shareId, t, toast]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer {...(isEmbed ? { p: '0 !important', borderRadius: '0' } : {})}>
|
<PageContainer {...(isEmbed ? { p: '0 !important', borderRadius: '0' } : {})}>
|
||||||
<Head>
|
<Head>
|
||||||
<title>{shareChatData.app.name}</title>
|
<title>{chatData.app.name}</title>
|
||||||
</Head>
|
</Head>
|
||||||
<Flex h={'100%'} flexDirection={['column', 'row']}>
|
<Flex h={'100%'} flexDirection={['column', 'row']}>
|
||||||
{showHistory === '1'
|
{showHistory === '1'
|
||||||
@ -199,12 +277,14 @@ const OutLink = ({
|
|||||||
);
|
);
|
||||||
})(
|
})(
|
||||||
<ChatHistorySlider
|
<ChatHistorySlider
|
||||||
appName={shareChatData.app.name}
|
appName={chatData.app.name}
|
||||||
appAvatar={shareChatData.app.avatar}
|
appAvatar={chatData.app.avatar}
|
||||||
activeChatId={chatId}
|
activeChatId={chatId}
|
||||||
history={history.map((item) => ({
|
history={histories.map((item) => ({
|
||||||
id: item.chatId,
|
id: item.chatId,
|
||||||
title: item.title
|
title: item.title,
|
||||||
|
customTitle: item.customTitle,
|
||||||
|
top: item.top
|
||||||
}))}
|
}))}
|
||||||
onClose={onCloseSlider}
|
onClose={onCloseSlider}
|
||||||
onChangeChat={(chatId) => {
|
onChangeChat={(chatId) => {
|
||||||
@ -218,9 +298,9 @@ const OutLink = ({
|
|||||||
onCloseSlider();
|
onCloseSlider();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onDelHistory={delOneShareHistoryByChatId}
|
onDelHistory={({ chatId }) => delOneHistory({ chatId, shareId, outLinkUid })}
|
||||||
onClearHistory={() => {
|
onClearHistory={() => {
|
||||||
delManyShareChatHistoryByShareId(shareId);
|
clearHistories({ shareId, outLinkUid });
|
||||||
router.replace({
|
router.replace({
|
||||||
query: {
|
query: {
|
||||||
...router.query,
|
...router.query,
|
||||||
@ -228,6 +308,16 @@ const OutLink = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
onSetHistoryTop={(e) => {
|
||||||
|
updateHistory(e);
|
||||||
|
}}
|
||||||
|
onSetCustomTitle={async (e) => {
|
||||||
|
updateHistory({
|
||||||
|
chatId: e.chatId,
|
||||||
|
title: e.title,
|
||||||
|
customTitle: e.title
|
||||||
|
});
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
: null}
|
: null}
|
||||||
@ -242,36 +332,24 @@ const OutLink = ({
|
|||||||
>
|
>
|
||||||
{/* header */}
|
{/* header */}
|
||||||
<ChatHeader
|
<ChatHeader
|
||||||
appAvatar={shareChatData.app.avatar}
|
appAvatar={chatData.app.avatar}
|
||||||
appName={shareChatData.app.name}
|
appName={chatData.app.name}
|
||||||
history={shareChatData.history.chats}
|
history={chatData.history}
|
||||||
onOpenSlider={onOpenSlider}
|
onOpenSlider={onOpenSlider}
|
||||||
/>
|
/>
|
||||||
{/* chat box */}
|
{/* chat box */}
|
||||||
<Box flex={1}>
|
<Box flex={1}>
|
||||||
<ChatBox
|
<ChatBox
|
||||||
active={!!shareChatData.app.name}
|
active={!!chatData.app.name}
|
||||||
ref={ChatBoxRef}
|
ref={ChatBoxRef}
|
||||||
appAvatar={shareChatData.app.avatar}
|
appAvatar={chatData.app.avatar}
|
||||||
userAvatar={shareChatData.userAvatar}
|
userAvatar={chatData.userAvatar}
|
||||||
userGuideModule={shareChatData.app?.userGuideModule}
|
userGuideModule={chatData.app?.userGuideModule}
|
||||||
showFileSelector={checkChatSupportSelectFileByChatModels(
|
showFileSelector={checkChatSupportSelectFileByChatModels(chatData.app.chatModels)}
|
||||||
shareChatData.app.chatModels
|
|
||||||
)}
|
|
||||||
feedbackType={'user'}
|
feedbackType={'user'}
|
||||||
onUpdateVariable={(e) => {
|
onUpdateVariable={(e) => {}}
|
||||||
setShareChatData((state) => ({
|
|
||||||
...state,
|
|
||||||
history: {
|
|
||||||
...state.history,
|
|
||||||
variables: e
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
onStartChat={startChat}
|
onStartChat={startChat}
|
||||||
onDelMessage={({ contentId, index }) =>
|
onDelMessage={(e) => delOneHistoryItem({ ...e, chatId })}
|
||||||
delShareChatHistoryItemById({ chatId, contentId, index })
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
@ -189,15 +189,6 @@ export async function searchDatasetData(props: SearchProps) {
|
|||||||
})
|
})
|
||||||
).filter((item) => item.score > similarity);
|
).filter((item) => item.score > similarity);
|
||||||
|
|
||||||
// (It's possible that rerank failed) concat rerank results and search results
|
|
||||||
set = new Set<string>(reRankResults.map((item) => item.id));
|
|
||||||
embeddingRecallResults.forEach((item) => {
|
|
||||||
if (!set.has(item.id) && item.score >= similarity) {
|
|
||||||
reRankResults.push(item);
|
|
||||||
set.add(item.id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
searchRes: reRankResults.slice(0, limit),
|
searchRes: reRankResults.slice(0, limit),
|
||||||
tokenLen
|
tokenLen
|
||||||
@ -382,7 +373,7 @@ export async function reRankSearchResult({
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
||||||
return [];
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ------------------ search end ------------------
|
// ------------------ search end ------------------
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { ModuleInputKeyEnum } from '@fastgpt/global/core/module/constants';
|
|||||||
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||||
import { RunningModuleItemType } from '@/types/app';
|
import { RunningModuleItemType } from '@/types/app';
|
||||||
import { ModuleDispatchProps } from '@/types/core/chat/type';
|
import { ModuleDispatchProps } from '@/types/core/chat/type';
|
||||||
import { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api';
|
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/module/node/constant';
|
||||||
import { ModuleItemType } from '@fastgpt/global/core/module/type';
|
import { ModuleItemType } from '@fastgpt/global/core/module/type';
|
||||||
import { UserType } from '@fastgpt/global/support/user/type';
|
import { UserType } from '@fastgpt/global/support/user/type';
|
||||||
|
|||||||
@ -48,7 +48,7 @@ export const dispatchRunPlugin = async (props: RunPluginProps): Promise<RunPlugi
|
|||||||
answerText,
|
answerText,
|
||||||
responseData: {
|
responseData: {
|
||||||
moduleLogo: plugin.avatar,
|
moduleLogo: plugin.avatar,
|
||||||
price: responseData.reduce((sum, item) => sum + item.price, 0),
|
price: responseData.reduce((sum, item) => sum + (item.price || 0), 0),
|
||||||
runningTime: responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0),
|
runningTime: responseData.reduce((sum, item) => sum + (item.runningTime || 0), 0),
|
||||||
pluginOutput: output?.pluginOutput
|
pluginOutput: output?.pluginOutput
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
import { POST } from '@fastgpt/service/common/api/plusRequest';
|
|
||||||
import type {
|
|
||||||
AuthLinkLimitProps,
|
|
||||||
AuthShareChatInitProps
|
|
||||||
} from '@fastgpt/global/support/outLink/api.d';
|
|
||||||
|
|
||||||
export function authOutLinkLimit(data: AuthLinkLimitProps) {
|
|
||||||
return POST('/support/outLink/authLimit', data);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function authShareChatInit(data: AuthShareChatInitProps) {
|
|
||||||
if (!global.feConfigs?.isPlus) return;
|
|
||||||
return POST('/support/outLink/authShareChatInit', data);
|
|
||||||
}
|
|
||||||
59
projects/app/src/service/support/permission/auth/chat.ts
Normal file
59
projects/app/src/service/support/permission/auth/chat.ts
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import { ChatSchema } from '@fastgpt/global/core/chat/type';
|
||||||
|
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||||
|
import { AuthModeType } from '@fastgpt/service/support/permission/type';
|
||||||
|
import { authOutLink } from './outLink';
|
||||||
|
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||||
|
import { authUserRole } from '@fastgpt/service/support/permission/auth/user';
|
||||||
|
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||||
|
|
||||||
|
/*
|
||||||
|
outLink: Must be the owner
|
||||||
|
token: team owner and chat owner have all permissions
|
||||||
|
*/
|
||||||
|
export async function autChatCrud({
|
||||||
|
chatId,
|
||||||
|
shareId,
|
||||||
|
outLinkUid,
|
||||||
|
per = 'owner',
|
||||||
|
...props
|
||||||
|
}: AuthModeType & {
|
||||||
|
chatId?: string;
|
||||||
|
shareId?: string;
|
||||||
|
outLinkUid?: string;
|
||||||
|
}): Promise<{
|
||||||
|
chat?: ChatSchema;
|
||||||
|
isOutLink: boolean;
|
||||||
|
uid?: string;
|
||||||
|
}> {
|
||||||
|
const isOutLink = Boolean(shareId && outLinkUid);
|
||||||
|
if (!chatId) return { isOutLink, uid: outLinkUid };
|
||||||
|
|
||||||
|
const chat = await MongoChat.findOne({ chatId }).lean();
|
||||||
|
|
||||||
|
if (!chat) return { isOutLink, uid: outLinkUid };
|
||||||
|
|
||||||
|
const { uid } = await (async () => {
|
||||||
|
// outLink Auth
|
||||||
|
if (shareId && outLinkUid) {
|
||||||
|
const { uid } = await authOutLink({ shareId, outLinkUid });
|
||||||
|
|
||||||
|
// auth outLinkUid
|
||||||
|
if (chat.shareId === shareId && chat.outLinkUid === uid) {
|
||||||
|
return { uid };
|
||||||
|
}
|
||||||
|
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||||
|
}
|
||||||
|
|
||||||
|
// req auth
|
||||||
|
const { tmbId, role } = await authUserRole(props);
|
||||||
|
if (role === TeamMemberRoleEnum.owner) return { uid: outLinkUid };
|
||||||
|
if (String(tmbId) === String(chat.tmbId)) return { uid: outLinkUid };
|
||||||
|
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||||
|
})();
|
||||||
|
|
||||||
|
return {
|
||||||
|
chat,
|
||||||
|
isOutLink,
|
||||||
|
uid
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,31 +1,74 @@
|
|||||||
import { authOutLinkLimit } from '@/service/support/outLink/auth';
|
import { POST } from '@fastgpt/service/common/api/plusRequest';
|
||||||
import { AuthLinkChatProps } from '@fastgpt/global/support/outLink/api.d';
|
import type {
|
||||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
AuthOutLinkChatProps,
|
||||||
import { getUserAndAuthBalance } from './user';
|
AuthOutLinkLimitProps,
|
||||||
|
AuthOutLinkInitProps,
|
||||||
|
AuthOutLinkResponse
|
||||||
|
} from '@fastgpt/global/support/outLink/api.d';
|
||||||
import { authOutLinkValid } from '@fastgpt/service/support/permission/auth/outLink';
|
import { authOutLinkValid } from '@fastgpt/service/support/permission/auth/outLink';
|
||||||
|
import { getUserAndAuthBalance } from '@fastgpt/service/support/user/controller';
|
||||||
|
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||||
|
import { OutLinkErrEnum } from '@fastgpt/global/common/error/code/outLink';
|
||||||
|
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
||||||
|
|
||||||
export async function authOutLinkChat({
|
export function authOutLinkInit(data: AuthOutLinkInitProps): Promise<AuthOutLinkResponse> {
|
||||||
|
if (!global.feConfigs?.isPlus) return Promise.resolve({ uid: data.outLinkUid });
|
||||||
|
return POST<AuthOutLinkResponse>('/support/outLink/authInit', data);
|
||||||
|
}
|
||||||
|
export function authOutLinkChatLimit(data: AuthOutLinkLimitProps): Promise<AuthOutLinkResponse> {
|
||||||
|
if (!global.feConfigs?.isPlus) return Promise.resolve({ uid: data.outLinkUid });
|
||||||
|
return POST<AuthOutLinkResponse>('/support/outLink/authChatStart', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const authOutLink = async ({
|
||||||
|
shareId,
|
||||||
|
outLinkUid
|
||||||
|
}: {
|
||||||
|
shareId?: string;
|
||||||
|
outLinkUid?: string;
|
||||||
|
}): Promise<{
|
||||||
|
uid: string;
|
||||||
|
appId: string;
|
||||||
|
shareChat: OutLinkSchema;
|
||||||
|
}> => {
|
||||||
|
if (!outLinkUid) {
|
||||||
|
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||||
|
}
|
||||||
|
const result = await authOutLinkValid({ shareId });
|
||||||
|
|
||||||
|
const { uid } = await authOutLinkInit({
|
||||||
|
outLinkUid,
|
||||||
|
tokenUrl: result.shareChat.limit?.hookUrl
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...result,
|
||||||
|
uid
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function authOutLinkChatStart({
|
||||||
shareId,
|
shareId,
|
||||||
ip,
|
ip,
|
||||||
authToken,
|
outLinkUid,
|
||||||
question
|
question
|
||||||
}: AuthLinkChatProps & {
|
}: AuthOutLinkChatProps & {
|
||||||
shareId: string;
|
shareId: string;
|
||||||
}) {
|
}) {
|
||||||
// get outLink
|
// get outLink and app
|
||||||
const { shareChat, app } = await authOutLinkValid({ shareId });
|
const { shareChat, appId } = await authOutLinkValid({ shareId });
|
||||||
|
|
||||||
const [user] = await Promise.all([
|
// check balance and chat limit
|
||||||
|
const [user, { uid }] = await Promise.all([
|
||||||
getUserAndAuthBalance({ tmbId: shareChat.tmbId, minBalance: 0 }),
|
getUserAndAuthBalance({ tmbId: shareChat.tmbId, minBalance: 0 }),
|
||||||
global.feConfigs?.isPlus
|
authOutLinkChatLimit({ outLink: shareChat, ip, outLinkUid, question })
|
||||||
? authOutLinkLimit({ outLink: shareChat, ip, authToken, question })
|
|
||||||
: undefined
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
authType: AuthUserTypeEnum.token,
|
authType: AuthUserTypeEnum.token,
|
||||||
responseDetail: shareChat.responseDetail,
|
responseDetail: shareChat.responseDetail,
|
||||||
user,
|
user,
|
||||||
app
|
appId,
|
||||||
|
uid
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,46 +0,0 @@
|
|||||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
|
||||||
import { parseHeaderCert } from '@fastgpt/service/support/permission/controller';
|
|
||||||
import { AuthModeType } from '@fastgpt/service/support/permission/type';
|
|
||||||
import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
|
|
||||||
import { UserType } from '@fastgpt/global/support/user/type';
|
|
||||||
import { getUserDetail } from '@/service/support/user/controller';
|
|
||||||
|
|
||||||
export async function getUserAndAuthBalance({
|
|
||||||
tmbId,
|
|
||||||
minBalance
|
|
||||||
}: {
|
|
||||||
tmbId: string;
|
|
||||||
minBalance?: number;
|
|
||||||
}) {
|
|
||||||
const user = await getUserDetail({ tmbId });
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return Promise.reject(UserErrEnum.unAuthUser);
|
|
||||||
}
|
|
||||||
if (minBalance !== undefined && global.feConfigs.isPlus && user.team.balance < minBalance) {
|
|
||||||
return Promise.reject(UserErrEnum.balanceNotEnough);
|
|
||||||
}
|
|
||||||
|
|
||||||
return user;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get user */
|
|
||||||
export async function authUser({
|
|
||||||
minBalance,
|
|
||||||
...props
|
|
||||||
}: AuthModeType & {
|
|
||||||
minBalance?: number;
|
|
||||||
}): Promise<
|
|
||||||
AuthResponseType & {
|
|
||||||
user: UserType;
|
|
||||||
}
|
|
||||||
> {
|
|
||||||
const result = await parseHeaderCert(props);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...result,
|
|
||||||
user: await getUserAndAuthBalance({ tmbId: result.tmbId, minBalance }),
|
|
||||||
isOwner: true,
|
|
||||||
canWrite: true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
|
||||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
|
||||||
import { UserType } from '@fastgpt/global/support/user/type';
|
|
||||||
import {
|
|
||||||
getTeamInfoByTmbId,
|
|
||||||
getUserDefaultTeam
|
|
||||||
} from '@fastgpt/service/support/user/team/controller';
|
|
||||||
|
|
||||||
export async function getUserDetail({
|
|
||||||
tmbId,
|
|
||||||
userId
|
|
||||||
}: {
|
|
||||||
tmbId?: string;
|
|
||||||
userId?: string;
|
|
||||||
}): Promise<UserType> {
|
|
||||||
const team = await (async () => {
|
|
||||||
if (tmbId) {
|
|
||||||
return getTeamInfoByTmbId({ tmbId });
|
|
||||||
}
|
|
||||||
if (userId) {
|
|
||||||
return getUserDefaultTeam({ userId });
|
|
||||||
}
|
|
||||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
|
||||||
})();
|
|
||||||
const user = await MongoUser.findById(team.userId);
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
_id: user._id,
|
|
||||||
username: user.username,
|
|
||||||
avatar: user.avatar,
|
|
||||||
balance: user.balance,
|
|
||||||
timezone: user.timezone,
|
|
||||||
promotionRate: user.promotionRate,
|
|
||||||
openaiAccount: user.openaiAccount,
|
|
||||||
team
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import { BillSourceEnum, PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants';
|
import { BillSourceEnum, PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants';
|
||||||
import { getAudioSpeechModel, getQAModel } from '@/service/core/ai/model';
|
import { getAudioSpeechModel, getQAModel } from '@/service/core/ai/model';
|
||||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
|
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { formatPrice } from '@fastgpt/global/support/wallet/bill/tools';
|
import { formatPrice } from '@fastgpt/global/support/wallet/bill/tools';
|
||||||
import { addLog } from '@fastgpt/service/common/mongo/controller';
|
import { addLog } from '@fastgpt/service/common/mongo/controller';
|
||||||
import type { ConcatBillProps, CreateBillProps } from '@fastgpt/global/support/wallet/bill/api.d';
|
import type { ConcatBillProps, CreateBillProps } from '@fastgpt/global/support/wallet/bill/api.d';
|
||||||
@ -41,7 +41,7 @@ export const pushChatBill = ({
|
|||||||
source: `${BillSourceEnum}`;
|
source: `${BillSourceEnum}`;
|
||||||
response: ChatHistoryItemResType[];
|
response: ChatHistoryItemResType[];
|
||||||
}) => {
|
}) => {
|
||||||
const total = response.reduce((sum, item) => sum + item.price, 0);
|
const total = response.reduce((sum, item) => sum + (item.price || 0), 0);
|
||||||
|
|
||||||
createBill({
|
createBill({
|
||||||
teamId,
|
teamId,
|
||||||
|
|||||||
@ -15,6 +15,7 @@ type Props = {
|
|||||||
updateUseTime: boolean;
|
updateUseTime: boolean;
|
||||||
source: `${ChatSourceEnum}`;
|
source: `${ChatSourceEnum}`;
|
||||||
shareId?: string;
|
shareId?: string;
|
||||||
|
outLinkUid?: string;
|
||||||
content: [ChatItemType, ChatItemType];
|
content: [ChatItemType, ChatItemType];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -27,10 +28,11 @@ export async function saveChat({
|
|||||||
updateUseTime,
|
updateUseTime,
|
||||||
source,
|
source,
|
||||||
shareId,
|
shareId,
|
||||||
|
outLinkUid,
|
||||||
content
|
content
|
||||||
}: Props) {
|
}: Props) {
|
||||||
try {
|
try {
|
||||||
const chatHistory = await MongoChat.findOne(
|
const chat = await MongoChat.findOne(
|
||||||
{
|
{
|
||||||
chatId,
|
chatId,
|
||||||
teamId,
|
teamId,
|
||||||
@ -57,7 +59,7 @@ export async function saveChat({
|
|||||||
content[1]?.value?.slice(0, 20) ||
|
content[1]?.value?.slice(0, 20) ||
|
||||||
'Chat';
|
'Chat';
|
||||||
|
|
||||||
if (chatHistory) {
|
if (chat) {
|
||||||
promise.push(
|
promise.push(
|
||||||
MongoChat.updateOne(
|
MongoChat.updateOne(
|
||||||
{ chatId },
|
{ chatId },
|
||||||
@ -77,7 +79,8 @@ export async function saveChat({
|
|||||||
variables,
|
variables,
|
||||||
title,
|
title,
|
||||||
source,
|
source,
|
||||||
shareId
|
shareId,
|
||||||
|
outLinkUid
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
|
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||||
|
|
||||||
export function selectShareResponse({ responseData }: { responseData: ChatHistoryItemResType[] }) {
|
export function selectShareResponse({
|
||||||
|
responseData = []
|
||||||
|
}: {
|
||||||
|
responseData?: ChatHistoryItemResType[];
|
||||||
|
}) {
|
||||||
const filedList = [
|
const filedList = [
|
||||||
'moduleType',
|
'moduleType',
|
||||||
'moduleName',
|
'moduleName',
|
||||||
'moduleLogo',
|
'moduleLogo',
|
||||||
'runningTime',
|
'runningTime',
|
||||||
|
'historyPreview',
|
||||||
'quoteList',
|
'quoteList',
|
||||||
'question'
|
'question'
|
||||||
];
|
];
|
||||||
@ -17,6 +22,6 @@ export function selectShareResponse({ responseData }: { responseData: ChatHistor
|
|||||||
obj[key] = item[key];
|
obj[key] = item[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return obj;
|
return obj as ChatHistoryItemResType;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant';
|
import { sseResponseEventEnum } from '@fastgpt/service/common/response/constant';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
import { parseStreamChunk, SSEParseData } from '@/utils/sse';
|
import { parseStreamChunk, SSEParseData } from '@/utils/sse';
|
||||||
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/api.d';
|
import type { ChatHistoryItemResType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import { StartChatFnProps } from '@/components/ChatBox';
|
import { StartChatFnProps } from '@/components/ChatBox';
|
||||||
import { getToken } from '@/web/support/user/auth';
|
import { getToken } from '@/web/support/user/auth';
|
||||||
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
import { ModuleOutputKeyEnum } from '@fastgpt/global/core/module/constants';
|
||||||
|
|||||||
@ -346,7 +346,7 @@ export const appTemplates: (AppItemType & {
|
|||||||
{
|
{
|
||||||
key: 'similarity',
|
key: 'similarity',
|
||||||
type: 'slider',
|
type: 'slider',
|
||||||
label: '相似度',
|
label: '相关度',
|
||||||
value: 0.4,
|
value: 0.4,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 1,
|
max: 1,
|
||||||
@ -1398,7 +1398,7 @@ export const appTemplates: (AppItemType & {
|
|||||||
{
|
{
|
||||||
key: 'similarity',
|
key: 'similarity',
|
||||||
type: 'slider',
|
type: 'slider',
|
||||||
label: '相似度',
|
label: '相关度',
|
||||||
value: 0.76,
|
value: 0.76,
|
||||||
min: 0,
|
min: 0,
|
||||||
max: 1,
|
max: 1,
|
||||||
|
|||||||
@ -1,42 +1,53 @@
|
|||||||
import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
|
import { GET, POST, DELETE, PUT } from '@/web/common/api/request';
|
||||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import type { InitChatResponse } from '@fastgpt/global/core/chat/api.d';
|
import type {
|
||||||
import type { RequestPaging } from '@/types';
|
InitChatProps,
|
||||||
import { UpdateHistoryProps } from '@fastgpt/global/core/chat/api.d';
|
InitChatResponse,
|
||||||
import type { AdminUpdateFeedbackParams } from '@fastgpt/global/core/chat/api.d';
|
InitOutLinkChatProps,
|
||||||
import { GetChatSpeechProps } from '@/global/core/chat/api.d';
|
getHistoriesProps
|
||||||
|
} from '@/global/core/chat/api.d';
|
||||||
|
import type {
|
||||||
|
AdminUpdateFeedbackParams,
|
||||||
|
ClearHistoriesProps,
|
||||||
|
DelHistoryProps,
|
||||||
|
DeleteChatItemProps,
|
||||||
|
UpdateHistoryProps
|
||||||
|
} from '@/global/core/chat/api.d';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取初始化聊天内容
|
* 获取初始化聊天内容
|
||||||
*/
|
*/
|
||||||
export const getInitChatSiteInfo = (data: { appId: string; chatId?: string }) =>
|
export const getInitChatInfo = (data: InitChatProps) =>
|
||||||
GET<InitChatResponse>(`/core/chat/init`, data);
|
GET<InitChatResponse>(`/core/chat/init`, data);
|
||||||
|
export const getInitOutLinkChatInfo = (data: InitOutLinkChatProps) =>
|
||||||
|
GET<InitChatResponse>(`/core/chat/outLink/init`, data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取历史记录
|
* get current window history(appid or shareId)
|
||||||
*/
|
*/
|
||||||
export const getChatHistory = (data: RequestPaging & { appId: string }) =>
|
export const getChatHistories = (data: getHistoriesProps) =>
|
||||||
POST<ChatHistoryItemType[]>('/core/chat/list', data);
|
POST<ChatHistoryItemType[]>('/core/chat/getHistories', data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除一条历史记录
|
* delete one history
|
||||||
*/
|
*/
|
||||||
export const delChatHistoryById = (chatId: string) => DELETE(`/core/chat/delete`, { chatId });
|
export const delChatHistoryById = (data: DelHistoryProps) => DELETE(`/core/chat/delHistory`, data);
|
||||||
/**
|
/**
|
||||||
* clear all history by appid
|
* clear all history by appid
|
||||||
*/
|
*/
|
||||||
export const clearChatHistoryByAppId = (appId: string) => DELETE(`/core/chat/delete`, { appId });
|
export const clearChatHistoryByAppId = (data: ClearHistoriesProps) =>
|
||||||
|
DELETE(`/core/chat/clearHistories`, data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除一句对话
|
* delete one chat record
|
||||||
*/
|
*/
|
||||||
export const delChatRecordById = (data: { chatId: string; contentId: string }) =>
|
export const delChatRecordById = (data: DeleteChatItemProps) =>
|
||||||
DELETE(`/core/chat/item/delete`, data);
|
DELETE(`/core/chat/item/delete`, data);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改历史记录: 标题/置顶
|
* 修改历史记录: 标题/置顶
|
||||||
*/
|
*/
|
||||||
export const putChatHistory = (data: UpdateHistoryProps) => PUT('/core/chat/update', data);
|
export const putChatHistory = (data: UpdateHistoryProps) => PUT('/core/chat/updateHistory', data);
|
||||||
|
|
||||||
export const userUpdateChatFeedback = (data: { chatItemId: string; userFeedback?: string }) =>
|
export const userUpdateChatFeedback = (data: { chatItemId: string; userFeedback?: string }) =>
|
||||||
POST('/core/chat/feedback/userUpdate', data);
|
POST('/core/chat/feedback/userUpdate', data);
|
||||||
|
|||||||
@ -2,35 +2,36 @@ import { create } from 'zustand';
|
|||||||
import { devtools, persist } from 'zustand/middleware';
|
import { devtools, persist } from 'zustand/middleware';
|
||||||
import { immer } from 'zustand/middleware/immer';
|
import { immer } from 'zustand/middleware/immer';
|
||||||
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
import type { InitChatResponse } from '@fastgpt/global/core/chat/api';
|
import type {
|
||||||
import { delChatHistoryById, getChatHistory, clearChatHistoryByAppId } from '@/web/core/chat/api';
|
InitChatResponse,
|
||||||
|
getHistoriesProps,
|
||||||
|
ClearHistoriesProps,
|
||||||
|
DelHistoryProps,
|
||||||
|
UpdateHistoryProps
|
||||||
|
} from '@/global/core/chat/api';
|
||||||
|
import {
|
||||||
|
delChatHistoryById,
|
||||||
|
getChatHistories,
|
||||||
|
clearChatHistoryByAppId,
|
||||||
|
delChatRecordById,
|
||||||
|
putChatHistory
|
||||||
|
} from '@/web/core/chat/api';
|
||||||
|
import { defaultChatData } from '@/global/core/chat/constants';
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
history: ChatHistoryItemType[];
|
histories: ChatHistoryItemType[];
|
||||||
loadHistory: (data: { appId: string }) => Promise<null>;
|
loadHistories: (data: getHistoriesProps) => Promise<null>;
|
||||||
delHistory(history: string): Promise<void>;
|
delOneHistory(data: DelHistoryProps): Promise<void>;
|
||||||
clearHistory(appId: string): Promise<void>;
|
clearHistories(data: ClearHistoriesProps): Promise<void>;
|
||||||
updateHistory: (history: ChatHistoryItemType) => void;
|
pushHistory: (history: ChatHistoryItemType) => void;
|
||||||
|
updateHistory: (e: UpdateHistoryProps & { updateTime?: Date; title?: string }) => Promise<any>;
|
||||||
chatData: InitChatResponse;
|
chatData: InitChatResponse;
|
||||||
setChatData: (e: InitChatResponse | ((e: InitChatResponse) => InitChatResponse)) => void;
|
setChatData: (e: InitChatResponse | ((e: InitChatResponse) => InitChatResponse)) => void;
|
||||||
lastChatAppId: string;
|
lastChatAppId: string;
|
||||||
setLastChatAppId: (id: string) => void;
|
setLastChatAppId: (id: string) => void;
|
||||||
lastChatId: string;
|
lastChatId: string;
|
||||||
setLastChatId: (id: string) => void;
|
setLastChatId: (id: string) => void;
|
||||||
};
|
delOneHistoryItem: (e: { chatId: string; contentId?: string; index: number }) => Promise<any>;
|
||||||
|
|
||||||
const defaultChatData: InitChatResponse = {
|
|
||||||
chatId: '',
|
|
||||||
appId: '',
|
|
||||||
app: {
|
|
||||||
name: 'Loading',
|
|
||||||
avatar: '/icon/logo.svg',
|
|
||||||
intro: '',
|
|
||||||
canUse: false
|
|
||||||
},
|
|
||||||
title: '新对话',
|
|
||||||
variables: {},
|
|
||||||
history: []
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useChatStore = create<State>()(
|
export const useChatStore = create<State>()(
|
||||||
@ -49,49 +50,62 @@ export const useChatStore = create<State>()(
|
|||||||
state.lastChatId = id;
|
state.lastChatId = id;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
history: [],
|
histories: [],
|
||||||
async loadHistory({ appId }) {
|
async loadHistories(e) {
|
||||||
const oneHistory = get().history[0];
|
const data = await getChatHistories(e);
|
||||||
if (oneHistory && oneHistory.appId === appId) return null;
|
|
||||||
const data = await getChatHistory({
|
|
||||||
appId,
|
|
||||||
pageNum: 1,
|
|
||||||
pageSize: 20
|
|
||||||
});
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.history = data;
|
state.histories = data;
|
||||||
});
|
});
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
async delHistory(chatId) {
|
async delOneHistory(props) {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.history = state.history.filter((item) => item.chatId !== chatId);
|
state.histories = state.histories.filter((item) => item.chatId !== props.chatId);
|
||||||
});
|
});
|
||||||
await delChatHistoryById(chatId);
|
await delChatHistoryById(props);
|
||||||
},
|
},
|
||||||
async clearHistory(appId) {
|
async clearHistories(data) {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.history = [];
|
state.histories = [];
|
||||||
});
|
});
|
||||||
await clearChatHistoryByAppId(appId);
|
await clearChatHistoryByAppId(data);
|
||||||
},
|
},
|
||||||
updateHistory(history) {
|
pushHistory(history) {
|
||||||
const index = get().history.findIndex((item) => item.chatId === history.chatId);
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const newHistory = (() => {
|
state.histories = [history, ...state.histories];
|
||||||
if (index > -1) {
|
});
|
||||||
return [
|
},
|
||||||
history,
|
async updateHistory(props) {
|
||||||
...get().history.slice(0, index),
|
const { chatId, customTitle, top, title, updateTime } = props;
|
||||||
...get().history.slice(index + 1)
|
const index = get().histories.findIndex((item) => item.chatId === chatId);
|
||||||
];
|
|
||||||
} else {
|
|
||||||
return [history, ...state.history];
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
state.history = newHistory;
|
if (index > -1) {
|
||||||
});
|
const newHistory = {
|
||||||
|
...get().histories[index],
|
||||||
|
...(title && { title }),
|
||||||
|
...(updateTime && { updateTime }),
|
||||||
|
...(customTitle !== undefined && { customTitle }),
|
||||||
|
...(top !== undefined && { top })
|
||||||
|
};
|
||||||
|
|
||||||
|
if (customTitle !== undefined || top !== undefined) {
|
||||||
|
try {
|
||||||
|
putChatHistory(props);
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
set((state) => {
|
||||||
|
const newHistories = (() => {
|
||||||
|
return [
|
||||||
|
newHistory,
|
||||||
|
...get().histories.slice(0, index),
|
||||||
|
...get().histories.slice(index + 1)
|
||||||
|
];
|
||||||
|
})();
|
||||||
|
|
||||||
|
state.histories = newHistories;
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
chatData: defaultChatData,
|
chatData: defaultChatData,
|
||||||
setChatData(e = defaultChatData) {
|
setChatData(e = defaultChatData) {
|
||||||
@ -104,6 +118,19 @@ export const useChatStore = create<State>()(
|
|||||||
state.chatData = e;
|
state.chatData = e;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
async delOneHistoryItem({ chatId, contentId, index }) {
|
||||||
|
if (!chatId || !contentId) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
get().setChatData((state) => ({
|
||||||
|
...state,
|
||||||
|
history: state.history.filter((_, i) => i !== index)
|
||||||
|
}));
|
||||||
|
await delChatRecordById({ chatId, contentId });
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,142 +1,39 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { devtools, persist } from 'zustand/middleware';
|
import { devtools, persist } from 'zustand/middleware';
|
||||||
import { immer } from 'zustand/middleware/immer';
|
import { immer } from 'zustand/middleware/immer';
|
||||||
import type {
|
import type { ChatHistoryItemType } from '@fastgpt/global/core/chat/type.d';
|
||||||
ShareChatHistoryItemType,
|
import { customAlphabet } from 'nanoid';
|
||||||
ShareChatType
|
const nanoid = customAlphabet(
|
||||||
} from '@fastgpt/global/support/outLink/api.d';
|
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWSYZ1234567890_',
|
||||||
import type { ChatSiteItemType } from '@fastgpt/global/core/chat/type.d';
|
24
|
||||||
import { HUMAN_ICON } from '@fastgpt/global/core/chat/constants';
|
);
|
||||||
import { chatContentReplaceBlock } from '@fastgpt/global/core/chat/utils';
|
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
shareChatData: ShareChatType;
|
localUId: string;
|
||||||
setShareChatData: (e: ShareChatType | ((e: ShareChatType) => ShareChatType)) => void;
|
shareChatHistory: (ChatHistoryItemType & { delete?: boolean })[];
|
||||||
shareChatHistory: ShareChatHistoryItemType[];
|
clearLocalHistory: (shareId?: string) => void;
|
||||||
saveChatResponse: (e: {
|
|
||||||
chatId: string;
|
|
||||||
prompts: ChatSiteItemType[];
|
|
||||||
variables: Record<string, any>;
|
|
||||||
shareId: string;
|
|
||||||
}) => void;
|
|
||||||
delOneShareHistoryByChatId: (chatId: string) => void;
|
|
||||||
delShareChatHistoryItemById: (e: { chatId: string; contentId?: string; index: number }) => void;
|
|
||||||
delManyShareChatHistoryByShareId: (shareId?: string) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const defaultHistory: ShareChatHistoryItemType = {
|
|
||||||
chatId: `${Date.now()}`,
|
|
||||||
updateTime: new Date(),
|
|
||||||
title: '新对话',
|
|
||||||
shareId: '',
|
|
||||||
chats: []
|
|
||||||
};
|
|
||||||
const defaultShareChatData: ShareChatType = {
|
|
||||||
userAvatar: HUMAN_ICON,
|
|
||||||
app: {
|
|
||||||
name: '',
|
|
||||||
avatar: '/icon/logo.svg',
|
|
||||||
intro: ''
|
|
||||||
},
|
|
||||||
history: defaultHistory
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useShareChatStore = create<State>()(
|
export const useShareChatStore = create<State>()(
|
||||||
devtools(
|
devtools(
|
||||||
persist(
|
persist(
|
||||||
immer((set, get) => ({
|
immer((set, get) => ({
|
||||||
shareChatData: defaultShareChatData,
|
localUId: `shareChat-${Date.now()}-${nanoid()}`,
|
||||||
setShareChatData(e) {
|
shareChatHistory: [], // old version field
|
||||||
const val = (() => {
|
clearLocalHistory() {
|
||||||
if (typeof e === 'function') {
|
// abandon
|
||||||
return e(get().shareChatData);
|
|
||||||
} else {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.shareChatData = val;
|
state.shareChatHistory = state.shareChatHistory.map((item) => ({
|
||||||
// update history
|
...item,
|
||||||
state.shareChatHistory = state.shareChatHistory.map((item) =>
|
delete: true
|
||||||
item.chatId === val.history.chatId ? val.history : item
|
}));
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
shareChatHistory: [],
|
|
||||||
saveChatResponse({ chatId, prompts, variables, shareId }) {
|
|
||||||
const chatHistory = get().shareChatHistory.find((item) => item.chatId === chatId);
|
|
||||||
const newTitle =
|
|
||||||
chatContentReplaceBlock(prompts[prompts.length - 2]?.value).slice(0, 20) ||
|
|
||||||
prompts[prompts.length - 1]?.value?.slice(0, 20) ||
|
|
||||||
'Chat';
|
|
||||||
|
|
||||||
const historyList = (() => {
|
|
||||||
if (chatHistory) {
|
|
||||||
return get().shareChatHistory.map((item) =>
|
|
||||||
item.chatId === chatId
|
|
||||||
? {
|
|
||||||
...item,
|
|
||||||
title: newTitle,
|
|
||||||
updateTime: new Date(),
|
|
||||||
chats: chatHistory.chats.concat(prompts).slice(-30),
|
|
||||||
variables
|
|
||||||
}
|
|
||||||
: item
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return get().shareChatHistory.concat({
|
|
||||||
chatId,
|
|
||||||
shareId,
|
|
||||||
title: newTitle,
|
|
||||||
updateTime: new Date(),
|
|
||||||
chats: prompts,
|
|
||||||
variables
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
historyList.sort((a, b) => new Date(b.updateTime) - new Date(a.updateTime));
|
|
||||||
|
|
||||||
set((state) => {
|
|
||||||
state.shareChatHistory = historyList.slice(0, 50);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
delOneShareHistoryByChatId(chatId: string) {
|
|
||||||
set((state) => {
|
|
||||||
state.shareChatHistory = state.shareChatHistory.filter(
|
|
||||||
(item) => item.chatId !== chatId
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
delShareChatHistoryItemById({ chatId, contentId }) {
|
|
||||||
set((state) => {
|
|
||||||
// update history store
|
|
||||||
const newHistoryList = state.shareChatHistory.map((item) =>
|
|
||||||
item.chatId === chatId
|
|
||||||
? {
|
|
||||||
...item,
|
|
||||||
chats: item.chats.filter((item) => item.dataId !== contentId)
|
|
||||||
}
|
|
||||||
: item
|
|
||||||
);
|
|
||||||
state.shareChatHistory = newHistoryList;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
delManyShareChatHistoryByShareId(shareId?: string) {
|
|
||||||
set((state) => {
|
|
||||||
if (shareId) {
|
|
||||||
state.shareChatHistory = state.shareChatHistory.filter(
|
|
||||||
(item) => item.shareId !== shareId
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
state.shareChatHistory = [];
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
name: 'shareChatStore',
|
name: 'shareChatStore',
|
||||||
partialize: (state) => ({
|
partialize: (state) => ({
|
||||||
|
localUId: state.localUId,
|
||||||
shareChatHistory: state.shareChatHistory
|
shareChatHistory: state.shareChatHistory
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -79,8 +79,7 @@ export const useDatasetStore = create<State>()(
|
|||||||
item._id === data.id
|
item._id === data.id
|
||||||
? {
|
? {
|
||||||
...item,
|
...item,
|
||||||
...data,
|
...data
|
||||||
tags: data.tags || []
|
|
||||||
}
|
}
|
||||||
: item
|
: item
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,13 +1,6 @@
|
|||||||
import { GET, POST, DELETE } from '@/web/common/api/request';
|
import { GET, POST, DELETE } from '@/web/common/api/request';
|
||||||
import type { InitShareChatResponse } from '@fastgpt/global/support/outLink/api.d';
|
|
||||||
import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type.d';
|
import type { OutLinkEditType, OutLinkSchema } from '@fastgpt/global/support/outLink/type.d';
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化分享聊天
|
|
||||||
*/
|
|
||||||
export const initShareChatInfo = (data: { shareId: string; authToken?: string }) =>
|
|
||||||
GET<InitShareChatResponse>(`/support/outLink/init`, data);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* create a shareChat
|
* create a shareChat
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import { GET } from '@/web/common/api/request';
|
import { GET } from '@/web/common/api/request';
|
||||||
import type { PaySchema } from '@fastgpt/global/support/wallet/pay/type.d';
|
import type { PaySchema } from '@fastgpt/global/support/wallet/pay/type.d';
|
||||||
import { delay } from '@fastgpt/global/common/system/utils';
|
|
||||||
|
|
||||||
export const getPayOrders = () => GET<PaySchema[]>(`/plusApi/support/wallet/pay/getPayOrders`);
|
export const getPayOrders = () => GET<PaySchema[]>(`/plusApi/support/wallet/pay/getPayOrders`);
|
||||||
|
|
||||||
export const getPayCode = (amount: number) =>
|
export const getPayCode = (amount: number) =>
|
||||||
@ -11,15 +9,9 @@ export const getPayCode = (amount: number) =>
|
|||||||
}>(`/plusApi/support/wallet/pay/getPayCode`, { amount });
|
}>(`/plusApi/support/wallet/pay/getPayCode`, { amount });
|
||||||
|
|
||||||
export const checkPayResult = (payId: string) =>
|
export const checkPayResult = (payId: string) =>
|
||||||
GET<number>(`/plusApi/support/wallet/pay/checkPayResult`, { payId }).then(() => {
|
GET<string>(`/plusApi/support/wallet/pay/checkPayResult`, { payId }).then((data) => {
|
||||||
async function startQueue() {
|
try {
|
||||||
try {
|
GET('/common/system/unlockTask');
|
||||||
await GET('/common/system/unlockTask');
|
} catch (error) {}
|
||||||
} catch (error) {
|
return data;
|
||||||
await delay(1000);
|
|
||||||
startQueue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
startQueue();
|
|
||||||
return 'success';
|
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user