4.8.5 test (#1805)
* perf: revert tip * feat: create copy app * perf: file stream read * perf: read directory over 100 files * perf: index * fix: team chat api error * lock * fix: i18n file
This commit is contained in:
parent
980b4d3db5
commit
5cc01b8509
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -4,7 +4,7 @@
|
|||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
"prettier.prettierPath": "",
|
"prettier.prettierPath": "",
|
||||||
"i18n-ally.localesPaths": [
|
"i18n-ally.localesPaths": [
|
||||||
"projects/app/i18n",
|
"packages/web/i18n",
|
||||||
],
|
],
|
||||||
"i18n-ally.enabledParsers": ["json", "yaml", "js", "ts"],
|
"i18n-ally.enabledParsers": ["json", "yaml", "js", "ts"],
|
||||||
"i18n-ally.keystyle": "nested",
|
"i18n-ally.keystyle": "nested",
|
||||||
|
|||||||
@ -32,6 +32,10 @@ curl --location --request POST 'https://{{host}}/api/admin/initv485' \
|
|||||||
## V4.8.5 更新说明
|
## V4.8.5 更新说明
|
||||||
|
|
||||||
1. 新增 - 合并插件和应用,统一成工作台
|
1. 新增 - 合并插件和应用,统一成工作台
|
||||||
2. 修复 - SSR渲染
|
2. 新增 - 应用创建副本功能
|
||||||
3. 修复 - 定时任务无法实际关闭
|
3. 优化 - 原文件编码存取
|
||||||
4. 修复 - 输入引导特殊字符导致正则报错
|
4. 优化 - 文件夹读取,支持单个文件夹超出 100 个文件
|
||||||
|
5. 优化 - 问答拆分/手动录入,当有`a`字段时,自动将`q`作为补充索引。
|
||||||
|
6. 修复 - SSR渲染
|
||||||
|
7. 修复 - 定时任务无法实际关闭
|
||||||
|
8. 修复 - 输入引导特殊字符导致正则报错
|
||||||
@ -21,12 +21,6 @@
|
|||||||
"react-i18next": "13.5.0",
|
"react-i18next": "13.5.0",
|
||||||
"zhlint": "^0.7.1"
|
"zhlint": "^0.7.1"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
|
||||||
"react": "18.3.1",
|
|
||||||
"react-dom": "18.3.1",
|
|
||||||
"@types/react": "18.3.0",
|
|
||||||
"@types/react-dom": "18.3.0"
|
|
||||||
},
|
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"./**/**/*.{ts,tsx,scss}": "npm run format-code",
|
"./**/**/*.{ts,tsx,scss}": "npm run format-code",
|
||||||
"./docSite/**/**/*.md": "npm run format-doc"
|
"./docSite/**/**/*.md": "npm run format-doc"
|
||||||
|
|||||||
@ -350,7 +350,7 @@ export const splitText2Chunks = (props: SplitProps): SplitResponse => {
|
|||||||
|
|
||||||
return commonSplit(props);
|
return commonSplit(props);
|
||||||
});
|
});
|
||||||
console.log(Date.now() - start);
|
|
||||||
return {
|
return {
|
||||||
chunks: splitResult.map((item) => item.chunks).flat(),
|
chunks: splitResult.map((item) => item.chunks).flat(),
|
||||||
chars: splitResult.reduce((sum, item) => sum + item.chars, 0)
|
chars: splitResult.reduce((sum, item) => sum + item.chars, 0)
|
||||||
|
|||||||
3
packages/global/core/dataset/type.d.ts
vendored
3
packages/global/core/dataset/type.d.ts
vendored
@ -179,10 +179,9 @@ export type DatasetFileSchema = {
|
|||||||
filename: string;
|
filename: string;
|
||||||
contentType: string;
|
contentType: string;
|
||||||
metadata: {
|
metadata: {
|
||||||
contentType: string;
|
|
||||||
datasetId: string;
|
|
||||||
teamId: string;
|
teamId: string;
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
|
encoding?: string;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -17,6 +17,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/node": "^20.8.5"
|
"@types/node": "^20.14.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {},
|
"dependencies": {},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.8.5",
|
"@types/node": "^20.14.2",
|
||||||
"@fastgpt/global": "workspace:*",
|
"@fastgpt/global": "workspace:*",
|
||||||
"@fastgpt/service": "workspace:*"
|
"@fastgpt/service": "workspace:*"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,8 @@ import { detectFileEncoding } from '@fastgpt/global/common/file/tools';
|
|||||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
import { MongoRawTextBuffer } from '../../buffer/rawText/schema';
|
import { MongoRawTextBuffer } from '../../buffer/rawText/schema';
|
||||||
import { readRawContentByFileBuffer } from '../read/utils';
|
import { readRawContentByFileBuffer } from '../read/utils';
|
||||||
import { gridFsStream2Buffer } from './utils';
|
import { gridFsStream2Buffer, stream2Encoding } from './utils';
|
||||||
|
import { addLog } from '../../system/log';
|
||||||
|
|
||||||
export function getGFSCollection(bucket: `${BucketNameEnum}`) {
|
export function getGFSCollection(bucket: `${BucketNameEnum}`) {
|
||||||
MongoFileSchema;
|
MongoFileSchema;
|
||||||
@ -44,8 +45,11 @@ export async function uploadFile({
|
|||||||
const stats = await fsp.stat(path);
|
const stats = await fsp.stat(path);
|
||||||
if (!stats.isFile()) return Promise.reject(`${path} is not a file`);
|
if (!stats.isFile()) return Promise.reject(`${path} is not a file`);
|
||||||
|
|
||||||
|
const { stream: readStream, encoding } = await stream2Encoding(fs.createReadStream(path));
|
||||||
|
|
||||||
metadata.teamId = teamId;
|
metadata.teamId = teamId;
|
||||||
metadata.tmbId = tmbId;
|
metadata.tmbId = tmbId;
|
||||||
|
metadata.encoding = encoding;
|
||||||
|
|
||||||
// create a gridfs bucket
|
// create a gridfs bucket
|
||||||
const bucket = getGridBucket(bucketName);
|
const bucket = getGridBucket(bucketName);
|
||||||
@ -57,7 +61,7 @@ export async function uploadFile({
|
|||||||
|
|
||||||
// save to gridfs
|
// save to gridfs
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
fs.createReadStream(path)
|
readStream
|
||||||
.pipe(stream as any)
|
.pipe(stream as any)
|
||||||
.on('finish', resolve)
|
.on('finish', resolve)
|
||||||
.on('error', reject);
|
.on('error', reject);
|
||||||
@ -113,19 +117,8 @@ export async function getDownloadStream({
|
|||||||
fileId: string;
|
fileId: string;
|
||||||
}) {
|
}) {
|
||||||
const bucket = getGridBucket(bucketName);
|
const bucket = getGridBucket(bucketName);
|
||||||
const encodeStream = bucket.openDownloadStream(new Types.ObjectId(fileId), { end: 100 });
|
|
||||||
const rawStream = bucket.openDownloadStream(new Types.ObjectId(fileId));
|
|
||||||
|
|
||||||
/* get encoding */
|
return bucket.openDownloadStream(new Types.ObjectId(fileId));
|
||||||
const buffer = await gridFsStream2Buffer(encodeStream);
|
|
||||||
|
|
||||||
const encoding = detectFileEncoding(buffer);
|
|
||||||
|
|
||||||
return {
|
|
||||||
fileStream: rawStream,
|
|
||||||
encoding
|
|
||||||
// encoding: 'utf-8'
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const readFileContentFromMongo = async ({
|
export const readFileContentFromMongo = async ({
|
||||||
@ -150,9 +143,8 @@ export const readFileContentFromMongo = async ({
|
|||||||
filename: fileBuffer.metadata?.filename || ''
|
filename: fileBuffer.metadata?.filename || ''
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const start = Date.now();
|
|
||||||
|
|
||||||
const [file, { encoding, fileStream }] = await Promise.all([
|
const [file, fileStream] = await Promise.all([
|
||||||
getFileById({ bucketName, fileId }),
|
getFileById({ bucketName, fileId }),
|
||||||
getDownloadStream({ bucketName, fileId })
|
getDownloadStream({ bucketName, fileId })
|
||||||
]);
|
]);
|
||||||
@ -163,8 +155,11 @@ export const readFileContentFromMongo = async ({
|
|||||||
|
|
||||||
const extension = file?.filename?.split('.')?.pop()?.toLowerCase() || '';
|
const extension = file?.filename?.split('.')?.pop()?.toLowerCase() || '';
|
||||||
|
|
||||||
|
const start = Date.now();
|
||||||
const fileBuffers = await gridFsStream2Buffer(fileStream);
|
const fileBuffers = await gridFsStream2Buffer(fileStream);
|
||||||
// console.log('get file buffer', Date.now() - start);
|
addLog.debug('get file buffer', { time: Date.now() - start });
|
||||||
|
|
||||||
|
const encoding = file?.metadata?.encoding || detectFileEncoding(fileBuffers);
|
||||||
|
|
||||||
const { rawText } = await readRawContentByFileBuffer({
|
const { rawText } = await readRawContentByFileBuffer({
|
||||||
extension,
|
extension,
|
||||||
@ -177,7 +172,8 @@ export const readFileContentFromMongo = async ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (rawText.trim()) {
|
// < 14M
|
||||||
|
if (fileBuffers.length < 14 * 1024 * 1024 && rawText.trim()) {
|
||||||
MongoRawTextBuffer.create({
|
MongoRawTextBuffer.create({
|
||||||
sourceId: fileId,
|
sourceId: fileId,
|
||||||
rawText,
|
rawText,
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
import { detectFileEncoding } from '@fastgpt/global/common/file/tools';
|
||||||
|
import { PassThrough } from 'stream';
|
||||||
|
|
||||||
export const gridFsStream2Buffer = (stream: NodeJS.ReadableStream) => {
|
export const gridFsStream2Buffer = (stream: NodeJS.ReadableStream) => {
|
||||||
return new Promise<Buffer>((resolve, reject) => {
|
return new Promise<Buffer>((resolve, reject) => {
|
||||||
let tmpBuffer: Buffer = Buffer.from([]);
|
let tmpBuffer: Buffer = Buffer.from([]);
|
||||||
@ -13,3 +16,38 @@ export const gridFsStream2Buffer = (stream: NodeJS.ReadableStream) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const stream2Encoding = async (stream: NodeJS.ReadableStream) => {
|
||||||
|
const start = Date.now();
|
||||||
|
const copyStream = stream.pipe(new PassThrough());
|
||||||
|
|
||||||
|
/* get encoding */
|
||||||
|
const buffer = await (() => {
|
||||||
|
return new Promise<Buffer>((resolve, reject) => {
|
||||||
|
let tmpBuffer: Buffer = Buffer.from([]);
|
||||||
|
|
||||||
|
stream.on('data', (chunk) => {
|
||||||
|
if (tmpBuffer.length < 200) {
|
||||||
|
tmpBuffer = Buffer.concat([tmpBuffer, chunk]);
|
||||||
|
|
||||||
|
if (tmpBuffer.length >= 200) {
|
||||||
|
resolve(tmpBuffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
stream.on('end', () => {
|
||||||
|
resolve(tmpBuffer);
|
||||||
|
});
|
||||||
|
stream.on('error', (err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
|
||||||
|
const enc = detectFileEncoding(buffer);
|
||||||
|
console.log('Get encoding time', Date.now() - start, enc);
|
||||||
|
return {
|
||||||
|
encoding: enc,
|
||||||
|
stream: copyStream
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@ -15,12 +15,12 @@
|
|||||||
"decompress": "^4.2.1",
|
"decompress": "^4.2.1",
|
||||||
"domino-ext": "^2.1.4",
|
"domino-ext": "^2.1.4",
|
||||||
"encoding": "^0.1.13",
|
"encoding": "^0.1.13",
|
||||||
"lodash": "^4.17.21",
|
|
||||||
"file-type": "^19.0.0",
|
"file-type": "^19.0.0",
|
||||||
"iconv-lite": "^0.6.3",
|
"iconv-lite": "^0.6.3",
|
||||||
"joplin-turndown-plugin-gfm": "^1.0.12",
|
"joplin-turndown-plugin-gfm": "^1.0.12",
|
||||||
"json5": "^2.2.3",
|
"json5": "^2.2.3",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
"mammoth": "^1.6.0",
|
"mammoth": "^1.6.0",
|
||||||
"mongoose": "^7.0.2",
|
"mongoose": "^7.0.2",
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
@ -39,10 +39,10 @@
|
|||||||
"@types/cookie": "^0.5.2",
|
"@types/cookie": "^0.5.2",
|
||||||
"@types/decompress": "^4.2.7",
|
"@types/decompress": "^4.2.7",
|
||||||
"@types/jsonwebtoken": "^9.0.3",
|
"@types/jsonwebtoken": "^9.0.3",
|
||||||
|
"@types/lodash": "^4.14.191",
|
||||||
"@types/multer": "^1.4.10",
|
"@types/multer": "^1.4.10",
|
||||||
"@types/node-cron": "^3.0.11",
|
"@types/node-cron": "^3.0.11",
|
||||||
"@types/papaparse": "5.3.7",
|
"@types/papaparse": "5.3.7",
|
||||||
"@types/lodash": "^4.14.191",
|
|
||||||
"@types/pg": "^8.6.6",
|
"@types/pg": "^8.6.6",
|
||||||
"@types/tunnel": "^0.0.4",
|
"@types/tunnel": "^0.0.4",
|
||||||
"@types/turndown": "^5.0.4"
|
"@types/turndown": "^5.0.4"
|
||||||
|
|||||||
@ -46,12 +46,26 @@ const MyMenu = ({
|
|||||||
_hover: {
|
_hover: {
|
||||||
backgroundColor: 'primary.50',
|
backgroundColor: 'primary.50',
|
||||||
color: 'primary.600'
|
color: 'primary.600'
|
||||||
|
},
|
||||||
|
_focus: {
|
||||||
|
backgroundColor: 'primary.50',
|
||||||
|
color: 'primary.600'
|
||||||
|
},
|
||||||
|
_active: {
|
||||||
|
backgroundColor: 'primary.50',
|
||||||
|
color: 'primary.600'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
danger: {
|
danger: {
|
||||||
color: 'red.600',
|
color: 'red.600',
|
||||||
_hover: {
|
_hover: {
|
||||||
background: 'red.1'
|
background: 'red.1'
|
||||||
|
},
|
||||||
|
_focus: {
|
||||||
|
background: 'red.1'
|
||||||
|
},
|
||||||
|
_active: {
|
||||||
|
background: 'red.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -76,7 +76,7 @@ const PopoverConfirm = ({
|
|||||||
<PopoverContent p={4}>
|
<PopoverContent p={4}>
|
||||||
<PopoverArrow />
|
<PopoverArrow />
|
||||||
|
|
||||||
<HStack alignItems={'flex-start'}>
|
<HStack alignItems={'flex-start'} color={'myGray.700'}>
|
||||||
<MyIcon name={map.icon as any} w={'1.5rem'} />
|
<MyIcon name={map.icon as any} w={'1.5rem'} />
|
||||||
<Box fontSize={'sm'}>{content}</Box>
|
<Box fontSize={'sm'}>{content}</Box>
|
||||||
</HStack>
|
</HStack>
|
||||||
|
|||||||
@ -9,11 +9,14 @@
|
|||||||
"Chat Logs Tips": "Logs will record online, shared and API (chatId required) conversation records for this app",
|
"Chat Logs Tips": "Logs will record online, shared and API (chatId required) conversation records for this app",
|
||||||
"Chat logs": "Chat Logs",
|
"Chat logs": "Chat Logs",
|
||||||
"Confirm Del App Tip": "Confirm to delete this app and all its chat records?",
|
"Confirm Del App Tip": "Confirm to delete this app and all its chat records?",
|
||||||
|
"Confirm copy app tip": "The system will create an application with the same configuration for you, but the permission will not be copied, please confirm!",
|
||||||
"Confirm delete folder tip": "Are you sure to delete this folder? All the following applications and corresponding chat records will be deleted, please confirm!",
|
"Confirm delete folder tip": "Are you sure to delete this folder? All the following applications and corresponding chat records will be deleted, please confirm!",
|
||||||
"Connection is invalid": "Connection is invalid",
|
"Connection is invalid": "Connection is invalid",
|
||||||
"Connection type is different": "Connection type is different",
|
"Connection type is different": "Connection type is different",
|
||||||
"Copy Module Config": "Copy Config",
|
"Copy Module Config": "Copy Config",
|
||||||
|
"Copy one app": "Copy",
|
||||||
"Create bot": "App",
|
"Create bot": "App",
|
||||||
|
"Create copy success": "Create copy success",
|
||||||
"Create one ai app": "Create AI app",
|
"Create one ai app": "Create AI app",
|
||||||
"Current settings": "Current settings",
|
"Current settings": "Current settings",
|
||||||
"Dataset Quote Template": "Knowledge Base QA Mode",
|
"Dataset Quote Template": "Knowledge Base QA Mode",
|
||||||
@ -77,5 +80,8 @@
|
|||||||
"Plugin": "Plugin",
|
"Plugin": "Plugin",
|
||||||
"Simple bot": "Simple bot",
|
"Simple bot": "Simple bot",
|
||||||
"Workflow bot": "Workflow"
|
"Workflow bot": "Workflow"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"Revert success": "Revert success"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8,11 +8,14 @@
|
|||||||
"Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录",
|
"Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录",
|
||||||
"Chat logs": "对话日志",
|
"Chat logs": "对话日志",
|
||||||
"Confirm Del App Tip": "确认删除该应用及其所有聊天记录?",
|
"Confirm Del App Tip": "确认删除该应用及其所有聊天记录?",
|
||||||
|
"Confirm copy app tip": "系统将为您创建一个相同配置应用,但权限不会进行复制,请确认!",
|
||||||
"Confirm delete folder tip": "确认删除该文件夹?将会删除它下面所有应用及对应的聊天记录,请确认!",
|
"Confirm delete folder tip": "确认删除该文件夹?将会删除它下面所有应用及对应的聊天记录,请确认!",
|
||||||
"Connection is invalid": "连接无效",
|
"Connection is invalid": "连接无效",
|
||||||
"Connection type is different": "连接的类型不一致",
|
"Connection type is different": "连接的类型不一致",
|
||||||
"Copy Module Config": "复制配置",
|
"Copy Module Config": "复制配置",
|
||||||
|
"Copy one app": "创建副本",
|
||||||
"Create bot": "应用",
|
"Create bot": "应用",
|
||||||
|
"Create copy success": "创建副本成功",
|
||||||
"Create one ai app": "创建一个AI应用",
|
"Create one ai app": "创建一个AI应用",
|
||||||
"Current settings": "当前配置",
|
"Current settings": "当前配置",
|
||||||
"Dataset Quote Template": "知识库问答模式",
|
"Dataset Quote Template": "知识库问答模式",
|
||||||
@ -76,5 +79,8 @@
|
|||||||
"Plugin": "插件",
|
"Plugin": "插件",
|
||||||
"Simple bot": "简易应用",
|
"Simple bot": "简易应用",
|
||||||
"Workflow bot": "工作流"
|
"Workflow bot": "工作流"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"Revert success": "回滚成功"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -252,6 +252,7 @@
|
|||||||
"Publish Confirm": "确认发布应用?会立即更新所有发布渠道的应用状态。",
|
"Publish Confirm": "确认发布应用?会立即更新所有发布渠道的应用状态。",
|
||||||
"Publish Failed": "发布失败",
|
"Publish Failed": "发布失败",
|
||||||
"Publish Success": "发布成功",
|
"Publish Success": "发布成功",
|
||||||
|
"Publish app tip": "发布应用后,所有发布渠道将会立即使用该版本",
|
||||||
"Question Guide": "猜你想问",
|
"Question Guide": "猜你想问",
|
||||||
"Question Guide Tip": "对话结束后,会为生成 3 个引导性问题。",
|
"Question Guide Tip": "对话结束后,会为生成 3 个引导性问题。",
|
||||||
"Quote prompt": "引用模板提示词",
|
"Quote prompt": "引用模板提示词",
|
||||||
18698
pnpm-lock.yaml
generated
18698
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,7 @@ module.exports = {
|
|||||||
locales: ['en', 'zh'],
|
locales: ['en', 'zh'],
|
||||||
localeDetection: false
|
localeDetection: false
|
||||||
},
|
},
|
||||||
localePath: typeof window === 'undefined' ? require('path').resolve('./i18n') : '/i18n',
|
localePath:
|
||||||
|
typeof window === 'undefined' ? require('path').resolve('../../packages/web/i18n') : '/i18n',
|
||||||
reloadOnPrerender: process.env.NODE_ENV === 'development'
|
reloadOnPrerender: process.env.NODE_ENV === 'development'
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "app",
|
"name": "app",
|
||||||
"version": "4.8.4",
|
"version": "4.8.5",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
@ -71,12 +71,12 @@
|
|||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/jsonwebtoken": "^9.0.3",
|
"@types/jsonwebtoken": "^9.0.3",
|
||||||
"@types/lodash": "^4.14.191",
|
"@types/lodash": "^4.14.191",
|
||||||
"@types/node": "^20.8.5",
|
"@types/node": "^20.14.2",
|
||||||
"@types/react": "18.3.0",
|
"@types/react": "18.3.0",
|
||||||
"@types/react-dom": "18.3.0",
|
"@types/react-dom": "18.3.0",
|
||||||
"@types/react-syntax-highlighter": "^15.5.6",
|
"@types/react-syntax-highlighter": "^15.5.6",
|
||||||
"@types/request-ip": "^0.0.37",
|
"@types/request-ip": "^0.0.37",
|
||||||
"eslint": "8.34.0",
|
"eslint": "8.56.0",
|
||||||
"eslint-config-next": "14.2.3",
|
"eslint-config-next": "14.2.3",
|
||||||
"nextjs-node-loader": "^1.1.5",
|
"nextjs-node-loader": "^1.1.5",
|
||||||
"typescript": "4.9.5"
|
"typescript": "4.9.5"
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
### FastGPT V4.8.4
|
### FastGPT V4.8.5
|
||||||
|
|
||||||
1. 新增 - 应用使用新权限系统。
|
1. 新增 - 应用使用新权限系统。
|
||||||
2. 新增 - 应用支持文件夹。
|
2. 新增 - 应用支持文件夹。
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { connectToDatabase } from '@/service/mongo';
|
|||||||
import { authFileToken } from '@fastgpt/service/support/permission/controller';
|
import { authFileToken } from '@fastgpt/service/support/permission/controller';
|
||||||
import { getDownloadStream, getFileById } from '@fastgpt/service/common/file/gridfs/controller';
|
import { getDownloadStream, getFileById } from '@fastgpt/service/common/file/gridfs/controller';
|
||||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
|
import { stream2Encoding } from '@fastgpt/service/common/file/gridfs/utils';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
try {
|
try {
|
||||||
@ -17,7 +18,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
throw new Error('fileId is empty');
|
throw new Error('fileId is empty');
|
||||||
}
|
}
|
||||||
|
|
||||||
const [file, { fileStream, encoding }] = await Promise.all([
|
const [file, fileStream] = await Promise.all([
|
||||||
getFileById({ bucketName, fileId }),
|
getFileById({ bucketName, fileId }),
|
||||||
getDownloadStream({ bucketName, fileId })
|
getDownloadStream({ bucketName, fileId })
|
||||||
]);
|
]);
|
||||||
@ -26,16 +27,26 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
return Promise.reject(CommonErrEnum.fileNotFound);
|
return Promise.reject(CommonErrEnum.fileNotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { stream, encoding } = await (async () => {
|
||||||
|
if (file.metadata?.encoding) {
|
||||||
|
return {
|
||||||
|
stream: fileStream,
|
||||||
|
encoding: file.metadata.encoding
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return stream2Encoding(fileStream);
|
||||||
|
})();
|
||||||
|
|
||||||
res.setHeader('Content-Type', `${file.contentType}; charset=${encoding}`);
|
res.setHeader('Content-Type', `${file.contentType}; charset=${encoding}`);
|
||||||
res.setHeader('Cache-Control', 'public, max-age=3600');
|
res.setHeader('Cache-Control', 'public, max-age=3600');
|
||||||
res.setHeader('Content-Disposition', `inline; filename="${encodeURIComponent(file.filename)}"`);
|
res.setHeader('Content-Disposition', `inline; filename="${encodeURIComponent(file.filename)}"`);
|
||||||
|
|
||||||
fileStream.pipe(res);
|
stream.pipe(res);
|
||||||
|
|
||||||
fileStream.on('error', () => {
|
stream.on('error', () => {
|
||||||
res.status(500).end();
|
res.status(500).end();
|
||||||
});
|
});
|
||||||
fileStream.on('end', () => {
|
stream.on('end', () => {
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -47,6 +58,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
|||||||
}
|
}
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
responseLimit: '32mb'
|
responseLimit: '100mb'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
50
projects/app/src/pages/api/core/app/copy.ts
Normal file
50
projects/app/src/pages/api/core/app/copy.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||||
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
|
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||||
|
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
|
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||||
|
import { onCreateApp } from './create';
|
||||||
|
|
||||||
|
export type copyAppQuery = {};
|
||||||
|
|
||||||
|
export type copyAppBody = { appId: string };
|
||||||
|
|
||||||
|
export type copyAppResponse = {
|
||||||
|
appId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
async function handler(
|
||||||
|
req: ApiRequestProps<copyAppBody, copyAppQuery>,
|
||||||
|
res: ApiResponseType<any>
|
||||||
|
): Promise<copyAppResponse> {
|
||||||
|
const [{ app, tmbId }] = await Promise.all([
|
||||||
|
authApp({
|
||||||
|
req,
|
||||||
|
authToken: true,
|
||||||
|
per: WritePermissionVal,
|
||||||
|
appId: req.body.appId
|
||||||
|
}),
|
||||||
|
authUserPer({
|
||||||
|
req,
|
||||||
|
authToken: true,
|
||||||
|
per: WritePermissionVal
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
const appId = await onCreateApp({
|
||||||
|
parentId: app.parentId,
|
||||||
|
name: app.name + ' Copy',
|
||||||
|
intro: app.intro,
|
||||||
|
avatar: app.avatar,
|
||||||
|
type: app.type,
|
||||||
|
modules: app.modules,
|
||||||
|
edges: app.edges,
|
||||||
|
teamId: app.teamId,
|
||||||
|
tmbId,
|
||||||
|
pluginData: app.pluginData
|
||||||
|
});
|
||||||
|
|
||||||
|
return { appId };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default NextAPI(handler);
|
||||||
@ -42,27 +42,22 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|||||||
const { nodes: formatNodes } = beforeUpdateAppFormat({ nodes });
|
const { nodes: formatNodes } = beforeUpdateAppFormat({ nodes });
|
||||||
|
|
||||||
// 更新模型
|
// 更新模型
|
||||||
await MongoApp.updateOne(
|
await MongoApp.findByIdAndUpdate(appId, {
|
||||||
{
|
...parseParentIdInMongo(parentId),
|
||||||
_id: appId
|
...(name && { name }),
|
||||||
},
|
...(type && { type }),
|
||||||
{
|
...(avatar && { avatar }),
|
||||||
...parseParentIdInMongo(parentId),
|
...(intro !== undefined && { intro }),
|
||||||
name,
|
...(defaultPermission && { defaultPermission }),
|
||||||
type,
|
...(teamTags && { teamTags }),
|
||||||
avatar,
|
...(formatNodes && {
|
||||||
intro,
|
modules: formatNodes
|
||||||
defaultPermission,
|
}),
|
||||||
...(teamTags && teamTags),
|
...(edges && {
|
||||||
...(formatNodes && {
|
edges
|
||||||
modules: formatNodes
|
}),
|
||||||
}),
|
...(chatConfig && { chatConfig })
|
||||||
...(edges && {
|
});
|
||||||
edges
|
|
||||||
}),
|
|
||||||
...(chatConfig && { chatConfig })
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NextAPI(handler);
|
export default NextAPI(handler);
|
||||||
|
|||||||
@ -1,50 +1,42 @@
|
|||||||
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 { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
import { MongoChat } from '@fastgpt/service/core/chat/chatSchema';
|
||||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||||
import { DelHistoryProps } from '@/global/core/chat/api';
|
import { DelHistoryProps } from '@/global/core/chat/api';
|
||||||
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||||
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
|
import { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||||
|
|
||||||
/* clear chat history */
|
/* clear chat history */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
async function handler(req: ApiRequestProps<{}, DelHistoryProps>, res: NextApiResponse) {
|
||||||
try {
|
const { appId, chatId } = req.query;
|
||||||
await connectToDatabase();
|
|
||||||
const { appId, chatId, shareId, outLinkUid } = req.query as DelHistoryProps;
|
|
||||||
|
|
||||||
await autChatCrud({
|
await autChatCrud({
|
||||||
req,
|
req,
|
||||||
authToken: true,
|
authToken: true,
|
||||||
appId,
|
...req.query,
|
||||||
chatId,
|
per: 'w'
|
||||||
shareId,
|
});
|
||||||
outLinkUid,
|
|
||||||
per: 'w'
|
|
||||||
});
|
|
||||||
|
|
||||||
await mongoSessionRun(async (session) => {
|
await mongoSessionRun(async (session) => {
|
||||||
await MongoChatItem.deleteMany(
|
await MongoChatItem.deleteMany(
|
||||||
{
|
{
|
||||||
appId,
|
appId,
|
||||||
chatId
|
chatId
|
||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
await MongoChat.findOneAndRemove(
|
await MongoChat.findOneAndRemove(
|
||||||
{
|
{
|
||||||
appId,
|
appId,
|
||||||
chatId
|
chatId
|
||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
jsonRes(res);
|
jsonRes(res);
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default NextAPI(handler);
|
||||||
|
|||||||
@ -1,42 +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 { 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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,37 +4,30 @@ import { connectToDatabase } from '@/service/mongo';
|
|||||||
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
import { MongoChatItem } from '@fastgpt/service/core/chat/chatItemSchema';
|
||||||
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||||
import type { DeleteChatItemProps } from '@/global/core/chat/api.d';
|
import type { DeleteChatItemProps } from '@/global/core/chat/api.d';
|
||||||
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
|
import { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
async function handler(req: ApiRequestProps<{}, DeleteChatItemProps>, res: NextApiResponse) {
|
||||||
try {
|
const { appId, chatId, contentId, shareId, outLinkUid } = req.query;
|
||||||
await connectToDatabase();
|
|
||||||
const { appId, chatId, contentId, shareId, outLinkUid } = req.query as DeleteChatItemProps;
|
|
||||||
|
|
||||||
if (!contentId || !chatId) {
|
if (!contentId || !chatId) {
|
||||||
return jsonRes(res);
|
return jsonRes(res);
|
||||||
}
|
|
||||||
|
|
||||||
await autChatCrud({
|
|
||||||
req,
|
|
||||||
authToken: true,
|
|
||||||
appId,
|
|
||||||
chatId,
|
|
||||||
shareId,
|
|
||||||
outLinkUid,
|
|
||||||
per: 'w'
|
|
||||||
});
|
|
||||||
|
|
||||||
await MongoChatItem.deleteOne({
|
|
||||||
appId,
|
|
||||||
chatId,
|
|
||||||
dataId: contentId
|
|
||||||
});
|
|
||||||
|
|
||||||
jsonRes(res);
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await autChatCrud({
|
||||||
|
req,
|
||||||
|
authToken: true,
|
||||||
|
...req.query,
|
||||||
|
per: 'w'
|
||||||
|
});
|
||||||
|
|
||||||
|
await MongoChatItem.deleteOne({
|
||||||
|
appId,
|
||||||
|
chatId,
|
||||||
|
dataId: contentId
|
||||||
|
});
|
||||||
|
|
||||||
|
jsonRes(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default NextAPI(handler);
|
||||||
|
|||||||
@ -1,40 +1,30 @@
|
|||||||
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 { UpdateHistoryProps } from '@/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 { autChatCrud } from '@/service/support/permission/auth/chat';
|
import { autChatCrud } from '@/service/support/permission/auth/chat';
|
||||||
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
|
import { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||||
|
|
||||||
/* update chat top, custom title */
|
/* update chat top, custom title */
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
async function handler(req: ApiRequestProps<UpdateHistoryProps>, res: NextApiResponse) {
|
||||||
try {
|
const { appId, chatId, customTitle, top } = req.body;
|
||||||
await connectToDatabase();
|
await autChatCrud({
|
||||||
const { appId, chatId, teamId, shareId, outLinkUid, customTitle, top } =
|
req,
|
||||||
req.body as UpdateHistoryProps;
|
authToken: true,
|
||||||
await autChatCrud({
|
...req.body,
|
||||||
req,
|
per: 'w'
|
||||||
authToken: true,
|
});
|
||||||
appId,
|
|
||||||
teamId,
|
|
||||||
chatId,
|
|
||||||
shareId,
|
|
||||||
outLinkUid,
|
|
||||||
per: 'w'
|
|
||||||
});
|
|
||||||
|
|
||||||
await MongoChat.findOneAndUpdate(
|
await MongoChat.findOneAndUpdate(
|
||||||
{ appId, chatId },
|
{ appId, chatId },
|
||||||
{
|
{
|
||||||
updateTime: new Date(),
|
updateTime: new Date(),
|
||||||
...(customTitle !== undefined && { customTitle }),
|
...(customTitle !== undefined && { customTitle }),
|
||||||
...(top !== undefined && { top })
|
...(top !== undefined && { top })
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
jsonRes(res);
|
jsonRes(res);
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default NextAPI(handler);
|
||||||
|
|||||||
@ -3,18 +3,17 @@ import { getPublishList, postRevertVersion } from '@/web/core/app/api/version';
|
|||||||
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
||||||
import CustomRightDrawer from '@fastgpt/web/components/common/MyDrawer/CustomRightDrawer';
|
import CustomRightDrawer from '@fastgpt/web/components/common/MyDrawer/CustomRightDrawer';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
|
||||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||||
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
|
import { formatTime2YMDHM } from '@fastgpt/global/common/string/time';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
|
import { AppVersionSchemaType } from '@fastgpt/global/core/app/version';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
|
||||||
import { AppContext } from './context';
|
import { AppContext } from './context';
|
||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
import { AppSchema } from '@fastgpt/global/core/app/type';
|
import { AppSchema } from '@fastgpt/global/core/app/type';
|
||||||
|
import PopoverConfirm from '@fastgpt/web/components/common/MyPopover/PopoverConfirm';
|
||||||
|
|
||||||
export type InitProps = {
|
export type InitProps = {
|
||||||
nodes: AppSchema['modules'];
|
nodes: AppSchema['modules'];
|
||||||
@ -33,11 +32,11 @@ const PublishHistoriesSlider = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { appT } = useI18n();
|
const { appT } = useI18n();
|
||||||
const { openConfirm, ConfirmModal } = useConfirm({
|
|
||||||
content: t('core.workflow.publish.OnRevert version confirm')
|
|
||||||
});
|
|
||||||
|
|
||||||
const { appDetail, setAppDetail } = useContextSelector(AppContext, (v) => v);
|
const { appDetail, setAppDetail, reloadAppLatestVersion } = useContextSelector(
|
||||||
|
AppContext,
|
||||||
|
(v) => v
|
||||||
|
);
|
||||||
const appId = appDetail._id;
|
const appId = appDetail._id;
|
||||||
|
|
||||||
const [selectedHistoryId, setSelectedHistoryId] = useState<string>();
|
const [selectedHistoryId, setSelectedHistoryId] = useState<string>();
|
||||||
@ -73,8 +72,8 @@ const PublishHistoriesSlider = ({
|
|||||||
[initData, onClose]
|
[initData, onClose]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { mutate: onRevert, isLoading: isReverting } = useRequest({
|
const { runAsync: onRevert } = useRequest2(
|
||||||
mutationFn: async (data: AppVersionSchemaType) => {
|
async (data: AppVersionSchemaType) => {
|
||||||
if (!appId) return;
|
if (!appId) return;
|
||||||
await postRevertVersion(appId, {
|
await postRevertVersion(appId, {
|
||||||
versionId: data._id,
|
versionId: data._id,
|
||||||
@ -90,10 +89,14 @@ const PublishHistoriesSlider = ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
onCloseSlider(data);
|
onCloseSlider(data);
|
||||||
|
reloadAppLatestVersion();
|
||||||
|
},
|
||||||
|
{
|
||||||
|
successToast: appT('version.Revert success')
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
const showLoading = isLoading || isReverting;
|
const showLoading = isLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -173,24 +176,28 @@ const PublishHistoriesSlider = ({
|
|||||||
{formatTime2YMDHM(item.time)}
|
{formatTime2YMDHM(item.time)}
|
||||||
</Box>
|
</Box>
|
||||||
{item._id === selectedHistoryId && (
|
{item._id === selectedHistoryId && (
|
||||||
<MyTooltip label={t('core.workflow.publish.OnRevert version')}>
|
<PopoverConfirm
|
||||||
<MyIcon
|
showCancel
|
||||||
name={'core/workflow/revertVersion'}
|
content={t('core.workflow.publish.OnRevert version confirm')}
|
||||||
w={'20px'}
|
onConfirm={() => onRevert(item)}
|
||||||
color={'primary.600'}
|
Trigger={
|
||||||
onClick={(e) => {
|
<Box>
|
||||||
e.stopPropagation();
|
<MyTooltip label={t('core.workflow.publish.OnRevert version')}>
|
||||||
openConfirm(() => onRevert(item))();
|
<MyIcon
|
||||||
}}
|
name={'core/workflow/revertVersion'}
|
||||||
/>
|
w={'20px'}
|
||||||
</MyTooltip>
|
color={'primary.600'}
|
||||||
|
/>
|
||||||
|
</MyTooltip>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</ScrollList>
|
</ScrollList>
|
||||||
</CustomRightDrawer>
|
</CustomRightDrawer>
|
||||||
<ConfirmModal />
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -98,7 +98,6 @@ const AppCard = () => {
|
|||||||
</Button>
|
</Button>
|
||||||
{appDetail.permission.hasWritePer && feConfigs?.show_team_chat && (
|
{appDetail.permission.hasWritePer && feConfigs?.show_team_chat && (
|
||||||
<Button
|
<Button
|
||||||
mr={3}
|
|
||||||
size={['sm', 'md']}
|
size={['sm', 'md']}
|
||||||
variant={'whitePrimary'}
|
variant={'whitePrimary'}
|
||||||
leftIcon={<DragHandleIcon w={'16px'} />}
|
leftIcon={<DragHandleIcon w={'16px'} />}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import MyIcon from '@fastgpt/web/components/common/Icon';
|
|||||||
import { compareWorkflow } from '@/web/core/workflow/utils';
|
import { compareWorkflow } from '@/web/core/workflow/utils';
|
||||||
import MyTag from '@fastgpt/web/components/common/Tag/index';
|
import MyTag from '@fastgpt/web/components/common/Tag/index';
|
||||||
import { publishStatusStyle } from '../constants';
|
import { publishStatusStyle } from '../constants';
|
||||||
|
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||||
|
|
||||||
const Header = ({
|
const Header = ({
|
||||||
appForm,
|
appForm,
|
||||||
@ -134,7 +135,13 @@ const Header = ({
|
|||||||
<PopoverConfirm
|
<PopoverConfirm
|
||||||
showCancel
|
showCancel
|
||||||
content={t('core.app.Publish Confirm')}
|
content={t('core.app.Publish Confirm')}
|
||||||
Trigger={<Button isDisabled={isPublished}>{t('core.app.Publish')}</Button>}
|
Trigger={
|
||||||
|
<Box>
|
||||||
|
<MyTooltip label={t('core.app.Publish app tip')}>
|
||||||
|
<Button isDisabled={isPublished}>{t('core.app.Publish')}</Button>
|
||||||
|
</MyTooltip>
|
||||||
|
</Box>
|
||||||
|
}
|
||||||
onConfirm={() => onSubmitPublish(appForm)}
|
onConfirm={() => onSubmitPublish(appForm)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -7,15 +7,22 @@ import { useContextSelector } from 'use-context-selector';
|
|||||||
import { AppContext, TabEnum } from '../context';
|
import { AppContext, TabEnum } from '../context';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { Flex } from '@chakra-ui/react';
|
import { Flex } from '@chakra-ui/react';
|
||||||
|
import { useBeforeunload } from '@fastgpt/web/hooks/useBeforeunload';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
const Logs = dynamic(() => import('../Logs/index'));
|
const Logs = dynamic(() => import('../Logs/index'));
|
||||||
const PublishChannel = dynamic(() => import('../Publish'));
|
const PublishChannel = dynamic(() => import('../Publish'));
|
||||||
|
|
||||||
const SimpleEdit = () => {
|
const SimpleEdit = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const { currentTab } = useContextSelector(AppContext, (v) => v);
|
const { currentTab } = useContextSelector(AppContext, (v) => v);
|
||||||
|
|
||||||
const [appForm, setAppForm] = useState(getDefaultAppForm());
|
const [appForm, setAppForm] = useState(getDefaultAppForm());
|
||||||
|
|
||||||
|
useBeforeunload({
|
||||||
|
tip: t('core.common.tip.leave page')
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex h={'100%'} flexDirection={'column'} pr={3} pb={3}>
|
<Flex h={'100%'} flexDirection={'column'} pr={3} pb={3}>
|
||||||
<Header appForm={appForm} setAppForm={setAppForm} />
|
<Header appForm={appForm} setAppForm={setAppForm} />
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import { useRouter } from 'next/router';
|
|||||||
|
|
||||||
import AppCard from '../WorkflowComponents/AppCard';
|
import AppCard from '../WorkflowComponents/AppCard';
|
||||||
import { uiWorkflow2StoreWorkflow } from '../WorkflowComponents/utils';
|
import { uiWorkflow2StoreWorkflow } from '../WorkflowComponents/utils';
|
||||||
|
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||||
const PublishHistories = dynamic(() => import('../PublishHistoriesSlider'));
|
const PublishHistories = dynamic(() => import('../PublishHistoriesSlider'));
|
||||||
|
|
||||||
const Header = () => {
|
const Header = () => {
|
||||||
@ -53,6 +54,7 @@ const Header = () => {
|
|||||||
router.push('/app/list');
|
router.push('/app/list');
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
}, [onSaveWorkflow, router]);
|
}, [onSaveWorkflow, router]);
|
||||||
|
|
||||||
// effect
|
// effect
|
||||||
useBeforeunload({
|
useBeforeunload({
|
||||||
callback: onSaveWorkflow,
|
callback: onSaveWorkflow,
|
||||||
@ -152,13 +154,17 @@ const Header = () => {
|
|||||||
showCancel
|
showCancel
|
||||||
content={t('core.app.Publish Confirm')}
|
content={t('core.app.Publish Confirm')}
|
||||||
Trigger={
|
Trigger={
|
||||||
<Button
|
<Box>
|
||||||
ml={[2, 4]}
|
<MyTooltip label={t('core.app.Publish app tip')}>
|
||||||
size={'sm'}
|
<Button
|
||||||
leftIcon={<MyIcon name={'common/publishFill'} w={['14px', '16px']} />}
|
ml={[2, 4]}
|
||||||
>
|
size={'sm'}
|
||||||
{t('core.app.Publish')}
|
leftIcon={<MyIcon name={'common/publishFill'} w={['14px', '16px']} />}
|
||||||
</Button>
|
>
|
||||||
|
{t('core.app.Publish')}
|
||||||
|
</Button>
|
||||||
|
</MyTooltip>
|
||||||
|
</Box>
|
||||||
}
|
}
|
||||||
onConfirm={() => onclickPublish()}
|
onConfirm={() => onclickPublish()}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -520,6 +520,7 @@ const WorkflowContextProvider = ({
|
|||||||
historiesDefaultData ||
|
historiesDefaultData ||
|
||||||
isSaving ||
|
isSaving ||
|
||||||
nodes.length === 0 ||
|
nodes.length === 0 ||
|
||||||
|
edges.length === 0 ||
|
||||||
!!workflowDebugData
|
!!workflowDebugData
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -44,6 +44,7 @@ type AppContextType = {
|
|||||||
chatConfig: AppChatConfigType;
|
chatConfig: AppChatConfigType;
|
||||||
}
|
}
|
||||||
| undefined;
|
| undefined;
|
||||||
|
reloadAppLatestVersion: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AppContext = createContext<AppContextType>({
|
export const AppContext = createContext<AppContextType>({
|
||||||
@ -72,7 +73,10 @@ export const AppContext = createContext<AppContextType>({
|
|||||||
onPublish: function (data: PostPublishAppProps): Promise<void> {
|
onPublish: function (data: PostPublishAppProps): Promise<void> {
|
||||||
throw new Error('Function not implemented.');
|
throw new Error('Function not implemented.');
|
||||||
},
|
},
|
||||||
appLatestVersion: undefined
|
appLatestVersion: undefined,
|
||||||
|
reloadAppLatestVersion: function (): void {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const AppContextProvider = ({ children }: { children: ReactNode }) => {
|
const AppContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
@ -190,7 +194,8 @@ const AppContextProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
onOpenTeamTagModal,
|
onOpenTeamTagModal,
|
||||||
onDelApp,
|
onDelApp,
|
||||||
onPublish,
|
onPublish,
|
||||||
appLatestVersion
|
appLatestVersion,
|
||||||
|
reloadAppLatestVersion
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import { Box, Grid, Flex, IconButton, border } from '@chakra-ui/react';
|
import { Box, Grid, Flex, IconButton } from '@chakra-ui/react';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { delAppById, putAppById } from '@/web/core/app/api';
|
import { delAppById, putAppById } from '@/web/core/app/api';
|
||||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||||
@ -34,16 +34,15 @@ const EditResourceModal = dynamic(() => import('@/components/common/Modal/EditRe
|
|||||||
const ConfigPerModal = dynamic(() => import('@/components/support/permission/ConfigPerModal'));
|
const ConfigPerModal = dynamic(() => import('@/components/support/permission/ConfigPerModal'));
|
||||||
|
|
||||||
import type { EditHttpPluginProps } from './HttpPluginEditModal';
|
import type { EditHttpPluginProps } from './HttpPluginEditModal';
|
||||||
|
import { postCopyApp } from '@/web/core/app/api/app';
|
||||||
const HttpEditModal = dynamic(() => import('./HttpPluginEditModal'));
|
const HttpEditModal = dynamic(() => import('./HttpPluginEditModal'));
|
||||||
|
|
||||||
const ListItem = () => {
|
const ListItem = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { appT } = useI18n();
|
const { appT } = useI18n();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { myApps, loadMyApps, onUpdateApp, setMoveAppId, folderDetail } = useContextSelector(
|
const { myApps, loadMyApps, onUpdateApp, setMoveAppId, folderDetail, parentId } =
|
||||||
AppListContext,
|
useContextSelector(AppListContext, (v) => v);
|
||||||
(v) => v
|
|
||||||
);
|
|
||||||
const [loadingAppId, setLoadingAppId] = useState<string>();
|
const [loadingAppId, setLoadingAppId] = useState<string>();
|
||||||
|
|
||||||
const [editedApp, setEditedApp] = useState<EditResourceInfoFormType>();
|
const [editedApp, setEditedApp] = useState<EditResourceInfoFormType>();
|
||||||
@ -68,27 +67,33 @@ const ListItem = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const { openConfirm, ConfirmModal } = useConfirm({
|
const { openConfirm: openConfirmDel, ConfirmModal: DelConfirmModal } = useConfirm({
|
||||||
type: 'delete'
|
type: 'delete'
|
||||||
});
|
});
|
||||||
|
const { runAsync: onclickDelApp } = useRequest2(
|
||||||
const { run: onclickDelApp } = useRequest2(
|
|
||||||
(id: string) => {
|
(id: string) => {
|
||||||
setLoadingAppId(id);
|
|
||||||
return delAppById(id);
|
return delAppById(id);
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
loadMyApps();
|
loadMyApps();
|
||||||
},
|
},
|
||||||
onFinally() {
|
|
||||||
setLoadingAppId(undefined);
|
|
||||||
},
|
|
||||||
successToast: t('common.Delete Success'),
|
successToast: t('common.Delete Success'),
|
||||||
errorToast: t('common.Delete Failed')
|
errorToast: t('common.Delete Failed')
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { openConfirm: openConfirmCopy, ConfirmModal: ConfirmCopyModal } = useConfirm({
|
||||||
|
content: appT('Confirm copy app tip')
|
||||||
|
});
|
||||||
|
const { runAsync: onclickCopy } = useRequest2(postCopyApp, {
|
||||||
|
onSuccess({ appId }) {
|
||||||
|
router.push(`/app/detail?appId=${appId}`);
|
||||||
|
loadMyApps();
|
||||||
|
},
|
||||||
|
successToast: appT('Create copy success')
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Grid
|
<Grid
|
||||||
@ -218,6 +223,16 @@ const ListItem = () => {
|
|||||||
: [])
|
: [])
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
icon: 'copy',
|
||||||
|
label: appT('Copy one app'),
|
||||||
|
onClick: () =>
|
||||||
|
openConfirmCopy(() => onclickCopy({ appId: app._id }))()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@ -238,7 +253,7 @@ const ListItem = () => {
|
|||||||
icon: 'delete',
|
icon: 'delete',
|
||||||
label: t('common.Delete'),
|
label: t('common.Delete'),
|
||||||
onClick: () =>
|
onClick: () =>
|
||||||
openConfirm(
|
openConfirmDel(
|
||||||
() => onclickDelApp(app._id),
|
() => onclickDelApp(app._id),
|
||||||
undefined,
|
undefined,
|
||||||
app.type === AppTypeEnum.folder
|
app.type === AppTypeEnum.folder
|
||||||
@ -280,7 +295,9 @@ const ListItem = () => {
|
|||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
{myApps.length === 0 && <EmptyTip text={'还没有应用,快去创建一个吧!'} pt={'30vh'} />}
|
{myApps.length === 0 && <EmptyTip text={'还没有应用,快去创建一个吧!'} pt={'30vh'} />}
|
||||||
<ConfirmModal />
|
|
||||||
|
<DelConfirmModal />
|
||||||
|
<ConfirmCopyModal />
|
||||||
{!!editedApp && (
|
{!!editedApp && (
|
||||||
<EditResourceModal
|
<EditResourceModal
|
||||||
{...editedApp}
|
{...editedApp}
|
||||||
|
|||||||
@ -163,7 +163,7 @@ const ChatHistorySlider = ({
|
|||||||
<Flex w={'100%'} px={[2, 5]} h={'36px'} my={5} alignItems={'center'}>
|
<Flex w={'100%'} px={[2, 5]} h={'36px'} my={5} alignItems={'center'}>
|
||||||
{!isPc && appId && (
|
{!isPc && appId && (
|
||||||
<Tabs
|
<Tabs
|
||||||
w={'180px'}
|
flex={'1 0 0'}
|
||||||
mr={2}
|
mr={2}
|
||||||
list={[
|
list={[
|
||||||
{ label: t('core.chat.Recent use'), id: TabEnum.recently },
|
{ label: t('core.chat.Recent use'), id: TabEnum.recently },
|
||||||
@ -176,7 +176,7 @@ const ChatHistorySlider = ({
|
|||||||
)}
|
)}
|
||||||
<Button
|
<Button
|
||||||
variant={'whitePrimary'}
|
variant={'whitePrimary'}
|
||||||
flex={1}
|
flex={['0', 1]}
|
||||||
h={'100%'}
|
h={'100%'}
|
||||||
color={'primary.600'}
|
color={'primary.600'}
|
||||||
borderRadius={'xl'}
|
borderRadius={'xl'}
|
||||||
|
|||||||
@ -298,7 +298,7 @@ const OutLink = ({
|
|||||||
onClose={onCloseSlider}
|
onClose={onCloseSlider}
|
||||||
>
|
>
|
||||||
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
|
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
|
||||||
<DrawerContent maxWidth={'250px'} boxShadow={'2px 0 10px rgba(0,0,0,0.15)'}>
|
<DrawerContent maxWidth={'75vw'} boxShadow={'2px 0 10px rgba(0,0,0,0.15)'}>
|
||||||
{children}
|
{children}
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|||||||
@ -83,8 +83,8 @@ const OutLink = () => {
|
|||||||
data: {
|
data: {
|
||||||
messages: prompts,
|
messages: prompts,
|
||||||
variables: {
|
variables: {
|
||||||
...customVariables,
|
...variables,
|
||||||
...variables
|
...customVariables
|
||||||
},
|
},
|
||||||
appId,
|
appId,
|
||||||
teamId,
|
teamId,
|
||||||
@ -290,7 +290,7 @@ const OutLink = () => {
|
|||||||
onClose={onCloseSlider}
|
onClose={onCloseSlider}
|
||||||
>
|
>
|
||||||
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
|
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
|
||||||
<DrawerContent maxWidth={'250px'}>{children}</DrawerContent>
|
<DrawerContent maxWidth={'75vw'}>{children}</DrawerContent>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
})(
|
})(
|
||||||
|
|||||||
@ -170,12 +170,14 @@ const FileSelector = ({
|
|||||||
const items = e.dataTransfer.items;
|
const items = e.dataTransfer.items;
|
||||||
const fileList: SelectFileItemType[] = [];
|
const fileList: SelectFileItemType[] = [];
|
||||||
|
|
||||||
if (e.dataTransfer.items.length <= 1) {
|
const firstEntry = items[0].webkitGetAsEntry();
|
||||||
const traverseFileTree = async (item: any) => {
|
|
||||||
return new Promise<void>((resolve, reject) => {
|
if (firstEntry?.isDirectory && items.length === 1) {
|
||||||
if (item.isFile) {
|
{
|
||||||
item.file((file: File) => {
|
const readFile = (entry: any) => {
|
||||||
const folderPath = (item.fullPath || '').split('/').slice(2, -1).join('/');
|
return new Promise((resolve) => {
|
||||||
|
entry.file((file: File) => {
|
||||||
|
const folderPath = (entry.fullPath || '').split('/').slice(2, -1).join('/');
|
||||||
|
|
||||||
if (filterTypeReg.test(file.name)) {
|
if (filterTypeReg.test(file.name)) {
|
||||||
fileList.push({
|
fileList.push({
|
||||||
@ -184,24 +186,45 @@ const FileSelector = ({
|
|||||||
file
|
file
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
resolve();
|
resolve(file);
|
||||||
});
|
});
|
||||||
} else if (item.isDirectory) {
|
});
|
||||||
const dirReader = item.createReader();
|
};
|
||||||
|
const traverseFileTree = (dirReader: any) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
let fileNum = 0;
|
||||||
dirReader.readEntries(async (entries: any[]) => {
|
dirReader.readEntries(async (entries: any[]) => {
|
||||||
for (let i = 0; i < entries.length; i++) {
|
for await (const entry of entries) {
|
||||||
await traverseFileTree(entries[i]);
|
if (entry.isFile) {
|
||||||
|
await readFile(entry);
|
||||||
|
fileNum++;
|
||||||
|
} else if (entry.isDirectory) {
|
||||||
|
await traverseFileTree(entry.createReader());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
for await (const item of items) {
|
// chrome: readEntries will return 100 entries at most
|
||||||
await traverseFileTree(item.webkitGetAsEntry());
|
if (fileNum === 100) {
|
||||||
|
await traverseFileTree(dirReader);
|
||||||
|
}
|
||||||
|
resolve('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
for await (const item of items) {
|
||||||
|
const entry = item.webkitGetAsEntry();
|
||||||
|
if (entry) {
|
||||||
|
if (entry.isFile) {
|
||||||
|
await readFile(entry);
|
||||||
|
} else if (entry.isDirectory) {
|
||||||
|
//@ts-ignore
|
||||||
|
await traverseFileTree(entry.createReader());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else if (firstEntry?.isFile) {
|
||||||
const files = Array.from(e.dataTransfer.files);
|
const files = Array.from(e.dataTransfer.files);
|
||||||
let isErr = files.some((item) => item.type === '');
|
let isErr = files.some((item) => item.type === '');
|
||||||
if (isErr) {
|
if (isErr) {
|
||||||
@ -220,6 +243,11 @@ const FileSelector = ({
|
|||||||
file
|
file
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
return toast({
|
||||||
|
title: fileT('upload error description'),
|
||||||
|
status: 'error'
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
selectFileCallback(fileList.slice(0, maxCount));
|
selectFileCallback(fileList.slice(0, maxCount));
|
||||||
|
|||||||
@ -55,6 +55,12 @@ export async function insertData2Dataset({
|
|||||||
|
|
||||||
if (!indexes.find((index) => index.defaultIndex)) {
|
if (!indexes.find((index) => index.defaultIndex)) {
|
||||||
indexes.unshift(getDefaultIndex({ q, a }));
|
indexes.unshift(getDefaultIndex({ q, a }));
|
||||||
|
} else if (q && a && !indexes.find((index) => index.text === q)) {
|
||||||
|
// push a q index
|
||||||
|
indexes.push({
|
||||||
|
defaultIndex: false,
|
||||||
|
text: q
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
indexes = indexes.slice(0, 6);
|
indexes = indexes.slice(0, 6);
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import { authOutLinkValid } from '@fastgpt/service/support/permission/publish/au
|
|||||||
import { AuthUserTypeEnum, ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
import { AuthUserTypeEnum, ReadPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
||||||
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
import { OutLinkChatAuthProps } from '@fastgpt/global/support/permission/chat';
|
||||||
|
import { addLog } from '@fastgpt/service/common/system/log';
|
||||||
/*
|
/*
|
||||||
outLink: Must be the owner
|
outLink: Must be the owner
|
||||||
token: team owner and chat owner have all permissions
|
token: team owner and chat owner have all permissions
|
||||||
@ -55,6 +56,7 @@ export async function autChatCrud({
|
|||||||
// auth team space chat
|
// auth team space chat
|
||||||
if (spaceTeamId && teamToken) {
|
if (spaceTeamId && teamToken) {
|
||||||
const { uid } = await authTeamSpaceToken({ teamId: spaceTeamId, teamToken });
|
const { uid } = await authTeamSpaceToken({ teamId: spaceTeamId, teamToken });
|
||||||
|
addLog.debug('Auth team token', { uid, spaceTeamId, teamToken, chatUid: chat.outLinkUid });
|
||||||
if (!chat || (String(chat.teamId) === String(spaceTeamId) && chat.outLinkUid === uid)) {
|
if (!chat || (String(chat.teamId) === String(spaceTeamId) && chat.outLinkUid === uid)) {
|
||||||
return { uid };
|
return { uid };
|
||||||
}
|
}
|
||||||
|
|||||||
16
projects/app/src/types/i18n.d.ts
vendored
16
projects/app/src/types/i18n.d.ts
vendored
@ -1,12 +1,12 @@
|
|||||||
import 'i18next';
|
import 'i18next';
|
||||||
import common from '../../i18n/zh/common.json';
|
import common from '@fastgpt/web/i18n/zh/common.json';
|
||||||
import dataset from '../../i18n/zh/dataset.json';
|
import dataset from '@fastgpt/web/i18n/zh/dataset.json';
|
||||||
import app from '../../i18n/zh/app.json';
|
import app from '@fastgpt/web/i18n/zh/app.json';
|
||||||
import file from '../../i18n/zh/file.json';
|
import file from '@fastgpt/web/i18n/zh/file.json';
|
||||||
import publish from '../../i18n/zh/publish.json';
|
import publish from '@fastgpt/web/i18n/zh/publish.json';
|
||||||
import workflow from '../../i18n/zh/workflow.json';
|
import workflow from '@fastgpt/web/i18n/zh/workflow.json';
|
||||||
import user from '../../i18n/zh/user.json';
|
import user from '@fastgpt/web/i18n/zh/user.json';
|
||||||
import chat from '../../i18n/zh/chat.json';
|
import chat from '@fastgpt/web/i18n/zh/chat.json';
|
||||||
|
|
||||||
export interface I18nNamespaces {
|
export interface I18nNamespaces {
|
||||||
common: typeof common;
|
common: typeof common;
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export const postUploadFiles = (
|
|||||||
onUploadProgress: (progressEvent: AxiosProgressEvent) => void
|
onUploadProgress: (progressEvent: AxiosProgressEvent) => void
|
||||||
) =>
|
) =>
|
||||||
POST<string>('/common/file/upload', data, {
|
POST<string>('/common/file/upload', data, {
|
||||||
timeout: 480000,
|
timeout: 600000,
|
||||||
onUploadProgress,
|
onUploadProgress,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'multipart/form-data; charset=utf-8'
|
'Content-Type': 'multipart/form-data; charset=utf-8'
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import type {
|
|||||||
transitionWorkflowBody,
|
transitionWorkflowBody,
|
||||||
transitionWorkflowResponse
|
transitionWorkflowResponse
|
||||||
} from '@/pages/api/core/app/transitionWorkflow';
|
} from '@/pages/api/core/app/transitionWorkflow';
|
||||||
|
import type { copyAppQuery, copyAppResponse } from '@/pages/api/core/app/copy';
|
||||||
|
|
||||||
/* folder */
|
/* folder */
|
||||||
export const postCreateAppFolder = (data: CreateAppFolderBody) =>
|
export const postCreateAppFolder = (data: CreateAppFolderBody) =>
|
||||||
@ -15,6 +16,7 @@ export const getAppFolderPath = (parentId: ParentIdType) =>
|
|||||||
GET<ParentTreePathItemType[]>(`/core/app/folder/path`, { parentId });
|
GET<ParentTreePathItemType[]>(`/core/app/folder/path`, { parentId });
|
||||||
|
|
||||||
/* detail */
|
/* detail */
|
||||||
|
|
||||||
export const postTransition2Workflow = (data: transitionWorkflowBody) =>
|
export const postTransition2Workflow = (data: transitionWorkflowBody) =>
|
||||||
POST<transitionWorkflowResponse>('/core/app/transitionWorkflow', data);
|
POST<transitionWorkflowResponse>('/core/app/transitionWorkflow', data);
|
||||||
|
|
||||||
|
export const postCopyApp = (data: copyAppQuery) => POST<copyAppResponse>('/core/app/copy', data);
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
"@nestjs/schematics": "^10.0.0",
|
"@nestjs/schematics": "^10.0.0",
|
||||||
"@nestjs/testing": "^10.0.0",
|
"@nestjs/testing": "^10.0.0",
|
||||||
"@types/jest": "^29.5.2",
|
"@types/jest": "^29.5.2",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.14.2",
|
||||||
"@types/supertest": "^6.0.0",
|
"@types/supertest": "^6.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user