Compare commits
7 Commits
v4.9.8-alp
...
gru/projec
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74be2169e4 | ||
|
|
cba8f773fe | ||
|
|
bd93f28d6f | ||
|
|
2063cb6314 | ||
|
|
12acaf491c | ||
|
|
3688842cc7 | ||
|
|
398d131bac |
@@ -132,15 +132,15 @@ services:
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.7-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.7-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.8 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.8 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.7-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.7-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.8 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.8 # 阿里云
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
@@ -150,8 +150,8 @@ services:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.7-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.7-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.8 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.8 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
|
||||
@@ -109,15 +109,15 @@ services:
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.7-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.7-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.8 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.8 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.7-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.7-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.8 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.8 # 阿里云
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
@@ -127,8 +127,8 @@ services:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.7-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.7-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.8 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.8 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
|
||||
@@ -96,15 +96,15 @@ services:
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.7-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.7-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.8 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.8 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.7-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.7-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.8 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.8 # 阿里云
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
@@ -114,8 +114,8 @@ services:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.7-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.7-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.8 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.8 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
|
||||
@@ -72,15 +72,15 @@ services:
|
||||
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.7-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.7-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.8 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.8 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt-mcp-server:
|
||||
container_name: fastgpt-mcp-server
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.7-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.7-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-mcp_server:v4.9.8 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-mcp_server:v4.9.8 # 阿里云
|
||||
ports:
|
||||
- 3005:3000
|
||||
networks:
|
||||
@@ -90,8 +90,8 @@ services:
|
||||
- FASTGPT_ENDPOINT=http://fastgpt:3000
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.7-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.7-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.8 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.8 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: 'V4.9.8(进行中)'
|
||||
title: 'V4.9.8'
|
||||
description: 'FastGPT V4.9.8 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
@@ -7,6 +7,17 @@ toc: true
|
||||
weight: 792
|
||||
---
|
||||
|
||||
## 升级指南
|
||||
|
||||
### 1. 做好数据备份
|
||||
|
||||
### 2. 更新镜像 tag
|
||||
|
||||
- 更新 FastGPT 镜像 tag: v4.9.8
|
||||
- 更新 FastGPT 商业版镜像 tag: v4.9.8
|
||||
- mcp_server 无需更新
|
||||
- Sandbox 无需更新
|
||||
- AIProxy 无需更新
|
||||
|
||||
## 🚀 新增内容
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ type ResponseType = {
|
||||
// 文件列表中,单项的文件类型
|
||||
type FileListItem = {
|
||||
id: string;
|
||||
parentId: string | null;
|
||||
parentId: string //也可能为 null 或者 undefined 类型;
|
||||
name: string;
|
||||
type: 'file' | 'folder';
|
||||
updateTime: Date;
|
||||
@@ -59,7 +59,7 @@ type FileListItem = {
|
||||
{{< markdownify >}}
|
||||
|
||||
{{% alert icon=" " context="success" %}}
|
||||
- parentId - 父级 id,可选,或者 null。
|
||||
- parentId - 父级 id,可选,或者 null | undefined。
|
||||
- searchKey - 检索词,可选
|
||||
{{% /alert %}}
|
||||
|
||||
@@ -68,7 +68,7 @@ curl --location --request POST '{{baseURL}}/v1/file/list' \
|
||||
--header 'Authorization: Bearer {{authorization}}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"parentId": null,
|
||||
"parentId": "",
|
||||
"searchKey": ""
|
||||
}'
|
||||
```
|
||||
|
||||
@@ -2,13 +2,28 @@ import { type ErrType } from '../errorCode';
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
/* dataset: 509000 */
|
||||
export enum SystemErrEnum {
|
||||
communityVersionNumLimit = 'communityVersionNumLimit'
|
||||
communityVersionNumLimit = 'communityVersionNumLimit',
|
||||
licenseAppAmountLimit = 'licenseAppAmountLimit',
|
||||
licenseDatasetAmountLimit = 'licenseDatasetAmountLimit',
|
||||
licenseUserAmountLimit = 'licenseUserAmountLimit'
|
||||
}
|
||||
|
||||
const systemErr = [
|
||||
{
|
||||
statusText: SystemErrEnum.communityVersionNumLimit,
|
||||
message: i18nT('common:code_error.system_error.community_version_num_limit')
|
||||
},
|
||||
{
|
||||
statusText: SystemErrEnum.licenseAppAmountLimit,
|
||||
message: i18nT('common:code_error.system_error.license_app_amount_limit')
|
||||
},
|
||||
{
|
||||
statusText: SystemErrEnum.licenseDatasetAmountLimit,
|
||||
message: i18nT('common:code_error.system_error.license_dataset_amount_limit')
|
||||
},
|
||||
{
|
||||
statusText: SystemErrEnum.licenseUserAmountLimit,
|
||||
message: i18nT('common:code_error.system_error.license_user_amount_limit')
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ export const checkPasswordRule = (password: string) => {
|
||||
/[A-Z]/, // Contains uppercase letters
|
||||
/[!@#$%^&*()_+=-]/ // Contains special characters
|
||||
];
|
||||
const validChars = /^[\dA-Za-z!@#$%^&*()_+=-]{6,100}$/;
|
||||
const validChars = /^[\dA-Za-z!@#$%^&*()_+=-]{8,100}$/;
|
||||
|
||||
// Check length and valid characters
|
||||
if (!validChars.test(password)) return false;
|
||||
|
||||
21
packages/global/common/system/types/index.d.ts
vendored
21
packages/global/common/system/types/index.d.ts
vendored
@@ -70,6 +70,9 @@ export type FastGPTFeConfigsType = {
|
||||
show_publish_dingtalk?: boolean;
|
||||
show_publish_offiaccount?: boolean;
|
||||
|
||||
show_dataset_enhance?: boolean;
|
||||
show_batch_eval?: boolean;
|
||||
|
||||
concatMd?: string;
|
||||
docUrl?: string;
|
||||
openAPIDocUrl?: string;
|
||||
@@ -142,3 +145,21 @@ export type customPdfParseType = {
|
||||
doc2xKey?: string;
|
||||
price?: number;
|
||||
};
|
||||
|
||||
export type LicenseDataType = {
|
||||
startTime: string;
|
||||
expiredTime: string;
|
||||
company: string;
|
||||
description?: string; // 描述
|
||||
hosts?: string[]; // 管理端有效域名
|
||||
maxUsers?: number; // 最大用户数,不填默认不上限
|
||||
maxApps?: number; // 最大应用数,不填默认不上限
|
||||
maxDatasets?: number; // 最大数据集数,不填默认不上限
|
||||
functions: {
|
||||
sso: boolean;
|
||||
pay: boolean;
|
||||
customTemplates: boolean;
|
||||
datasetEnhance: boolean;
|
||||
batchEval: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,26 +2,44 @@ import { SystemConfigsTypeEnum } from '@fastgpt/global/common/system/config/cons
|
||||
import { MongoSystemConfigs } from './schema';
|
||||
import { type FastGPTConfigFileType } from '@fastgpt/global/common/system/types';
|
||||
import { FastGPTProUrl } from '../constants';
|
||||
import { type LicenseDataType } from '@fastgpt/global/common/system/types';
|
||||
|
||||
export const getFastGPTConfigFromDB = async () => {
|
||||
export const getFastGPTConfigFromDB = async (): Promise<{
|
||||
fastgptConfig: FastGPTConfigFileType;
|
||||
licenseData?: LicenseDataType;
|
||||
}> => {
|
||||
if (!FastGPTProUrl) {
|
||||
return {
|
||||
config: {} as FastGPTConfigFileType
|
||||
fastgptConfig: {} as FastGPTConfigFileType
|
||||
};
|
||||
}
|
||||
|
||||
const res = await MongoSystemConfigs.findOne({
|
||||
type: SystemConfigsTypeEnum.fastgpt
|
||||
}).sort({
|
||||
createTime: -1
|
||||
});
|
||||
const [fastgptConfig, licenseConfig] = await Promise.all([
|
||||
MongoSystemConfigs.findOne({
|
||||
type: SystemConfigsTypeEnum.fastgpt
|
||||
}).sort({
|
||||
createTime: -1
|
||||
}),
|
||||
MongoSystemConfigs.findOne({
|
||||
type: SystemConfigsTypeEnum.license
|
||||
}).sort({
|
||||
createTime: -1
|
||||
})
|
||||
]);
|
||||
|
||||
const config = res?.value || {};
|
||||
const config = fastgptConfig?.value || {};
|
||||
const licenseData = licenseConfig?.value?.data as LicenseDataType | undefined;
|
||||
|
||||
const fastgptConfigTime = fastgptConfig?.createTime.getTime().toString();
|
||||
const licenseConfigTime = licenseConfig?.createTime.getTime().toString();
|
||||
// 利用配置文件的创建时间(更新时间)来做缓存,如果前端命中缓存,则不需要再返回配置文件
|
||||
global.systemInitBufferId = res ? res.createTime.getTime().toString() : undefined;
|
||||
global.systemInitBufferId = fastgptConfigTime
|
||||
? `${fastgptConfigTime}-${licenseConfigTime}`
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
config: config as FastGPTConfigFileType
|
||||
fastgptConfig: config as FastGPTConfigFileType,
|
||||
licenseData
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -22,8 +22,7 @@ import {
|
||||
import { type PluginRuntimeType } from '@fastgpt/global/core/plugin/type';
|
||||
import { MongoSystemPlugin } from './systemPluginSchema';
|
||||
import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin';
|
||||
import { MongoAppVersion } from '../version/schema';
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
/*
|
||||
plugin id rule:
|
||||
@@ -111,19 +110,13 @@ export async function getChildAppPreviewNode({
|
||||
|
||||
const version = await getAppVersionById({ appId, versionId, app: item });
|
||||
|
||||
if (!version.versionId) return Promise.reject(i18nT('common:app_not_version'));
|
||||
|
||||
const versionData = await MongoAppVersion.findById(
|
||||
version.versionId,
|
||||
'_id versionName appId time'
|
||||
).lean();
|
||||
|
||||
const isLatest = versionData
|
||||
? await checkIsLatestVersion({
|
||||
appId,
|
||||
versionId: versionData._id
|
||||
})
|
||||
: true;
|
||||
const isLatest =
|
||||
version.versionId && Types.ObjectId.isValid(version.versionId)
|
||||
? await checkIsLatestVersion({
|
||||
appId,
|
||||
versionId: version.versionId
|
||||
})
|
||||
: true;
|
||||
|
||||
return {
|
||||
id: String(item._id),
|
||||
@@ -140,7 +133,7 @@ export async function getChildAppPreviewNode({
|
||||
templateType: FlowNodeTemplateTypeEnum.teamApp,
|
||||
|
||||
version: version.versionId,
|
||||
versionLabel: versionData?.versionName || '',
|
||||
versionLabel: version?.versionName || '',
|
||||
isLatestVersion: isLatest,
|
||||
|
||||
originCost: 0,
|
||||
|
||||
@@ -119,6 +119,7 @@ const AppSchema = new Schema({
|
||||
defaultPermission: Number
|
||||
});
|
||||
|
||||
AppSchema.index({ type: 1 });
|
||||
AppSchema.index({ teamId: 1, updateTime: -1 });
|
||||
AppSchema.index({ teamId: 1, type: 1 });
|
||||
AppSchema.index(
|
||||
|
||||
@@ -15,6 +15,7 @@ export const getAppLatestVersion = async (appId: string, app?: AppSchema) => {
|
||||
if (version) {
|
||||
return {
|
||||
versionId: version._id,
|
||||
versionName: version.versionName,
|
||||
nodes: version.nodes,
|
||||
edges: version.edges,
|
||||
chatConfig: version.chatConfig || app?.chatConfig || {}
|
||||
@@ -22,6 +23,7 @@ export const getAppLatestVersion = async (appId: string, app?: AppSchema) => {
|
||||
}
|
||||
return {
|
||||
versionId: app?.pluginData?.nodeVersion,
|
||||
versionName: app?.name,
|
||||
nodes: app?.modules || [],
|
||||
edges: app?.edges || [],
|
||||
chatConfig: app?.chatConfig || {}
|
||||
@@ -47,6 +49,7 @@ export const getAppVersionById = async ({
|
||||
if (version) {
|
||||
return {
|
||||
versionId: version._id,
|
||||
versionName: version.versionName,
|
||||
nodes: version.nodes,
|
||||
edges: version.edges,
|
||||
chatConfig: version.chatConfig || app?.chatConfig || {}
|
||||
|
||||
@@ -123,6 +123,7 @@ const DatasetSchema = new Schema({
|
||||
|
||||
try {
|
||||
DatasetSchema.index({ teamId: 1 });
|
||||
DatasetSchema.index({ type: 1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
@@ -54,23 +54,50 @@ export const checkTeamDatasetLimit = async (teamId: string) => {
|
||||
})
|
||||
]);
|
||||
|
||||
// User check
|
||||
if (standardConstants && datasetCount >= standardConstants.maxDatasetAmount) {
|
||||
return Promise.reject(TeamErrEnum.datasetAmountNotEnough);
|
||||
}
|
||||
|
||||
// System check
|
||||
if (global?.licenseData?.maxDatasets && typeof global?.licenseData?.maxDatasets === 'number') {
|
||||
const totalDatasets = await MongoDataset.countDocuments({
|
||||
type: { $ne: DatasetTypeEnum.folder }
|
||||
});
|
||||
if (totalDatasets >= global.licenseData.maxDatasets) {
|
||||
return Promise.reject(SystemErrEnum.licenseDatasetAmountLimit);
|
||||
}
|
||||
}
|
||||
// Open source check
|
||||
if (!global.feConfigs.isPlus && datasetCount >= 30) {
|
||||
return Promise.reject(SystemErrEnum.communityVersionNumLimit);
|
||||
}
|
||||
};
|
||||
|
||||
export const checkTeamAppLimit = async (teamId: string, amount = 1) => {
|
||||
const [{ standardConstants }, appCount] = await Promise.all([
|
||||
getTeamStandPlan({ teamId }),
|
||||
MongoApp.countDocuments({
|
||||
teamId,
|
||||
type: { $in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin] }
|
||||
type: {
|
||||
$in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool]
|
||||
}
|
||||
})
|
||||
]);
|
||||
|
||||
if (standardConstants && appCount + amount >= standardConstants.maxAppAmount) {
|
||||
return Promise.reject(TeamErrEnum.appAmountNotEnough);
|
||||
}
|
||||
|
||||
// System check
|
||||
if (global?.licenseData?.maxApps && typeof global?.licenseData?.maxApps === 'number') {
|
||||
const totalApps = await MongoApp.countDocuments({
|
||||
type: {
|
||||
$in: [AppTypeEnum.simple, AppTypeEnum.workflow, AppTypeEnum.plugin, AppTypeEnum.tool]
|
||||
}
|
||||
});
|
||||
if (totalApps >= global.licenseData.maxApps) {
|
||||
return Promise.reject(SystemErrEnum.licenseAppAmountLimit);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
7
packages/service/type.d.ts
vendored
7
packages/service/type.d.ts
vendored
@@ -1,4 +1,8 @@
|
||||
import type { FastGPTFeConfigsType, SystemEnvType } from '@fastgpt/global/common/system/types';
|
||||
import type {
|
||||
FastGPTFeConfigsType,
|
||||
LicenseDataType,
|
||||
SystemEnvType
|
||||
} from '@fastgpt/global/common/system/types';
|
||||
import {
|
||||
TTSModelType,
|
||||
RerankModelItemType,
|
||||
@@ -17,6 +21,7 @@ declare global {
|
||||
var feConfigs: FastGPTFeConfigsType;
|
||||
var systemEnv: SystemEnvType;
|
||||
var subPlans: SubPlanType | undefined;
|
||||
var licenseData: LicenseDataType | undefined;
|
||||
|
||||
var workerPoll: Record<WorkerNameEnum, WorkerPool>;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { useRequest2 } from '../../../hooks/useRequest';
|
||||
import MyDivider from '../MyDivider';
|
||||
import type { useScrollPagination } from '../../../hooks/useScrollPagination';
|
||||
import Avatar from '../Avatar';
|
||||
import EmptyTip from '../EmptyTip';
|
||||
|
||||
/** 选择组件 Props 类型
|
||||
* value: 选中的值
|
||||
@@ -141,43 +142,49 @@ const MySelect = <T = any,>(
|
||||
const ListRender = useMemo(() => {
|
||||
return (
|
||||
<>
|
||||
{filterList.map((item, i) => (
|
||||
<Box key={i}>
|
||||
<MenuItem
|
||||
{...menuItemStyles}
|
||||
{...(value === item.value
|
||||
? {
|
||||
ref: SelectedItemRef,
|
||||
color: 'primary.700',
|
||||
bg: 'myGray.100',
|
||||
fontWeight: '600'
|
||||
{filterList.length > 0 ? (
|
||||
filterList.map((item, i) => (
|
||||
<Box key={i}>
|
||||
<MenuItem
|
||||
{...menuItemStyles}
|
||||
{...(value === item.value
|
||||
? {
|
||||
ref: SelectedItemRef,
|
||||
color: 'primary.700',
|
||||
bg: 'myGray.100',
|
||||
fontWeight: '600'
|
||||
}
|
||||
: {
|
||||
color: 'myGray.900'
|
||||
})}
|
||||
onClick={() => {
|
||||
if (value !== item.value) {
|
||||
onClickChange(item.value);
|
||||
}
|
||||
: {
|
||||
color: 'myGray.900'
|
||||
})}
|
||||
onClick={() => {
|
||||
if (value !== item.value) {
|
||||
onClickChange(item.value);
|
||||
}
|
||||
}}
|
||||
whiteSpace={'pre-wrap'}
|
||||
fontSize={'sm'}
|
||||
display={'block'}
|
||||
mb={0.5}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
{item.icon && <Avatar mr={2} src={item.icon as any} w={item.iconSize ?? '1rem'} />}
|
||||
{item.label}
|
||||
</Flex>
|
||||
{item.description && (
|
||||
<Box color={'myGray.500'} fontSize={'xs'}>
|
||||
{item.description}
|
||||
</Box>
|
||||
)}
|
||||
</MenuItem>
|
||||
{item.showBorder && <MyDivider my={2} />}
|
||||
</Box>
|
||||
))}
|
||||
}}
|
||||
whiteSpace={'pre-wrap'}
|
||||
fontSize={'sm'}
|
||||
display={'block'}
|
||||
mb={0.5}
|
||||
>
|
||||
<Flex alignItems={'center'}>
|
||||
{item.icon && (
|
||||
<Avatar mr={2} src={item.icon as any} w={item.iconSize ?? '1rem'} />
|
||||
)}
|
||||
{item.label}
|
||||
</Flex>
|
||||
{item.description && (
|
||||
<Box color={'myGray.500'} fontSize={'xs'}>
|
||||
{item.description}
|
||||
</Box>
|
||||
)}
|
||||
</MenuItem>
|
||||
{item.showBorder && <MyDivider my={2} />}
|
||||
</Box>
|
||||
))
|
||||
) : (
|
||||
<EmptyTip py={0} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}, [filterList, onClickChange, value]);
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"package_usage_rules": "Package usage rules: The system will give priority to using more advanced packages, and the original unused packages will take effect later.",
|
||||
"password": "Password",
|
||||
"password_mismatch": "Password Inconsistency: Two passwords are inconsistent",
|
||||
"password_tip": "Password must be at least 6 characters long and contain at least two combinations: numbers, letters, or special characters",
|
||||
"password_tip": "Password must be at least 8 characters long and contain at least two combinations: numbers, letters, or special characters",
|
||||
"password_update_error": "Exception when changing password",
|
||||
"password_update_success": "Password changed successfully",
|
||||
"pending_usage": "To be used",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"Action": "Action",
|
||||
"Add": "Add",
|
||||
"add_success": "Added Successfully",
|
||||
"Add_new_input": "Add new input",
|
||||
"All": "All",
|
||||
"App": "Application",
|
||||
@@ -30,7 +29,7 @@
|
||||
"FAQ.ai_point_q": "What are AI points?",
|
||||
"FAQ.check_subscription_a": "Go to Account - Personal Information - Package Details - Usage. You can view the effective and expiration dates of your subscribed packages. After the paid package expires, it will automatically switch to the free version.",
|
||||
"FAQ.check_subscription_q": "Where can I view my subscribed packages?",
|
||||
"FAQ.dataset_compute_a": "1 Dataset storage equals 1 Dataset index. A piece of Dataset data can contain one or more Dataset indexes. In enhanced training, 1 piece of data generates 5 indexes.",
|
||||
"FAQ.dataset_compute_a": "1 knowledge base storage is equal to 1 knowledge base index. \nA single chunked data usually corresponds to multiple indexes. You can see \"n group indexes\" in a single knowledge base collection.",
|
||||
"FAQ.dataset_compute_q": "How is Dataset storage calculated?",
|
||||
"FAQ.dataset_index_a": "No, but if the Dataset index exceeds the limit, you cannot insert or update Dataset content.",
|
||||
"FAQ.dataset_index_q": "Will the Dataset index be deleted if it exceeds the limit?",
|
||||
@@ -64,7 +63,6 @@
|
||||
"Parse": "Analysis",
|
||||
"Permission": "Permission",
|
||||
"Permission_tip": "Individual permissions are greater than group permissions",
|
||||
"please_input_name": "Please Enter a Name",
|
||||
"Preview": "Preview",
|
||||
"Remove": "Remove",
|
||||
"Rename": "Rename",
|
||||
@@ -94,6 +92,7 @@
|
||||
"action_confirm": "Confirm",
|
||||
"add_new": "add_new",
|
||||
"add_new_param": "Add new param",
|
||||
"add_success": "Added Successfully",
|
||||
"all_quotes": "All quotes",
|
||||
"all_result": "Full Results",
|
||||
"app_not_version": "This application has not been published, please publish it first",
|
||||
@@ -149,6 +148,9 @@
|
||||
"code_error.plugin_error.not_exist": "Plugin Does Not Exist",
|
||||
"code_error.plugin_error.un_auth": "Unauthorized to Operate This Plugin",
|
||||
"code_error.system_error.community_version_num_limit": "Exceeded Open Source Version Limit, Please Upgrade to Commercial Version: https://tryfastgpt.ai",
|
||||
"code_error.system_error.license_app_amount_limit": "Exceed the maximum number of applications in the system",
|
||||
"code_error.system_error.license_dataset_amount_limit": "Exceed the maximum number of knowledge bases in the system",
|
||||
"code_error.system_error.license_user_amount_limit": "Exceed the maximum number of users in the system",
|
||||
"code_error.team_error.ai_points_not_enough": "Insufficient AI Points",
|
||||
"code_error.team_error.app_amount_not_enough": "Application Limit Reached",
|
||||
"code_error.team_error.cannot_delete_default_group": "Cannot delete default group",
|
||||
@@ -962,6 +964,7 @@
|
||||
"permission.manager": "administrator",
|
||||
"permission.read": "Read permission",
|
||||
"permission.write": "write permission",
|
||||
"please_input_name": "Please Enter a Name",
|
||||
"plugin.App": "Select App",
|
||||
"plugin.Currentapp": "Current App",
|
||||
"plugin.Description": "Description",
|
||||
@@ -1260,7 +1263,7 @@
|
||||
"user.no_usage_records": "No Usage Records",
|
||||
"user.old_password": "Old Password",
|
||||
"user.password_message": "Password must be at least 4 characters and at most 60 characters",
|
||||
"user.password_tip": "Password must be at least 6 characters long and contain at least two combinations: numbers, letters, or special characters",
|
||||
"user.password_tip": "Password must be at least 8 characters long and contain at least two combinations: numbers, letters, or special characters",
|
||||
"user.reset_password": "Reset Password",
|
||||
"user.reset_password_tip": "The initial password is not set/the password has not been modified for a long time, please reset the password",
|
||||
"user.team.Balance": "Team Balance",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"login_success": "Login successful",
|
||||
"no_remind": "Don't remind again",
|
||||
"password_condition": "Password maximum 60 characters",
|
||||
"password_tip": "Password must be at least 6 characters long and contain at least two combinations: numbers, letters, or special characters",
|
||||
"password_tip": "Password must be at least 8 characters long and contain at least two combinations: numbers, letters, or special characters",
|
||||
"policy_tip": "By using this service, you agree to our",
|
||||
"privacy": "Privacy Policy",
|
||||
"privacy_policy": "Privacy Policy",
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"package_usage_rules": "套餐使用规则:系统优先使用更高级的套餐,原未用完的套餐将延后生效",
|
||||
"password": "密码",
|
||||
"password_mismatch": "密码不一致: 两次密码不一致",
|
||||
"password_tip": "密码至少 6 位,且至少包含两种组合:数字、字母或特殊字符",
|
||||
"password_tip": "密码至少 8 位,且至少包含两种组合:数字、字母或特殊字符",
|
||||
"password_update_error": "修改密码异常",
|
||||
"password_update_success": "修改密码成功",
|
||||
"pending_usage": "待使用",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"Action": "操作",
|
||||
"Add": "添加",
|
||||
"add_success": "添加成功",
|
||||
"Add_new_input": "新增输入",
|
||||
"All": "全部",
|
||||
"App": "应用",
|
||||
@@ -30,7 +29,7 @@
|
||||
"FAQ.ai_point_q": "什么是AI积分?",
|
||||
"FAQ.check_subscription_a": "账号-个人信息-套餐详情-使用情况。您可以查看所拥有套餐的生效和到期时间。当付费套餐到期后将自动切换免费版。",
|
||||
"FAQ.check_subscription_q": "在哪里查看已订阅的套餐?",
|
||||
"FAQ.dataset_compute_a": "1条知识库存储等于1条知识库索引。一条知识库数据可以包含1条或多条知识库索引。增强训练中,1条数据会生成5条索引。",
|
||||
"FAQ.dataset_compute_a": "1条知识库存储等于1条知识库索引。一条分块数据,通常对应多条索引,可以在单个知识库集合中看到\"n组索引\"",
|
||||
"FAQ.dataset_compute_q": "知识库存储怎么计算?",
|
||||
"FAQ.dataset_index_a": "不会。但知识库索引超出时,无法插入和更新知识库内容。",
|
||||
"FAQ.dataset_index_q": "知识库索引超出会删除么?",
|
||||
@@ -64,7 +63,6 @@
|
||||
"Parse": "解析",
|
||||
"Permission": "权限",
|
||||
"Permission_tip": "个人权限大于群组权限",
|
||||
"please_input_name": "请输入名称",
|
||||
"Preview": "预览",
|
||||
"Remove": "移除",
|
||||
"Rename": "重命名",
|
||||
@@ -94,6 +92,7 @@
|
||||
"action_confirm": "操作确认",
|
||||
"add_new": "新增",
|
||||
"add_new_param": "新增参数",
|
||||
"add_success": "添加成功",
|
||||
"all_quotes": "全部引用",
|
||||
"all_result": "完整结果",
|
||||
"app_not_version": " 该应用未发布过,请先发布应用",
|
||||
@@ -149,6 +148,9 @@
|
||||
"code_error.plugin_error.not_exist": "插件不存在",
|
||||
"code_error.plugin_error.un_auth": "无权操作该插件",
|
||||
"code_error.system_error.community_version_num_limit": "超出开源版数量限制,请升级商业版: https://fastgpt.in",
|
||||
"code_error.system_error.license_app_amount_limit": "超出系统最大应用数量",
|
||||
"code_error.system_error.license_dataset_amount_limit": "超出系统最大知识库数量",
|
||||
"code_error.system_error.license_user_amount_limit": "超出系统最大用户数量",
|
||||
"code_error.team_error.ai_points_not_enough": "AI 积分不足",
|
||||
"code_error.team_error.app_amount_not_enough": "应用数量已达上限~",
|
||||
"code_error.team_error.cannot_delete_default_group": "不能删除默认群组",
|
||||
@@ -961,6 +963,7 @@
|
||||
"permission.manager": "管理员",
|
||||
"permission.read": "读权限",
|
||||
"permission.write": "写权限",
|
||||
"please_input_name": "请输入名称",
|
||||
"plugin.App": "选择应用",
|
||||
"plugin.Currentapp": "当前应用",
|
||||
"plugin.Description": "描述",
|
||||
@@ -1259,7 +1262,7 @@
|
||||
"user.no_usage_records": "暂无使用记录",
|
||||
"user.old_password": "旧密码",
|
||||
"user.password_message": "密码最少 4 位最多 60 位",
|
||||
"user.password_tip": "密码至少 6 位,且至少包含两种组合:数字、字母或特殊字符",
|
||||
"user.password_tip": "密码至少 8 位,且至少包含两种组合:数字、字母或特殊字符",
|
||||
"user.reset_password": "重置密码",
|
||||
"user.reset_password_tip": "未设置初始密码/长时间未修改密码,请重置密码",
|
||||
"user.team.Balance": "团队余额",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"login_success": "登录成功",
|
||||
"no_remind": "不再提醒",
|
||||
"password_condition": "密码最多 60 位",
|
||||
"password_tip": "密码至少 6 位,且至少包含两种组合:数字、字母或特殊字符",
|
||||
"password_tip": "密码至少 8 位,且至少包含两种组合:数字、字母或特殊字符",
|
||||
"policy_tip": "使用即代表你同意我们的",
|
||||
"privacy": "隐私协议",
|
||||
"privacy_policy": "隐私政策",
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"package_usage_rules": "套餐使用規則:系統優先使用更進階的套餐,原未用完的套餐將延遲生效",
|
||||
"password": "密碼",
|
||||
"password_mismatch": "密碼不一致:兩次密碼不一致",
|
||||
"password_tip": "密碼至少 6 位,且至少包含兩種組合:數字、字母或特殊字元",
|
||||
"password_tip": "密碼至少 8 位,且至少包含兩種組合:數字、字母或特殊字元",
|
||||
"password_update_error": "修改密碼異常",
|
||||
"password_update_success": "修改密碼成功",
|
||||
"pending_usage": "待使用",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"Action": "操作",
|
||||
"Add": "新增",
|
||||
"add_success": "新增成功",
|
||||
"Add_new_input": "新增輸入",
|
||||
"All": "全部",
|
||||
"App": "應用程式",
|
||||
@@ -30,7 +29,7 @@
|
||||
"FAQ.ai_point_q": "什麼是 AI 點數?",
|
||||
"FAQ.check_subscription_a": "帳號 - 個人資訊 - 方案詳細資訊 - 使用狀況。您可以檢視已訂閱方案的生效和到期時間。當付費方案到期後,將自動切換為免費版。",
|
||||
"FAQ.check_subscription_q": "在哪裡可以檢視已訂閱的方案?",
|
||||
"FAQ.dataset_compute_a": "1 筆知識庫儲存等於 1 筆知識庫索引。一筆知識庫資料可以包含 1 筆或多筆知識庫索引。在增強訓練中,1 筆資料會產生 5 筆索引。",
|
||||
"FAQ.dataset_compute_a": "1條知識庫存儲等於1條知識庫索引。\n一條分塊數據,通常對應多條索引,可以在單個知識庫集合中看到\"n組索引\"",
|
||||
"FAQ.dataset_compute_q": "知識庫儲存如何計算?",
|
||||
"FAQ.dataset_index_a": "不會,但知識庫索引超出限制時,將無法插入和更新知識庫內容。",
|
||||
"FAQ.dataset_index_q": "知識庫索引超出是否會被刪除?",
|
||||
@@ -64,7 +63,6 @@
|
||||
"Parse": "解析",
|
||||
"Permission": "權限",
|
||||
"Permission_tip": "個人權限大於群組權限",
|
||||
"please_input_name": "請輸入名稱",
|
||||
"Preview": "預覽",
|
||||
"Remove": "移除",
|
||||
"Rename": "重新命名",
|
||||
@@ -94,6 +92,7 @@
|
||||
"action_confirm": "確認",
|
||||
"add_new": "新增",
|
||||
"add_new_param": "新增參數",
|
||||
"add_success": "新增成功",
|
||||
"all_quotes": "全部引用",
|
||||
"all_result": "完整結果",
|
||||
"app_not_version": "該應用未發布過,請先發布應用",
|
||||
@@ -148,6 +147,9 @@
|
||||
"code_error.plugin_error.not_exist": "外掛程式不存在",
|
||||
"code_error.plugin_error.un_auth": "無權操作此外掛程式",
|
||||
"code_error.system_error.community_version_num_limit": "超出開源版數量限制,請升級商業版:https://tryfastgpt.ai",
|
||||
"code_error.system_error.license_app_amount_limit": "超出系統最大應用數量",
|
||||
"code_error.system_error.license_dataset_amount_limit": "超出系統最大知識庫數量",
|
||||
"code_error.system_error.license_user_amount_limit": "超出系統最大用戶數量",
|
||||
"code_error.team_error.ai_points_not_enough": "AI 點數不足",
|
||||
"code_error.team_error.app_amount_not_enough": "已達應用程式數量上限",
|
||||
"code_error.team_error.cannot_delete_default_group": "無法刪除預設群組",
|
||||
@@ -961,6 +963,7 @@
|
||||
"permission.manager": "管理員",
|
||||
"permission.read": "讀取權限",
|
||||
"permission.write": "寫入權限",
|
||||
"please_input_name": "請輸入名稱",
|
||||
"plugin.App": "選擇應用程式",
|
||||
"plugin.Currentapp": "目前應用程式",
|
||||
"plugin.Description": "描述",
|
||||
@@ -1259,7 +1262,7 @@
|
||||
"user.no_usage_records": "無使用紀錄",
|
||||
"user.old_password": "舊密碼",
|
||||
"user.password_message": "密碼最少 4 位最多 60 位",
|
||||
"user.password_tip": "密碼至少 6 位,且至少包含兩種組合:數字、字母或特殊字元",
|
||||
"user.password_tip": "密碼至少 8 位,且至少包含兩種組合:數字、字母或特殊字元",
|
||||
"user.reset_password": "重置密碼",
|
||||
"user.reset_password_tip": "未設置初始密碼/長時間未修改密碼,請重置密碼",
|
||||
"user.team.Balance": "團隊餘額",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"login_success": "登入成功",
|
||||
"no_remind": "不再提醒",
|
||||
"password_condition": "密碼最多 60 個字元",
|
||||
"password_tip": "密碼至少 6 位,且至少包含兩種組合:數字、字母或特殊字元",
|
||||
"password_tip": "密碼至少 8 位,且至少包含兩種組合:數字、字母或特殊字元",
|
||||
"policy_tip": "使用即代表您同意我們的",
|
||||
"privacy": "隱私權政策",
|
||||
"privacy_policy": "隱私權政策",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app",
|
||||
"version": "4.9.7",
|
||||
"version": "4.9.8",
|
||||
"private": false,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -17,7 +17,9 @@ export async function register() {
|
||||
{ getSystemPluginCb },
|
||||
{ startMongoWatch },
|
||||
{ startCron },
|
||||
{ startTrainingQueue }
|
||||
{ startTrainingQueue },
|
||||
{ preLoadWorker },
|
||||
{ loadSystemModels }
|
||||
] = await Promise.all([
|
||||
import('@fastgpt/service/common/mongo/init'),
|
||||
import('@fastgpt/service/common/mongo/index'),
|
||||
@@ -28,7 +30,9 @@ export async function register() {
|
||||
import('@/service/core/app/plugin'),
|
||||
import('@/service/common/system/volumnMongoWatch'),
|
||||
import('@/service/common/system/cron'),
|
||||
import('@/service/core/dataset/training/utils')
|
||||
import('@/service/core/dataset/training/utils'),
|
||||
import('@fastgpt/service/worker/preload'),
|
||||
import('@fastgpt/service/core/ai/config/utils')
|
||||
]);
|
||||
|
||||
// 执行初始化流程
|
||||
@@ -40,8 +44,9 @@ export async function register() {
|
||||
connectMongo(connectionLogMongo, MONGO_LOG_URL);
|
||||
|
||||
//init system config;init vector database;init root user
|
||||
await Promise.all([getInitConfig(), initVectorStore(), initRootUser()]);
|
||||
await Promise.all([getInitConfig(), initVectorStore(), initRootUser(), loadSystemModels()]);
|
||||
|
||||
// 异步加载
|
||||
initSystemPluginGroups();
|
||||
initAppTemplateTypes();
|
||||
getSystemPluginCb();
|
||||
@@ -49,6 +54,12 @@ export async function register() {
|
||||
startCron();
|
||||
startTrainingQueue(true);
|
||||
|
||||
try {
|
||||
await preLoadWorker();
|
||||
} catch (error) {
|
||||
console.error('Preload worker error', error);
|
||||
}
|
||||
|
||||
console.log('Init system success');
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@@ -104,8 +104,8 @@ const NodeCard = (props: Props) => {
|
||||
const isAppNode = node && AppNodeFlowNodeTypeMap[node?.flowNodeType];
|
||||
const showVersion = useMemo(() => {
|
||||
if (!isAppNode || !node?.pluginId) return false;
|
||||
const splitRes = node.pluginId.split('-');
|
||||
if (splitRes.length > 1) {
|
||||
if ([FlowNodeTypeEnum.tool, FlowNodeTypeEnum.toolSet].includes(node.flowNodeType)) return false;
|
||||
if (node.pluginId.split('-').length > 1) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -403,7 +403,9 @@ const MenuRender = React.memo(function MenuRender({
|
||||
outputs: node.data.outputs,
|
||||
showStatus: node.data.showStatus,
|
||||
pluginId: node.data.pluginId,
|
||||
version: node.data.version
|
||||
version: node.data.version,
|
||||
versionLabel: node.data.versionLabel,
|
||||
isLatestVersion: node.data.isLatestVersion
|
||||
};
|
||||
|
||||
return [
|
||||
@@ -423,7 +425,9 @@ const MenuRender = React.memo(function MenuRender({
|
||||
pluginId: template.pluginId,
|
||||
inputs: template.inputs,
|
||||
outputs: template.outputs,
|
||||
version: template.version
|
||||
version: template.version,
|
||||
versionLabel: template.versionLabel,
|
||||
isLatestVersion: template.isLatestVersion
|
||||
},
|
||||
selected: true,
|
||||
parentNodeId: undefined,
|
||||
|
||||
@@ -218,47 +218,48 @@ const CollectionChunkForm = ({ form }: { form: UseFormReturn<CollectionChunkForm
|
||||
gridTemplateColumns={'repeat(2, 1fr)'}
|
||||
/>
|
||||
</Box>
|
||||
{trainingType === DatasetCollectionDataProcessModeEnum.chunk && (
|
||||
<Box mt={6}>
|
||||
<Box fontSize={'sm'} mb={2} color={'myGray.600'}>
|
||||
{t('dataset:enhanced_indexes')}
|
||||
{trainingType === DatasetCollectionDataProcessModeEnum.chunk &&
|
||||
feConfigs?.show_dataset_enhance !== false && (
|
||||
<Box mt={6}>
|
||||
<Box fontSize={'sm'} mb={2} color={'myGray.600'}>
|
||||
{t('dataset:enhanced_indexes')}
|
||||
</Box>
|
||||
<HStack gap={[3, 7]}>
|
||||
<HStack flex={'1'} spacing={1}>
|
||||
<MyTooltip label={!feConfigs?.isPlus ? t('common:commercial_function_tip') : ''}>
|
||||
<Checkbox
|
||||
isDisabled={!feConfigs?.isPlus}
|
||||
isChecked={autoIndexes}
|
||||
{...register('autoIndexes')}
|
||||
>
|
||||
<FormLabel>{t('dataset:auto_indexes')}</FormLabel>
|
||||
</Checkbox>
|
||||
</MyTooltip>
|
||||
<QuestionTip label={t('dataset:auto_indexes_tips')} />
|
||||
</HStack>
|
||||
<HStack flex={'1'} spacing={1}>
|
||||
<MyTooltip
|
||||
label={
|
||||
!feConfigs?.isPlus
|
||||
? t('common:commercial_function_tip')
|
||||
: !datasetDetail?.vlmModel
|
||||
? t('common:error_vlm_not_config')
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<Checkbox
|
||||
isDisabled={!feConfigs?.isPlus || !datasetDetail?.vlmModel}
|
||||
isChecked={imageIndex}
|
||||
{...register('imageIndex')}
|
||||
>
|
||||
<FormLabel>{t('dataset:image_auto_parse')}</FormLabel>
|
||||
</Checkbox>
|
||||
</MyTooltip>
|
||||
<QuestionTip label={t('dataset:image_auto_parse_tips')} />
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
<HStack gap={[3, 7]}>
|
||||
<HStack flex={'1'} spacing={1}>
|
||||
<MyTooltip label={!feConfigs?.isPlus ? t('common:commercial_function_tip') : ''}>
|
||||
<Checkbox
|
||||
isDisabled={!feConfigs?.isPlus}
|
||||
isChecked={autoIndexes}
|
||||
{...register('autoIndexes')}
|
||||
>
|
||||
<FormLabel>{t('dataset:auto_indexes')}</FormLabel>
|
||||
</Checkbox>
|
||||
</MyTooltip>
|
||||
<QuestionTip label={t('dataset:auto_indexes_tips')} />
|
||||
</HStack>
|
||||
<HStack flex={'1'} spacing={1}>
|
||||
<MyTooltip
|
||||
label={
|
||||
!feConfigs?.isPlus
|
||||
? t('common:commercial_function_tip')
|
||||
: !datasetDetail?.vlmModel
|
||||
? t('common:error_vlm_not_config')
|
||||
: ''
|
||||
}
|
||||
>
|
||||
<Checkbox
|
||||
isDisabled={!feConfigs?.isPlus || !datasetDetail?.vlmModel}
|
||||
isChecked={imageIndex}
|
||||
{...register('imageIndex')}
|
||||
>
|
||||
<FormLabel>{t('dataset:image_auto_parse')}</FormLabel>
|
||||
</Checkbox>
|
||||
</MyTooltip>
|
||||
<QuestionTip label={t('dataset:image_auto_parse_tips')} />
|
||||
</HStack>
|
||||
</HStack>
|
||||
</Box>
|
||||
)}
|
||||
)}
|
||||
<Box mt={6}>
|
||||
<Box fontSize={'sm'} mb={2} color={'myGray.600'}>
|
||||
{t('dataset:params_setting')}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import ApiKeyTable from '@/components/support/apikey/Table';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||
import FillRowTabs from '@fastgpt/web/components/common/Tabs/FillRowTabs';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import {
|
||||
Box,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import React, { useState } from 'react';
|
||||
import { Box, Flex, useTheme } from '@chakra-ui/react';
|
||||
import { getInforms, readInform } from '@/web/support/user/inform/api';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import { serviceSideProps } from '@/web/common/i18n/utils';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import AccountContainer from '@/pageComponents/account/AccountContainer';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import {
|
||||
Grid,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import { Box, Card, Flex } from '@chakra-ui/react';
|
||||
import React, { useCallback } from 'react';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import { serviceSideProps } from '@/web/common/i18n/utils';
|
||||
import AccountContainer from '@/pageComponents/account/AccountContainer';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import { Flex, Box, HStack } from '@chakra-ui/react';
|
||||
import { UsageSourceEnum, UsageSourceMap } from '@fastgpt/global/support/wallet/usage/constants';
|
||||
|
||||
@@ -21,7 +21,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
await authCert({ req, authRoot: true });
|
||||
|
||||
// load config
|
||||
const [{ config: dbConfig }, fileConfig] = await Promise.all([
|
||||
const [{ fastgptConfig: dbConfig }, fileConfig] = await Promise.all([
|
||||
getFastGPTConfigFromDB(),
|
||||
readConfigData('config.json')
|
||||
]);
|
||||
|
||||
@@ -11,6 +11,7 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||
import { TeamAppCreatePermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
||||
import { checkTeamAppLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||
|
||||
export type createHttpPluginQuery = {};
|
||||
|
||||
@@ -35,6 +36,8 @@ async function handler(
|
||||
? await authApp({ req, appId: parentId, per: TeamAppCreatePermissionVal, authToken: true })
|
||||
: await authUserPer({ req, authToken: true, per: TeamAppCreatePermissionVal });
|
||||
|
||||
await checkTeamAppLimit(teamId);
|
||||
|
||||
const httpPluginId = await mongoSessionRun(async (session) => {
|
||||
// create http plugin folder
|
||||
const httpPluginId = await onCreateApp({
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
getMCPToolSetRuntimeNode
|
||||
} from '@fastgpt/global/core/app/mcpTools/utils';
|
||||
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
||||
import { checkTeamAppLimit } from '@fastgpt/service/support/permission/teamLimit';
|
||||
|
||||
export type createMCPToolsQuery = {};
|
||||
|
||||
@@ -35,6 +36,8 @@ async function handler(
|
||||
? await authApp({ req, appId: parentId, per: TeamAppCreatePermissionVal, authToken: true })
|
||||
: await authUserPer({ req, authToken: true, per: TeamAppCreatePermissionVal });
|
||||
|
||||
await checkTeamAppLimit(teamId);
|
||||
|
||||
const mcpToolsId = await mongoSessionRun(async (session) => {
|
||||
const mcpToolsId = await onCreateApp({
|
||||
name,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState, useRef } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import NextHead from '@/components/common/NextHead';
|
||||
import { useRouter } from 'next/router';
|
||||
import { getInitChatInfo } from '@/web/core/chat/api';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import DashboardContainer from '@/pageComponents/dashboard/Container';
|
||||
|
||||
import PluginCard from '@/pageComponents/dashboard/SystemPlugin/ToolCard';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React, { useMemo, useState, useEffect } from 'react';
|
||||
'use client';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { Box, Flex, Button, useDisclosure, Input, InputGroup } from '@chakra-ui/react';
|
||||
import { AddIcon } from '@chakra-ui/icons';
|
||||
import { serviceSideProps } from '@/web/common/i18n/utils';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import { serviceSideProps } from '@/web/common/i18n/utils';
|
||||
import React, { useState } from 'react';
|
||||
import DashboardContainer from '@/pageComponents/dashboard/Container';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import { serviceSideProps } from '@/web/common/i18n/utils';
|
||||
import DashboardContainer from '@/pageComponents/dashboard/Container';
|
||||
import { Box, Button, Flex, Grid, HStack } from '@chakra-ui/react';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
import { Box, Flex, type FlexProps } from '@chakra-ui/react';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
'use client';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { Box, Flex, Button, InputGroup, InputLeftElement, Input } from '@chakra-ui/react';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
@@ -10,7 +10,6 @@ import json5 from 'json5';
|
||||
import { defaultGroup, defaultTemplateTypes } from '@fastgpt/web/core/workflow/constants';
|
||||
import { MongoPluginGroups } from '@fastgpt/service/core/app/plugin/pluginGroupSchema';
|
||||
import { MongoTemplateTypes } from '@fastgpt/service/core/app/templates/templateTypeSchema';
|
||||
import { loadSystemModels } from '@fastgpt/service/core/ai/config/utils';
|
||||
import { POST } from '@fastgpt/service/common/api/plusRequest';
|
||||
import {
|
||||
type DeepRagSearchProps,
|
||||
@@ -28,7 +27,6 @@ import {
|
||||
getProApiDatasetFilePreviewUrlRequest
|
||||
} from '@/service/core/dataset/apiDataset/controller';
|
||||
import { isProVersion } from './constants';
|
||||
import { preLoadWorker } from '@fastgpt/service/worker/preload';
|
||||
|
||||
export const readConfigData = async (name: string) => {
|
||||
const splitName = name.split('.');
|
||||
@@ -95,12 +93,25 @@ export function initGlobalVariables() {
|
||||
|
||||
/* Init system data(Need to connected db). It only needs to run once */
|
||||
export async function getInitConfig() {
|
||||
await Promise.all([initSystemConfig(), getSystemVersion(), loadSystemModels()]);
|
||||
try {
|
||||
await preLoadWorker();
|
||||
} catch (error) {
|
||||
console.error('Preload worker error', error);
|
||||
}
|
||||
const getSystemVersion = async () => {
|
||||
if (global.systemVersion) return;
|
||||
try {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
global.systemVersion = process.env.npm_package_version || '0.0.0';
|
||||
} else {
|
||||
const packageJson = json5.parse(await fs.promises.readFile('/app/package.json', 'utf-8'));
|
||||
|
||||
global.systemVersion = packageJson?.version;
|
||||
}
|
||||
console.log(`System Version: ${global.systemVersion}`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
global.systemVersion = '0.0.0';
|
||||
}
|
||||
};
|
||||
|
||||
await Promise.all([initSystemConfig(), getSystemVersion()]);
|
||||
}
|
||||
|
||||
const defaultFeConfigs: FastGPTFeConfigsType = {
|
||||
@@ -125,10 +136,12 @@ const defaultFeConfigs: FastGPTFeConfigsType = {
|
||||
|
||||
export async function initSystemConfig() {
|
||||
// load config
|
||||
const [{ config: dbConfig }, fileConfig] = await Promise.all([
|
||||
const [{ fastgptConfig, licenseData }, fileConfig] = await Promise.all([
|
||||
getFastGPTConfigFromDB(),
|
||||
readConfigData('config.json')
|
||||
]);
|
||||
global.licenseData = licenseData;
|
||||
|
||||
const fileRes = json5.parse(fileConfig) as FastGPTConfigFileType;
|
||||
|
||||
// get config from database
|
||||
@@ -136,16 +149,18 @@ export async function initSystemConfig() {
|
||||
feConfigs: {
|
||||
...fileRes?.feConfigs,
|
||||
...defaultFeConfigs,
|
||||
...(dbConfig.feConfigs || {}),
|
||||
isPlus: !!FastGPTProUrl,
|
||||
...(fastgptConfig.feConfigs || {}),
|
||||
isPlus: !!licenseData,
|
||||
show_aiproxy: !!process.env.AIPROXY_API_ENDPOINT,
|
||||
show_coupon: process.env.SHOW_COUPON === 'true'
|
||||
show_coupon: process.env.SHOW_COUPON === 'true',
|
||||
show_dataset_enhance: licenseData?.functions?.datasetEnhance,
|
||||
show_batch_eval: licenseData?.functions?.batchEval
|
||||
},
|
||||
systemEnv: {
|
||||
...fileRes.systemEnv,
|
||||
...(dbConfig.systemEnv || {})
|
||||
...(fastgptConfig.systemEnv || {})
|
||||
},
|
||||
subPlans: dbConfig.subPlans || fileRes.subPlans
|
||||
subPlans: fastgptConfig.subPlans
|
||||
};
|
||||
|
||||
// set config
|
||||
@@ -154,28 +169,11 @@ export async function initSystemConfig() {
|
||||
console.log({
|
||||
feConfigs: global.feConfigs,
|
||||
systemEnv: global.systemEnv,
|
||||
subPlans: global.subPlans
|
||||
subPlans: global.subPlans,
|
||||
licenseData: global.licenseData
|
||||
});
|
||||
}
|
||||
|
||||
async function getSystemVersion() {
|
||||
if (global.systemVersion) return;
|
||||
try {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
global.systemVersion = process.env.npm_package_version || '0.0.0';
|
||||
} else {
|
||||
const packageJson = json5.parse(await fs.promises.readFile('/app/package.json', 'utf-8'));
|
||||
|
||||
global.systemVersion = packageJson?.version;
|
||||
}
|
||||
console.log(`System Version: ${global.systemVersion}`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
global.systemVersion = '0.0.0';
|
||||
}
|
||||
}
|
||||
|
||||
export async function initSystemPluginGroups() {
|
||||
try {
|
||||
const { groupOrder, ...restDefaultGroup } = defaultGroup;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { debounce } from 'lodash';
|
||||
import { MongoAppTemplate } from '@fastgpt/service/core/app/templates/templateSchema';
|
||||
import { getAppTemplatesAndLoadThem } from '@fastgpt/templates/register';
|
||||
import { watchSystemModelUpdate } from '@fastgpt/service/core/ai/config/utils';
|
||||
import { SystemConfigsTypeEnum } from '@fastgpt/global/common/system/config/constants';
|
||||
|
||||
export const startMongoWatch = async () => {
|
||||
reloadConfigWatch();
|
||||
@@ -21,7 +22,11 @@ const reloadConfigWatch = () => {
|
||||
|
||||
changeStream.on('change', async (change) => {
|
||||
try {
|
||||
if (change.operationType === 'insert') {
|
||||
if (
|
||||
(change.operationType === 'insert' &&
|
||||
change.fullDocument.type === SystemConfigsTypeEnum.fastgptPro) ||
|
||||
change.operationType === 'update'
|
||||
) {
|
||||
await initSystemConfig();
|
||||
console.log('refresh system config');
|
||||
}
|
||||
|
||||
@@ -23,13 +23,25 @@ type State = {
|
||||
const createCustomStorage = () => {
|
||||
const sessionKeys = ['source', 'chatId', 'appId'];
|
||||
|
||||
// 从 URL 中获取 appId 作为存储键的一部分
|
||||
const getStorageKey = (name: string) => {
|
||||
let appId = '';
|
||||
if (typeof window !== 'undefined') {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
appId = urlParams.get('appId') || '';
|
||||
}
|
||||
return appId ? `${name}_${appId}` : name;
|
||||
};
|
||||
|
||||
return {
|
||||
getItem: (name: string) => {
|
||||
const sessionData = JSON.parse(sessionStorage.getItem(name) || '{}');
|
||||
const localData = JSON.parse(localStorage.getItem(name) || '{}');
|
||||
const storageKey = getStorageKey(name);
|
||||
const sessionData = JSON.parse(sessionStorage.getItem(storageKey) || '{}');
|
||||
const localData = JSON.parse(localStorage.getItem(storageKey) || '{}');
|
||||
return JSON.stringify({ ...localData, ...sessionData });
|
||||
},
|
||||
setItem: (name: string, value: string) => {
|
||||
const storageKey = getStorageKey(name);
|
||||
const data = JSON.parse(value);
|
||||
|
||||
// 分离 session 和 local 数据
|
||||
@@ -42,15 +54,16 @@ const createCustomStorage = () => {
|
||||
|
||||
// 分别存储
|
||||
if (Object.keys(sessionData).length > 0) {
|
||||
sessionStorage.setItem(name, JSON.stringify({ state: sessionData, version: 0 }));
|
||||
sessionStorage.setItem(storageKey, JSON.stringify({ state: sessionData, version: 0 }));
|
||||
}
|
||||
if (Object.keys(localData).length > 0) {
|
||||
localStorage.setItem(name, JSON.stringify({ state: localData, version: 0 }));
|
||||
localStorage.setItem(storageKey, JSON.stringify({ state: localData, version: 0 }));
|
||||
}
|
||||
},
|
||||
removeItem: (name: string) => {
|
||||
sessionStorage.removeItem(name);
|
||||
localStorage.removeItem(name);
|
||||
const storageKey = getStorageKey(name);
|
||||
sessionStorage.removeItem(storageKey);
|
||||
localStorage.removeItem(storageKey);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -125,3 +138,5 @@ export const useChatStore = create<State>()(
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
export { createCustomStorage };
|
||||
|
||||
@@ -4,8 +4,8 @@ import { checkPasswordRule } from '@fastgpt/global/common/string/password';
|
||||
describe('PasswordRule', () => {
|
||||
it('should be a valid password', () => {
|
||||
// Small password
|
||||
expect(checkPasswordRule('123A')).toBe(false);
|
||||
expect(checkPasswordRule('@ga21')).toBe(false);
|
||||
expect(checkPasswordRule('123ABC')).toBe(false);
|
||||
expect(checkPasswordRule('@ga2123')).toBe(false);
|
||||
|
||||
// Test single type characters
|
||||
expect(checkPasswordRule('123456')).toBe(false);
|
||||
@@ -14,13 +14,13 @@ describe('PasswordRule', () => {
|
||||
expect(checkPasswordRule('!@#$%^')).toBe(false); // only special chars
|
||||
|
||||
// Test two types combination
|
||||
expect(checkPasswordRule('abc123')).toBe(true); // lowercase + numbers
|
||||
expect(checkPasswordRule('abcABC')).toBe(true); // lowercase + uppercase
|
||||
expect(checkPasswordRule('abc!@#')).toBe(true); // lowercase + special chars
|
||||
expect(checkPasswordRule('ABC!@#')).toBe(true); // uppercase + special chars
|
||||
expect(checkPasswordRule('ABC123')).toBe(true); // uppercase + numbers
|
||||
expect(checkPasswordRule('123!@#')).toBe(true); // numbers + special chars
|
||||
expect(checkPasswordRule('!@123fa')).toBe(true); // numbers + special chars
|
||||
expect(checkPasswordRule('abcd1234')).toBe(true); // lowercase + numbers
|
||||
expect(checkPasswordRule('abcdABCD')).toBe(true); // lowercase + uppercase
|
||||
expect(checkPasswordRule('abcd!@#$')).toBe(true); // lowercase + special chars
|
||||
expect(checkPasswordRule('ABCD!@#$')).toBe(true); // uppercase + special chars
|
||||
expect(checkPasswordRule('ABCD1234')).toBe(true); // uppercase + numbers
|
||||
expect(checkPasswordRule('1234!@#$')).toBe(true); // numbers + special chars
|
||||
expect(checkPasswordRule('!@123fab')).toBe(true); // numbers + special chars
|
||||
expect(checkPasswordRule('+2222()222')).toBe(true); // special chars + numbers
|
||||
expect(checkPasswordRule('_2222()-+=22')).toBe(true); // special chars + numbers
|
||||
|
||||
|
||||
170
test/cases/web/core/chat/context/useChatStore.test.ts
Normal file
170
test/cases/web/core/chat/context/useChatStore.test.ts
Normal file
@@ -0,0 +1,170 @@
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
import { useChatStore, createCustomStorage } from '@/web/core/chat/context/useChatStore';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
|
||||
vi.mock('@fastgpt/global/common/string/tools', () => ({
|
||||
getNanoid: vi.fn().mockReturnValue('test-nanoid')
|
||||
}));
|
||||
|
||||
const mockStorage = () => {
|
||||
const store = new Map();
|
||||
return {
|
||||
getItem: (key: string) => store.get(key) || null,
|
||||
setItem: (key: string, value: string) => store.set(key, value),
|
||||
clear: () => store.clear(),
|
||||
removeItem: (key: string) => store.delete(key)
|
||||
};
|
||||
};
|
||||
|
||||
const mockWindow = () => {
|
||||
const windowMock = {
|
||||
location: {
|
||||
search: '?appId=test123'
|
||||
},
|
||||
sessionStorage: mockStorage(),
|
||||
localStorage: mockStorage()
|
||||
};
|
||||
|
||||
vi.stubGlobal('window', windowMock);
|
||||
global.sessionStorage = windowMock.sessionStorage;
|
||||
global.localStorage = windowMock.localStorage;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
vi.clearAllMocks();
|
||||
mockWindow();
|
||||
const store = useChatStore.getState();
|
||||
store.source = undefined;
|
||||
store.appId = '';
|
||||
store.chatId = '';
|
||||
store.lastChatId = '';
|
||||
store.lastChatAppId = '';
|
||||
store.outLinkAuthData = {};
|
||||
sessionStorage.clear();
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
describe('useChatStore', () => {
|
||||
it('should set source and restore last chat if available', () => {
|
||||
const store = useChatStore.getState();
|
||||
store.lastChatAppId = 'app123';
|
||||
store.lastChatId = `${ChatSourceEnum.share}-chat123`;
|
||||
|
||||
store.setSource(ChatSourceEnum.share);
|
||||
|
||||
const updatedStore = useChatStore.getState();
|
||||
expect(updatedStore.source).toBe(ChatSourceEnum.share);
|
||||
expect(updatedStore.chatId).toBe('chat123');
|
||||
expect(updatedStore.lastChatAppId).toBe('app123');
|
||||
});
|
||||
|
||||
it('should generate new chatId when source changes', () => {
|
||||
const store = useChatStore.getState();
|
||||
store.source = ChatSourceEnum.share;
|
||||
store.chatId = 'old-id';
|
||||
|
||||
store.setSource(ChatSourceEnum.api);
|
||||
const updatedStore = useChatStore.getState();
|
||||
|
||||
expect(updatedStore.chatId).toBe('test-nanoid');
|
||||
expect(updatedStore.chatId).not.toBe('old-id');
|
||||
});
|
||||
|
||||
it('should set appId and lastChatAppId', () => {
|
||||
const store = useChatStore.getState();
|
||||
store.setAppId('test123');
|
||||
const updatedStore = useChatStore.getState();
|
||||
|
||||
expect(updatedStore.appId).toBe('test123');
|
||||
expect(updatedStore.lastChatAppId).toBe('test123');
|
||||
});
|
||||
|
||||
it('should not set empty appId', () => {
|
||||
const store = useChatStore.getState();
|
||||
store.setAppId('test123');
|
||||
store.setAppId('');
|
||||
const updatedStore = useChatStore.getState();
|
||||
|
||||
expect(updatedStore.appId).toBe('test123');
|
||||
expect(updatedStore.lastChatAppId).toBe('test123');
|
||||
});
|
||||
|
||||
it('should set chatId and lastChatId', () => {
|
||||
const store = useChatStore.getState();
|
||||
store.source = ChatSourceEnum.share;
|
||||
store.setChatId('test-id');
|
||||
const updatedStore = useChatStore.getState();
|
||||
|
||||
expect(updatedStore.chatId).toBe('test-id');
|
||||
expect(updatedStore.lastChatId).toBe(`${ChatSourceEnum.share}-test-id`);
|
||||
});
|
||||
|
||||
it('should generate new chatId if none provided', () => {
|
||||
const store = useChatStore.getState();
|
||||
store.source = ChatSourceEnum.share;
|
||||
store.setChatId();
|
||||
const updatedStore = useChatStore.getState();
|
||||
|
||||
expect(updatedStore.chatId).toBe('test-nanoid');
|
||||
});
|
||||
|
||||
it('should set outLinkAuthData', () => {
|
||||
const store = useChatStore.getState();
|
||||
const authData = { apikey: 'test-key' };
|
||||
store.setOutLinkAuthData(authData);
|
||||
const updatedStore = useChatStore.getState();
|
||||
|
||||
expect(updatedStore.outLinkAuthData).toEqual(authData);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createCustomStorage', () => {
|
||||
it('should create storage with appId in key', () => {
|
||||
const storage = createCustomStorage();
|
||||
const testData = {
|
||||
state: {
|
||||
source: ChatSourceEnum.share,
|
||||
chatId: '123',
|
||||
appId: 'app123',
|
||||
lastChatId: 'last123',
|
||||
lastChatAppId: 'lastApp123'
|
||||
},
|
||||
version: 0
|
||||
};
|
||||
|
||||
storage.setItem('test', JSON.stringify(testData));
|
||||
|
||||
const sessionResult = JSON.parse(sessionStorage.getItem('test_test123') || '{}');
|
||||
const localResult = JSON.parse(localStorage.getItem('test_test123') || '{}');
|
||||
|
||||
expect(sessionResult.state).toEqual({
|
||||
source: ChatSourceEnum.share,
|
||||
chatId: '123',
|
||||
appId: 'app123'
|
||||
});
|
||||
|
||||
expect(localResult.state).toEqual({
|
||||
lastChatId: 'last123',
|
||||
lastChatAppId: 'lastApp123'
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove items from both storages', () => {
|
||||
const storage = createCustomStorage();
|
||||
const testData = {
|
||||
state: {
|
||||
source: ChatSourceEnum.share,
|
||||
chatId: '123'
|
||||
},
|
||||
version: 0
|
||||
};
|
||||
|
||||
storage.setItem('test', JSON.stringify(testData));
|
||||
storage.removeItem('test');
|
||||
|
||||
expect(sessionStorage.getItem('test_test123')).toBeNull();
|
||||
expect(localStorage.getItem('test_test123')).toBeNull();
|
||||
});
|
||||
});
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"/root/.cache/node/corepack/v1/pnpm/9.15.5/bin/pnpm.cjs":["f5286335ac1397138eff043e0d78e29501577055",0,1472],"/root/.cache/node/corepack/v1/pnpm/9.15.5/dist/pnpm.cjs":["0b2b22796df6e249cd89f4cdf06786d40a52564e",1472,886352]}
|
||||
Reference in New Issue
Block a user