diff --git a/docSite/content/zh-cn/docs/development/configuration.md b/docSite/content/zh-cn/docs/development/configuration.md index cf700392b..826f8b767 100644 --- a/docSite/content/zh-cn/docs/development/configuration.md +++ b/docSite/content/zh-cn/docs/development/configuration.md @@ -11,7 +11,7 @@ weight: 708 **开发环境下**,你需要将示例配置文件 `config.json` 复制成 `config.local.json` 文件才会生效。 -这个配置文件中包含了系统参数和各个模型配置,`使用时务必去掉注释!!!!!!!!!!!!!!` +这个配置文件中包含了系统参数和各个模型配置: ## 4.6.8+ 版本新配置文件 @@ -158,7 +158,7 @@ llm模型全部合并 1. [部署 ReRank 模型](/docs/development/custom-models/bge-rerank/) 1. 找到 FastGPT 的配置文件中的 `reRankModels`, 4.6.6 以前是 `ReRankModels`。 -2. 修改对应的值:(记得去掉注释) +2. 修改对应的值: ```json { diff --git a/docSite/content/zh-cn/docs/development/upgrading/484.md b/docSite/content/zh-cn/docs/development/upgrading/484.md index d673a2da3..9a2592bd1 100644 --- a/docSite/content/zh-cn/docs/development/upgrading/484.md +++ b/docSite/content/zh-cn/docs/development/upgrading/484.md @@ -4,14 +4,26 @@ description: 'FastGPT V4.8.4 更新说明' icon: 'upgrade' draft: false toc: true -weight: 821 +weight: 820 --- - +- 商业版镜像 tag 修改成 v4.8.4 + +### 2. 商业版用户执行初始化 + +从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 商业版的域名**。 + +```bash +curl --location --request POST 'https://{{host}}/api/admin/init/484' \ +--header 'rootkey: {{rootkey}}' \ +--header 'Content-Type: application/json' +``` ## V4.8.4 更新说明 diff --git a/files/docker/docker-compose-milvus.yml b/files/docker/docker-compose-milvus.yml index 20fcaad10..a5149cbc9 100644 --- a/files/docker/docker-compose-milvus.yml +++ b/files/docker/docker-compose-milvus.yml @@ -154,6 +154,8 @@ services: - MILVUS_TOKEN=none # sandbox 地址 - SANDBOX_URL=http://sandbox:3000 + # 日志等级: debug, info, warn, error + - LOG_LEVEL=info volumes: - ./config.json:/app/data/config.json diff --git a/files/docker/docker-compose-pgvector.yml b/files/docker/docker-compose-pgvector.yml index ae340a4c9..70c5f51c6 100644 --- a/files/docker/docker-compose-pgvector.yml +++ b/files/docker/docker-compose-pgvector.yml @@ -111,6 +111,8 @@ services: - PG_URL=postgresql://username:password@pg:5432/postgres # sandbox 地址 - SANDBOX_URL=http://sandbox:3000 + # 日志等级: debug, info, warn, error + - LOG_LEVEL=info volumes: - ./config.json:/app/data/config.json diff --git a/files/docker/docker-compose-zilliz.yml b/files/docker/docker-compose-zilliz.yml index 564d7650d..2607ce1ad 100644 --- a/files/docker/docker-compose-zilliz.yml +++ b/files/docker/docker-compose-zilliz.yml @@ -92,6 +92,8 @@ services: - MILVUS_TOKEN=zilliz_cloud_token # sandbox 地址 - SANDBOX_URL=http://sandbox:3000 + # 日志等级: debug, info, warn, error + - LOG_LEVEL=info volumes: - ./config.json:/app/data/config.json diff --git a/packages/global/core/app/collaborator.d.ts b/packages/global/core/app/collaborator.d.ts index bfc2d8751..f9773ec94 100644 --- a/packages/global/core/app/collaborator.d.ts +++ b/packages/global/core/app/collaborator.d.ts @@ -1,9 +1,8 @@ +import { UpdateClbPermissionProps } from '../../support/permission/collaborator'; import { PermissionValueType } from '../../support/permission/type'; -export type UpdateAppCollaboratorBody = { +export type UpdateAppCollaboratorBody = UpdateClbPermissionProps & { appId: string; - tmbIds: string[]; - permission: PermissionValueType; }; export type AppCollaboratorDeleteParams = { diff --git a/packages/global/core/workflow/type/index.d.ts b/packages/global/core/workflow/type/index.d.ts index 726fec06d..3f6e2d2ac 100644 --- a/packages/global/core/workflow/type/index.d.ts +++ b/packages/global/core/workflow/type/index.d.ts @@ -20,6 +20,7 @@ import { RuntimeNodeItemType } from '../runtime/type'; import { PluginTypeEnum } from '../../plugin/constants'; import { RuntimeEdgeItemType, StoreEdgeItemType } from './edge'; import { NextApiResponse } from 'next'; +import { AppDetailType, AppSchema } from '../../app/type'; export type FlowNodeCommonType = { flowNodeType: FlowNodeTypeEnum; // render node card @@ -131,7 +132,7 @@ export type ChatDispatchProps = { teamId: string; tmbId: string; user: UserModelSchema; - appId?: string; + app: AppDetailType | AppSchema; chatId?: string; responseChatItemId?: string; histories: ChatItemType[]; diff --git a/packages/global/support/permission/collaborator.d.ts b/packages/global/support/permission/collaborator.d.ts index 9225151d1..1d4a957de 100644 --- a/packages/global/support/permission/collaborator.d.ts +++ b/packages/global/support/permission/collaborator.d.ts @@ -8,3 +8,8 @@ export type CollaboratorItemType = { name: string; avatar: string; }; + +export type UpdateClbPermissionProps = { + tmbIds: string[]; + permission: PermissionValueType; +}; diff --git a/packages/global/support/permission/constant.ts b/packages/global/support/permission/constant.ts index fbd86a27c..1afdb573f 100644 --- a/packages/global/support/permission/constant.ts +++ b/packages/global/support/permission/constant.ts @@ -11,7 +11,10 @@ export enum AuthUserTypeEnum { export enum PermissionTypeEnum { 'private' = 'private', - 'public' = 'public' + 'public' = 'public', + clbPrivate = 'clbPrivate', + publicRead = 'publicRead', + publicWrite = 'publicWrite' } export const PermissionTypeMap = { [PermissionTypeEnum.private]: { @@ -21,6 +24,18 @@ export const PermissionTypeMap = { [PermissionTypeEnum.public]: { iconLight: 'support/permission/publicLight', label: 'permission.Public' + }, + [PermissionTypeEnum.publicRead]: { + iconLight: 'support/permission/publicLight', + label: '团队可访问' + }, + [PermissionTypeEnum.publicWrite]: { + iconLight: 'support/permission/publicLight', + label: '团队可编辑' + }, + [PermissionTypeEnum.clbPrivate]: { + iconLight: 'support/permission/privateLight', + label: '仅协作者' } }; diff --git a/packages/global/support/permission/user/constant.ts b/packages/global/support/permission/user/constant.ts index 8dcb8a81e..2ff438e0f 100644 --- a/packages/global/support/permission/user/constant.ts +++ b/packages/global/support/permission/user/constant.ts @@ -1,11 +1,14 @@ import { PermissionKeyEnum, PermissionList, ReadPermissionVal } from '../constant'; +import { PermissionListType } from '../type'; -export const TeamPermissionList = { +export const TeamPermissionList: PermissionListType = { [PermissionKeyEnum.read]: { - ...PermissionList[PermissionKeyEnum.read] + ...PermissionList[PermissionKeyEnum.read], + description: '成员仅可阅读相关资源,无法新建资源' }, [PermissionKeyEnum.write]: { - ...PermissionList[PermissionKeyEnum.write] + ...PermissionList[PermissionKeyEnum.write], + description: '除了可读资源外,还可以新建新的资源' }, [PermissionKeyEnum.manage]: { ...PermissionList[PermissionKeyEnum.manage], diff --git a/packages/global/support/user/team/controller.d.ts b/packages/global/support/user/team/controller.d.ts index f80bc9fad..6ac334f89 100644 --- a/packages/global/support/user/team/controller.d.ts +++ b/packages/global/support/user/team/controller.d.ts @@ -22,7 +22,7 @@ export type UpdateTeamProps = { /* ------------- member ----------- */ export type DelMemberProps = { - memberId: string; + tmbId: string; }; export type UpdateTeamMemberProps = { teamId: string; @@ -33,7 +33,7 @@ export type UpdateTeamMemberProps = { export type InviteMemberProps = { teamId: string; usernames: string[]; - role: `${TeamMemberRoleEnum}`; + permission: PermissionValueType; }; export type UpdateInviteProps = { tmbId: string; @@ -43,8 +43,3 @@ export type InviteMemberResponse = Record< 'invite' | 'inValid' | 'inTeam', { username: string; userId: string }[] >; - -export type UpdateTeamMemberPermissionProps = { - memberIds: string[]; - permission: PermissionValueType; -}; diff --git a/packages/service/common/string/tiktoken/index.ts b/packages/service/common/string/tiktoken/index.ts index 49a3cd85f..c51f763e7 100644 --- a/packages/service/common/string/tiktoken/index.ts +++ b/packages/service/common/string/tiktoken/index.ts @@ -70,7 +70,7 @@ export const countGptMessagesTokens = ( callbackMap[id] = (data) => { // 检测是否有内存泄漏 - addLog.info(`Count token time: ${Date.now() - start}, token: ${data}`); + addLog.debug(`Count token time: ${Date.now() - start}, token: ${data}`); // console.log(process.memoryUsage()); resolve(data); diff --git a/packages/service/common/system/log.ts b/packages/service/common/system/log.ts index 9157bfa27..768f943f1 100644 --- a/packages/service/common/system/log.ts +++ b/packages/service/common/system/log.ts @@ -1,11 +1,12 @@ import dayjs from 'dayjs'; import chalk from 'chalk'; +import { isProduction } from './constants'; enum LogLevelEnum { - debug = 'debug', - info = 'info', - warn = 'warn', - error = 'error' + debug = 0, + info = 1, + warn = 2, + error = 3 } const logMap = { [LogLevelEnum.debug]: { @@ -21,20 +22,35 @@ const logMap = { levelLog: chalk.red('[Error]') } }; +const envLogLevelMap: Record = { + debug: 0, + info: 1, + warn: 2, + error: 3 +}; + +const logLevel = (() => { + if (!isProduction) return LogLevelEnum.debug; + const envLogLevel = (process.env.LOG_LEVEL || 'info').toLocaleLowerCase(); + if (!envLogLevel || envLogLevelMap[envLogLevel] === undefined) return LogLevelEnum.info; + return envLogLevelMap[envLogLevel]; +})(); /* add logger */ export const addLog = { log(level: LogLevelEnum, msg: string, obj: Record = {}) { + if (level < logLevel) return; + const stringifyObj = JSON.stringify(obj); const isEmpty = Object.keys(obj).length === 0; console.log( `${logMap[level].levelLog} ${dayjs().format('YYYY-MM-DD HH:mm:ss')} ${msg} ${ - level !== 'error' && !isEmpty ? stringifyObj : '' + level !== LogLevelEnum.error && !isEmpty ? stringifyObj : '' }` ); - level === 'error' && console.error(obj); + level === LogLevelEnum.error && console.error(obj); const lokiUrl = process.env.LOKI_LOG_URL as string; if (!lokiUrl) return; diff --git a/packages/service/core/dataset/collection/controller.ts b/packages/service/core/dataset/collection/controller.ts index 6b69ed379..60e6ca483 100644 --- a/packages/service/core/dataset/collection/controller.ts +++ b/packages/service/core/dataset/collection/controller.ts @@ -142,17 +142,17 @@ export const delCollectionRelatedSource = async ({ .map((item) => item?.metadata?.relatedImgId || '') .filter(Boolean); + // delete files + await delFileByFileIdList({ + bucketName: BucketNameEnum.dataset, + fileIdList + }); // delete images await delImgByRelatedId({ teamId, relateIds: relatedImageIds, session }); - // delete files - await delFileByFileIdList({ - bucketName: BucketNameEnum.dataset, - fileIdList - }); }; /** * delete collection and it related data @@ -182,14 +182,16 @@ export async function delCollectionAndRelatedSources({ ); const collectionIds = collections.map((item) => String(item._id)); - await delCollectionRelatedSource({ collections, session }); - // delete training data await MongoDatasetTraining.deleteMany({ teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } }); + + /* file and imgs */ + await delCollectionRelatedSource({ collections, session }); + // delete dataset.datas await MongoDatasetData.deleteMany( { teamId, datasetIds: { $in: datasetIds }, collectionId: { $in: collectionIds } }, @@ -199,6 +201,7 @@ export async function delCollectionAndRelatedSources({ // delete collections await MongoDatasetCollection.deleteMany( { + teamId, _id: { $in: collectionIds } }, { session } diff --git a/packages/service/core/dataset/collection/utils.ts b/packages/service/core/dataset/collection/utils.ts index 0117b0fe0..dd538aa74 100644 --- a/packages/service/core/dataset/collection/utils.ts +++ b/packages/service/core/dataset/collection/utils.ts @@ -42,7 +42,7 @@ export async function findCollectionAndChild({ return collections; } const [collection, childCollections] = await Promise.all([ - MongoDatasetCollection.findById(collectionId, fields), + MongoDatasetCollection.findById(collectionId, fields).lean(), find(collectionId) ]); diff --git a/packages/service/core/dataset/controller.ts b/packages/service/core/dataset/controller.ts index 91ab753e7..8a5b5295a 100644 --- a/packages/service/core/dataset/controller.ts +++ b/packages/service/core/dataset/controller.ts @@ -82,17 +82,18 @@ export async function delDatasetRelevantData({ teamId, datasetId: { $in: datasetIds } }, - '_id teamId fileId metadata' + '_id teamId datasetId fileId metadata' ).lean(); - // image and file - await delCollectionRelatedSource({ collections, session }); - // delete training data await MongoDatasetTraining.deleteMany({ teamId, datasetId: { $in: datasetIds } }); + + // image and file + await delCollectionRelatedSource({ collections, session }); + // delete dataset.datas await MongoDatasetData.deleteMany({ teamId, datasetId: { $in: datasetIds } }, { session }); diff --git a/packages/service/core/workflow/dispatch/index.ts b/packages/service/core/workflow/dispatch/index.ts index d4d20f1df..7ce1f4c2a 100644 --- a/packages/service/core/workflow/dispatch/index.ts +++ b/packages/service/core/workflow/dispatch/index.ts @@ -407,13 +407,13 @@ export function responseStatus({ /* get system variable */ export function getSystemVariable({ user, - appId, + app, chatId, responseChatItemId, histories = [] }: Props) { return { - appId, + appId: String(app._id), chatId, responseChatItemId, histories, diff --git a/packages/service/core/workflow/dispatch/tools/http468.ts b/packages/service/core/workflow/dispatch/tools/http468.ts index 60ee4c1aa..bb4862e96 100644 --- a/packages/service/core/workflow/dispatch/tools/http468.ts +++ b/packages/service/core/workflow/dispatch/tools/http468.ts @@ -43,7 +43,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise => { const { res, - teamId, - tmbId, + app: workflowApp, stream, detail, histories, @@ -46,10 +45,11 @@ export const dispatchAppRequest = async (props: Props): Promise => { return Promise.reject('Input is empty'); } + // 检查该工作流的tmb是否有调用该app的权限(不是校验对话的人,是否有权限) const { app: appData } = await authAppByTmbId({ appId: app.id, - teamId, - tmbId, + teamId: workflowApp.teamId, + tmbId: workflowApp.tmbId, per: ReadPermissionVal }); @@ -68,7 +68,7 @@ export const dispatchAppRequest = async (props: Props): Promise => { const { flowResponses, flowUsages, assistantResponses } = await dispatchWorkFlow({ ...props, - appId: app.id, + app: appData, runtimeNodes: storeNodes2RuntimeNodes(appData.modules, getDefaultEntryNodeIds(appData.modules)), runtimeEdges: initWorkflowEdgeStatus(appData.edges), histories: chatHistories, diff --git a/packages/service/core/workflow/dispatch/tools/runLaf.ts b/packages/service/core/workflow/dispatch/tools/runLaf.ts index 6ba2625d5..d4def684b 100644 --- a/packages/service/core/workflow/dispatch/tools/runLaf.ts +++ b/packages/service/core/workflow/dispatch/tools/runLaf.ts @@ -21,7 +21,7 @@ const UNDEFINED_SIGN = 'UNDEFINED_SIGN'; export const dispatchLafRequest = async (props: LafRequestProps): Promise => { let { - appId, + app: { _id: appId }, chatId, responseChatItemId, variables, diff --git a/packages/service/support/permission/auth/dataset.ts b/packages/service/support/permission/auth/dataset.ts index e55ed064d..0ca4e7caf 100644 --- a/packages/service/support/permission/auth/dataset.ts +++ b/packages/service/support/permission/auth/dataset.ts @@ -198,4 +198,4 @@ export async function authDatasetFile({ } catch (error) { return Promise.reject(DatasetErrEnum.unAuthDatasetFile); } -} \ No newline at end of file +} diff --git a/packages/service/support/permission/controller.ts b/packages/service/support/permission/controller.ts index a0fbf632f..378b58b5e 100644 --- a/packages/service/support/permission/controller.ts +++ b/packages/service/support/permission/controller.ts @@ -7,8 +7,6 @@ import { AuthUserTypeEnum, PerResourceTypeEnum } from '@fastgpt/global/support/p import { authOpenApiKey } from '../openapi/auth'; import { FileTokenQuery } from '@fastgpt/global/common/file/type'; import { MongoResourcePermission } from './schema'; -import { PermissionValueType } from '@fastgpt/global/support/permission/type'; -import { mongoSessionRun } from '../../common/mongo/sessionRun'; export const getResourcePermission = async ({ resourceType, @@ -36,41 +34,6 @@ export const getResourcePermission = async ({ export const delResourcePermissionById = (id: string) => { return MongoResourcePermission.findByIdAndRemove(id); }; -export const updateResourcePermission = async ({ - resourceId, - resourceType, - teamId, - tmbIdList, - permission -}: { - resourceId?: string; - resourceType: PerResourceTypeEnum; - teamId: string; - tmbIdList: string[]; - permission: PermissionValueType; -}) => { - await mongoSessionRun((session) => { - return Promise.all( - tmbIdList.map((tmbId) => - MongoResourcePermission.findOneAndUpdate( - { - resourceType, - teamId, - tmbId, - resourceId - }, - { - permission - }, - { - session, - upsert: true - } - ) - ) - ); - }); -}; /* 下面代码等迁移 */ /* create token */ diff --git a/packages/service/support/permission/schema.ts b/packages/service/support/permission/schema.ts index f072c5fa9..05a2db9ea 100644 --- a/packages/service/support/permission/schema.ts +++ b/packages/service/support/permission/schema.ts @@ -45,6 +45,11 @@ try { unique: true } ); + ResourcePermissionSchema.index({ + resourceType: 1, + teamId: 1, + resourceId: 1 + }); } catch (error) { console.log(error); } diff --git a/packages/service/support/user/team/controller.ts b/packages/service/support/user/team/controller.ts index 43b3ca4d1..0d3cad678 100644 --- a/packages/service/support/user/team/controller.ts +++ b/packages/service/support/user/team/controller.ts @@ -9,10 +9,7 @@ import { MongoTeamMember } from './teamMemberSchema'; import { MongoTeam } from './teamSchema'; import { UpdateTeamProps } from '@fastgpt/global/support/user/team/controller'; import { getResourcePermission } from '../../permission/controller'; -import { - PerResourceTypeEnum, - ReadPermissionVal -} from '@fastgpt/global/support/permission/constant'; +import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant'; import { TeamPermission } from '@fastgpt/global/support/permission/user/controller'; async function getTeamMember(match: Record): Promise { diff --git a/packages/web/components/common/MySelect/index.tsx b/packages/web/components/common/MySelect/index.tsx index aa1afb2a0..53a1db95e 100644 --- a/packages/web/components/common/MySelect/index.tsx +++ b/packages/web/components/common/MySelect/index.tsx @@ -21,6 +21,7 @@ export type SelectProps = ButtonProps & { list: { alias?: string; label: string | React.ReactNode; + description?: string; value: string | number; }[]; isLoading?: boolean; @@ -125,10 +126,12 @@ const MySelect = ( {...menuItemStyles} {...(value === item.value ? { - color: 'primary.500', - bg: 'myWhite.300' + color: 'primary.600', + bg: 'myGray.100' } - : {})} + : { + color: 'myGray.900' + })} onClick={() => { if (onchange && value !== item.value) { onchange(item.value); @@ -136,8 +139,14 @@ const MySelect = ( }} whiteSpace={'pre-wrap'} fontSize={'sm'} + display={'block'} > - {item.label} + {item.label} + {item.description && ( + + {item.description} + + )} ))} diff --git a/packages/web/hooks/useToast.tsx b/packages/web/hooks/useToast.ts similarity index 100% rename from packages/web/hooks/useToast.tsx rename to packages/web/hooks/useToast.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 77c22edc8..654fc6704 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -422,6 +422,9 @@ importers: js-yaml: specifier: ^4.1.0 version: 4.1.0 + json5: + specifier: ^2.2.3 + version: 2.2.3 jsonwebtoken: specifier: ^9.0.2 version: 9.0.2 diff --git a/projects/app/.env.template b/projects/app/.env.template index 13aa25d71..512385605 100644 --- a/projects/app/.env.template +++ b/projects/app/.env.template @@ -32,5 +32,7 @@ SANDBOX_URL=http://localhost:3001 PRO_URL= # 首页路径 HOME_URL=/ +# 日志等级: debug, info, warn, error +LOG_LEVEL=info # Loki Log Path # LOKI_LOG_URL= \ No newline at end of file diff --git a/projects/app/data/config.json b/projects/app/data/config.json index ecd8f51d1..e1e1e59f8 100644 --- a/projects/app/data/config.json +++ b/projects/app/data/config.json @@ -1,37 +1,36 @@ +// 已使用 json5 进行解析,会自动去掉注释,无需手动去除 { "feConfigs": { - "lafEnv": "https://laf.dev" + "lafEnv": "https://laf.dev" // laf环境。 https://laf.run (杭州阿里云) ,或者私有化的laf环境。如果使用 Laf openapi 功能,需要最新版的 laf 。 }, "systemEnv": { - "openapiPrefix": "fastgpt", "vectorMaxProcess": 15, "qaMaxProcess": 15, - "pgHNSWEfSearch": 100, - "tokenWorkers": 20 + "pgHNSWEfSearch": 100 // 向量搜索参数。越大,搜索越精确,但是速度越慢。设置为100,有99%+精度。 }, "llmModels": [ { - "model": "gpt-3.5-turbo", - "name": "gpt-3.5-turbo", - "maxContext": 16000, - "avatar": "/imgs/model/openai.svg", - "maxResponse": 4000, - "quoteMaxToken": 13000, - "maxTemperature": 1.2, - "charsPointsPrice": 0, - "censor": false, - "vision": false, - "datasetProcess": true, - "usedInClassify": true, - "usedInExtractFields": true, - "usedInToolCall": true, - "usedInQueryExtension": true, - "toolChoice": true, - "functionCall": true, - "customCQPrompt": "", - "customExtractPrompt": "", - "defaultSystemChatPrompt": "", - "defaultConfig": {} + "model": "gpt-3.5-turbo", // 模型名(对应OneAPI中渠道的模型名) + "name": "gpt-3.5-turbo", // 模型别名 + "avatar": "/imgs/model/openai.svg", // 模型的logo + "maxContext": 16000, // 最大上下文 + "maxResponse": 4000, // 最大回复 + "quoteMaxToken": 13000, // 最大引用内容 + "maxTemperature": 1.2, // 最大温度 + "charsPointsPrice": 0, // n积分/1k token(商业版) + "censor": false, // 是否开启敏感校验(商业版) + "vision": false, // 是否支持图片输入 + "datasetProcess": true, // 是否设置为知识库处理模型(QA),务必保证至少有一个为true,否则知识库会报错 + "usedInClassify": true, // 是否用于问题分类(务必保证至少有一个为true) + "usedInExtractFields": true, // 是否用于内容提取(务必保证至少有一个为true) + "usedInToolCall": true, // 是否用于工具调用(务必保证至少有一个为true) + "usedInQueryExtension": true, // 是否用于问题优化(务必保证至少有一个为true) + "toolChoice": true, // 是否支持工具选择(分类,内容提取,工具调用会用到。目前只有gpt支持) + "functionCall": false, // 是否支持函数调用(分类,内容提取,工具调用会用到。会优先使用 toolChoice,如果为false,则使用 functionCall,如果仍为 false,则使用提示词模式) + "customCQPrompt": "", // 自定义文本分类提示词(不支持工具和函数调用的模型 + "customExtractPrompt": "", // 自定义内容提取提示词 + "defaultSystemChatPrompt": "", // 对话默认携带的系统提示词 + "defaultConfig": {} // 请求API时,挟带一些默认配置(比如 GLM4 的 top_p) }, { "model": "gpt-4-0125-preview", @@ -82,40 +81,16 @@ ], "vectorModels": [ { - "model": "text-embedding-3-large", - "name": "Embedding-3-large", - "avatar": "/imgs/model/openai.svg", - "charsPointsPrice": 0, - "defaultToken": 512, - "maxToken": 3000, - "weight": 100, - "dbConfig": {}, - "queryConfig": {}, - "defaultConfig": { - "dimensions": 1024 - } - }, - { - "model": "text-embedding-3-small", - "name": "Embedding-3-small", - "avatar": "/imgs/model/openai.svg", - "charsPointsPrice": 0, - "defaultToken": 512, - "maxToken": 3000, - "weight": 100, - "dbConfig": {}, - "queryConfig": {} - }, - { - "model": "text-embedding-ada-002", - "name": "text-embedding-ada-002", - "avatar": "/imgs/model/openai.svg", - "charsPointsPrice": 0, - "defaultToken": 512, - "maxToken": 3000, - "weight": 100, - "dbConfig": {}, - "queryConfig": {} + "model": "text-embedding-ada-002", // 模型名(与OneAPI对应) + "name": "Embedding-2", // 模型展示名 + "avatar": "/imgs/model/openai.svg", // logo + "charsPointsPrice": 0, // n积分/1k token + "defaultToken": 700, // 默认文本分割时候的 token + "maxToken": 3000, // 最大 token + "weight": 100, // 优先训练权重 + "defaultConfig": {}, // 自定义额外参数。例如,如果希望使用 embedding3-large 的话,可以传入 dimensions:1024,来返回1024维度的向量。(目前必须小于1536维度) + "dbConfig": {}, // 存储时的额外参数(非对称向量模型时候需要用到) + "queryConfig": {} // 参训时的额外参数 } ], "reRankModels": [], @@ -125,36 +100,12 @@ "name": "OpenAI TTS1", "charsPointsPrice": 0, "voices": [ - { - "label": "Alloy", - "value": "alloy", - "bufferId": "openai-Alloy" - }, - { - "label": "Echo", - "value": "echo", - "bufferId": "openai-Echo" - }, - { - "label": "Fable", - "value": "fable", - "bufferId": "openai-Fable" - }, - { - "label": "Onyx", - "value": "onyx", - "bufferId": "openai-Onyx" - }, - { - "label": "Nova", - "value": "nova", - "bufferId": "openai-Nova" - }, - { - "label": "Shimmer", - "value": "shimmer", - "bufferId": "openai-Shimmer" - } + { "label": "Alloy", "value": "alloy", "bufferId": "openai-Alloy" }, + { "label": "Echo", "value": "echo", "bufferId": "openai-Echo" }, + { "label": "Fable", "value": "fable", "bufferId": "openai-Fable" }, + { "label": "Onyx", "value": "onyx", "bufferId": "openai-Onyx" }, + { "label": "Nova", "value": "nova", "bufferId": "openai-Nova" }, + { "label": "Shimmer", "value": "shimmer", "bufferId": "openai-Shimmer" } ] } ], diff --git a/projects/app/i18n/en/common.json b/projects/app/i18n/en/common.json index 73ef9b600..8fa52adc5 100644 --- a/projects/app/i18n/en/common.json +++ b/projects/app/i18n/en/common.json @@ -1223,6 +1223,7 @@ "Default permission": "Default permission", "Manage": "Manage", "Not collaborator": "Not collaborator", + "Owner": "Owner", "Permission": "Permission", "Permission config": "Permission config", "Private": "Private", @@ -1571,9 +1572,6 @@ "Invite Member Success Tip": "Invitation completed\nSuccess: {{success}}\nInvalid usernames: {{inValid}}\nAlready in team: {{inTeam}}", "Invite Member Tips": "The invitee can access or use other resources within the team", "Invite Role Admin Alias": "Invite Admin", - "Invite Role Admin Tip": "Admin\nCan create, edit, and use team resources", - "Invite Role Visitor Alias": "Invite Visitor", - "Invite Role Visitor Tip": "Visitor\nCan only use resources, no creation or editing rights", "Leave Team": "Leave Team", "Leave Team Failed": "Failed to leave team", "Manage": "Team Management", diff --git a/projects/app/i18n/en/user.json b/projects/app/i18n/en/user.json index 5a3d9e94b..43268318c 100644 --- a/projects/app/i18n/en/user.json +++ b/projects/app/i18n/en/user.json @@ -1,5 +1,21 @@ { + "permission": { + "Set read permission": "Read permission", + "Set write permission": "Write permission" + }, "team": { "Add manager": "Add manager" + }, + "user": { + "team": { + "permission": { + "Manage": "Admin", + "Manage tip": "Team administrator with full permissions", + "Read": "Read", + "Read desc": "Members can only read related resources and cannot create new resources", + "Write": "Write", + "Write tip": "In addition to readable resources, you can create new resources" + } + } } } diff --git a/projects/app/i18n/zh/common.json b/projects/app/i18n/zh/common.json index 137981015..30eec28d4 100644 --- a/projects/app/i18n/zh/common.json +++ b/projects/app/i18n/zh/common.json @@ -1231,6 +1231,7 @@ "Default permission": "默认权限", "Manage": "管理", "Not collaborator": "暂无协作者", + "Owner": "创建者", "Permission": "权限", "Permission config": "权限配置", "Private": "私有", @@ -1579,9 +1580,6 @@ "Invite Member Success Tip": "邀请成员完成\n成功: {{success}}人\n用户名无效: {{inValid}}\n已在团队中:{{inTeam}}", "Invite Member Tips": "对方可查阅或使用团队内的其他资源", "Invite Role Admin Alias": "邀请管理员加入", - "Invite Role Admin Tip": "管理员\n可创建、编辑和使用团队资源", - "Invite Role Visitor Alias": "邀请访客加入", - "Invite Role Visitor Tip": "访客\n仅可使用资源,无创建编辑权限", "Leave Team": "离开团队", "Leave Team Failed": "离开团队异常", "Manage": "团队管理", diff --git a/projects/app/i18n/zh/user.json b/projects/app/i18n/zh/user.json index 377234a7f..8e1051fa9 100644 --- a/projects/app/i18n/zh/user.json +++ b/projects/app/i18n/zh/user.json @@ -1,4 +1,14 @@ { + "permission": { + "Manage": "管理员", + "Manage tip": "团队管理员,拥有全部权限", + "Read": "仅读", + "Read desc": "成员仅可阅读相关资源,无法新建资源", + "Set read permission": "设为仅读", + "Set write permission": "设为可写", + "Write": "可写", + "Write tip": "除了可读资源外,还可以新建新的资源" + }, "team": { "Add manager": "添加管理员" } diff --git a/projects/app/package.json b/projects/app/package.json index 03d75be2c..a062eeaa8 100644 --- a/projects/app/package.json +++ b/projects/app/package.json @@ -1,6 +1,6 @@ { "name": "app", - "version": "4.8.3", + "version": "4.8.4", "private": false, "scripts": { "dev": "next dev", @@ -43,6 +43,7 @@ "mermaid": "^10.2.3", "nanoid": "^4.0.1", "next": "14.2.3", + "json5": "^2.2.3", "next-i18next": "15.2.0", "nextjs-node-loader": "^1.1.5", "nprogress": "^0.2.0", diff --git a/projects/app/src/components/common/folder/SlideCard.tsx b/projects/app/src/components/common/folder/SlideCard.tsx index fbe99bae9..c53722694 100644 --- a/projects/app/src/components/common/folder/SlideCard.tsx +++ b/projects/app/src/components/common/folder/SlideCard.tsx @@ -8,13 +8,13 @@ import { useTranslation } from 'next-i18next'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { PermissionValueType } from '@fastgpt/global/support/permission/type'; import DefaultPermissionList from '@/components/support/permission/DefaultPerList'; -import { - CollaboratorContextProvider, +import CollaboratorContextProvider, { MemberManagerInputPropsType } from '../../support/permission/MemberManager/context'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; const FolderSlideCard = ({ + refreshDeps, name, intro, onEdit, @@ -25,6 +25,7 @@ const FolderSlideCard = ({ defaultPer, managePer }: { + refreshDeps?: any[]; name: string; intro?: string; onEdit: () => void; @@ -86,22 +87,24 @@ const FolderSlideCard = ({ > {t('common.Move')} - + {managePer.permission.isOwner && ( + + )} )} @@ -125,7 +128,7 @@ const FolderSlideCard = ({ )} - + {({ MemberListCard, onOpenManageModal, onOpenAddMember }) => { return ( <> diff --git a/projects/app/src/components/support/permission/ConfigPerModal/index.tsx b/projects/app/src/components/support/permission/ConfigPerModal/index.tsx index 9cc19ebcf..6e947294b 100644 --- a/projects/app/src/components/support/permission/ConfigPerModal/index.tsx +++ b/projects/app/src/components/support/permission/ConfigPerModal/index.tsx @@ -2,7 +2,7 @@ import React from 'react'; import MyModal from '@fastgpt/web/components/common/MyModal'; import { useTranslation } from 'next-i18next'; import { PermissionValueType } from '@fastgpt/global/support/permission/type'; -import { MemberManagerInputPropsType, CollaboratorContextProvider } from '../MemberManager/context'; +import CollaboratorContextProvider, { MemberManagerInputPropsType } from '../MemberManager/context'; import { Box, Button, Flex, HStack, ModalBody } from '@chakra-ui/react'; import Avatar from '@/components/Avatar'; import DefaultPermissionList from '../DefaultPerList'; diff --git a/projects/app/src/components/support/permission/IconText/index.tsx b/projects/app/src/components/support/permission/IconText/index.tsx index 6c15cbd6b..61d76610b 100644 --- a/projects/app/src/components/support/permission/IconText/index.tsx +++ b/projects/app/src/components/support/permission/IconText/index.tsx @@ -20,8 +20,11 @@ const PermissionIconText = ({ const per = useMemo(() => { if (permission) return permission; - if (defaultPermission) { - return new Permission({ per: defaultPermission }).hasReadPer ? 'public' : 'private'; + if (defaultPermission !== undefined) { + const Per = new Permission({ per: defaultPermission }); + if (Per.hasWritePer) return PermissionTypeEnum.publicWrite; + if (Per.hasReadPer) return PermissionTypeEnum.publicRead; + return PermissionTypeEnum.clbPrivate; } return 'private'; }, [defaultPermission, permission]); diff --git a/projects/app/src/components/support/permission/MemberManager/AddMemberModal.tsx b/projects/app/src/components/support/permission/MemberManager/AddMemberModal.tsx index e52366967..09c5770bf 100644 --- a/projects/app/src/components/support/permission/MemberManager/AddMemberModal.tsx +++ b/projects/app/src/components/support/permission/MemberManager/AddMemberModal.tsx @@ -26,12 +26,14 @@ import MyBox from '@fastgpt/web/components/common/MyBox'; import { ChevronDownIcon } from '@chakra-ui/icons'; import Avatar from '@/components/Avatar'; import { useRequest } from '@fastgpt/web/hooks/useRequest'; +import { useTranslation } from 'next-i18next'; export type AddModalPropsType = { onClose: () => void; }; function AddMemberModal({ onClose }: AddModalPropsType) { + const { t } = useTranslation(); const { userInfo } = useUserStore(); const { permissionList, collaboratorList, onUpdateCollaborators, getPerLabelList } = @@ -63,9 +65,12 @@ function AddMemberModal({ onClose }: AddModalPropsType) { const { mutate: onConfirm, isLoading: isUpdating } = useRequest({ mutationFn: () => { - return onUpdateCollaborators(selectedMemberIdList, selectedPermission); + return onUpdateCollaborators({ + tmbIds: selectedMemberIdList, + permission: selectedPermission + }); }, - successToast: '添加成功', + successToast: t('common.Add Success'), errorToast: 'Error', onSuccess() { onClose(); diff --git a/projects/app/src/components/support/permission/MemberManager/ManageModal.tsx b/projects/app/src/components/support/permission/MemberManager/ManageModal.tsx index c3dc50df0..609553a50 100644 --- a/projects/app/src/components/support/permission/MemberManager/ManageModal.tsx +++ b/projects/app/src/components/support/permission/MemberManager/ManageModal.tsx @@ -39,7 +39,10 @@ function ManageModal({ onClose }: ManageModalProps) { const { mutate: onUpdate, isLoading: isUpdating } = useRequest({ mutationFn: ({ tmbId, per }: { tmbId: string; per: PermissionValueType }) => { - return onUpdateCollaborators([tmbId], per); + return onUpdateCollaborators({ + tmbIds: [tmbId], + permission: per + }); }, successToast: '更新成功', errorToast: 'Error' diff --git a/projects/app/src/components/support/permission/MemberManager/PermissionSelect.tsx b/projects/app/src/components/support/permission/MemberManager/PermissionSelect.tsx index 61ca618e7..1739da037 100644 --- a/projects/app/src/components/support/permission/MemberManager/PermissionSelect.tsx +++ b/projects/app/src/components/support/permission/MemberManager/PermissionSelect.tsx @@ -6,7 +6,8 @@ import { MenuList, Box, Radio, - useOutsideClick + useOutsideClick, + HStack } from '@chakra-ui/react'; import React, { useMemo, useRef, useState } from 'react'; import MyIcon from '@fastgpt/web/components/common/Icon'; @@ -142,9 +143,15 @@ function PermissionSelect({ bottom={0} left={0} /> - + {Button} - + {item.name} - {item.description} + {t(item.description)} @@ -236,7 +243,7 @@ function PermissionSelect({ {onDelete && ( <> - { onDelete(); @@ -244,8 +251,8 @@ function PermissionSelect({ }} > - {t('common.Delete')} - + {t('common.Remove')} + )} diff --git a/projects/app/src/components/support/permission/MemberManager/context.tsx b/projects/app/src/components/support/permission/MemberManager/context.tsx index ccbfbd0d9..3198350a5 100644 --- a/projects/app/src/components/support/permission/MemberManager/context.tsx +++ b/projects/app/src/components/support/permission/MemberManager/context.tsx @@ -1,5 +1,8 @@ import { BoxProps, useDisclosure } from '@chakra-ui/react'; -import { CollaboratorItemType } from '@fastgpt/global/support/permission/collaborator'; +import { + CollaboratorItemType, + UpdateClbPermissionProps +} from '@fastgpt/global/support/permission/collaborator'; import { PermissionList } from '@fastgpt/global/support/permission/constant'; import { Permission } from '@fastgpt/global/support/permission/controller'; import { PermissionListType, PermissionValueType } from '@fastgpt/global/support/permission/type'; @@ -9,6 +12,7 @@ import { createContext } from 'use-context-selector'; import dynamic from 'next/dynamic'; import MemberListCard, { MemberListCardProps } from './MemberListCard'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; const AddMemberModal = dynamic(() => import('./AddMemberModal')); const ManageModal = dynamic(() => import('./ManageModal')); @@ -16,8 +20,9 @@ export type MemberManagerInputPropsType = { permission: Permission; onGetCollaboratorList: () => Promise; permissionList: PermissionListType; - onUpdateCollaborators: (tmbIds: string[], permission: PermissionValueType) => any; + onUpdateCollaborators: (props: UpdateClbPermissionProps) => any; onDelOneCollaborator: (tmbId: string) => any; + refreshDeps?: any[]; }; export type MemberManagerPropsType = MemberManagerInputPropsType & { collaboratorList: CollaboratorItemType[]; @@ -55,24 +60,28 @@ export const CollaboratorContext = createContext({ permission: new Permission() }); -export const CollaboratorContextProvider = ({ +const CollaboratorContextProvider = ({ permission, onGetCollaboratorList, permissionList, onUpdateCollaborators, onDelOneCollaborator, + refreshDeps = [], children }: MemberManagerInputPropsType & { children: (props: ChildrenProps) => ReactNode; }) => { const { data: collaboratorList = [], - refetch: refetchCollaboratorList, - isLoading: isFetchingCollaborator - } = useQuery(['collaboratorList'], onGetCollaboratorList); + runAsync: refetchCollaboratorList, + loading: isFetchingCollaborator + } = useRequest2(onGetCollaboratorList, { + manual: false, + refreshDeps + }); - const onUpdateCollaboratorsThen = async (tmbIds: string[], permission: PermissionValueType) => { - await onUpdateCollaborators(tmbIds, permission); + const onUpdateCollaboratorsThen = async (props: UpdateClbPermissionProps) => { + await onUpdateCollaborators(props); refetchCollaboratorList(); }; const onDelOneCollaboratorThen = async (tmbId: string) => { @@ -136,3 +145,5 @@ export const CollaboratorContextProvider = ({ ); }; + +export default CollaboratorContextProvider; diff --git a/projects/app/src/components/support/permission/PermissionTags/index.tsx b/projects/app/src/components/support/permission/PermissionTags/index.tsx new file mode 100644 index 000000000..99582d5b6 --- /dev/null +++ b/projects/app/src/components/support/permission/PermissionTags/index.tsx @@ -0,0 +1,66 @@ +import React, { useMemo } from 'react'; +import { Permission } from '@fastgpt/global/support/permission/controller'; +import { PermissionListType } from '@fastgpt/global/support/permission/type'; +import { PermissionList } from '@fastgpt/global/support/permission/constant'; +import MyTag from '@fastgpt/web/components/common/Tag/index'; +import { HStack } from '@chakra-ui/react'; +import { useTranslation } from 'next-i18next'; + +const PermissionTag = ({ + permission, + permissionList +}: { + permission: Permission; + permissionList: PermissionListType; +}) => { + const { t } = useTranslation(); + + const { commonLabel, otherLabels } = useMemo(() => { + const Per = new Permission({ per: permission.value }); + + const commonLabel = (() => { + if (permission.isOwner) return t('permission.Owner'); + if (permission.hasManagePer) return PermissionList['manage'].name; + if (permission.hasWritePer) return PermissionList['write'].name; + if (permission.hasReadPer) return PermissionList['read'].name; + + return; + })(); + + const otherLabels: string[] = []; + Object.values(permissionList).forEach((item) => { + if (item.checkBoxType === 'multiple') { + if (Per.checkPer(item.value)) { + otherLabels.push(item.name); + } + } + }); + + return { + commonLabel, + otherLabels + }; + }, [ + permission.hasManagePer, + permission.hasReadPer, + permission.hasWritePer, + permission.value, + permissionList + ]); + return ( + + {commonLabel && ( + + {commonLabel} + + )} + {otherLabels.map((tag, i) => ( + + {tag} + + ))} + + ); +}; + +export default PermissionTag; diff --git a/projects/app/src/components/support/user/team/TeamManageModal/components/InviteModal.tsx b/projects/app/src/components/support/user/team/TeamManageModal/components/InviteModal.tsx index b0583a45e..ffdc77fd8 100644 --- a/projects/app/src/components/support/user/team/TeamManageModal/components/InviteModal.tsx +++ b/projects/app/src/components/support/user/team/TeamManageModal/components/InviteModal.tsx @@ -3,12 +3,18 @@ import MyModal from '@fastgpt/web/components/common/MyModal'; import { useTranslation } from 'next-i18next'; import { ModalCloseButton, ModalBody, Box, ModalFooter, Button } from '@chakra-ui/react'; import TagTextarea from '@/components/common/Textarea/TagTextarea'; -import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant'; import { useRequest } from '@fastgpt/web/hooks/useRequest'; import { postInviteTeamMember } from '@/web/support/user/team/api'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import type { InviteMemberResponse } from '@fastgpt/global/support/user/team/controller.d'; import MySelect from '@fastgpt/web/components/common/MySelect'; +import { + ManagePermissionVal, + ReadPermissionVal, + WritePermissionVal +} from '@fastgpt/global/support/permission/constant'; +import { useI18n } from '@/web/context/I18n'; +import { useUserStore } from '@/web/support/user/useUserStore'; const InviteModal = ({ teamId, @@ -20,25 +26,37 @@ const InviteModal = ({ onSuccess: () => void; }) => { const { t } = useTranslation(); + const { userT } = useI18n(); const { ConfirmModal, openConfirm } = useConfirm({ title: t('user.team.Invite Member Result Tip'), showCancel: false }); + const { userInfo } = useUserStore(); + const [inviteUsernames, setInviteUsernames] = useState([]); const inviteTypes = useMemo( () => [ { - alias: t('user.team.Invite Role Visitor Alias'), - label: t('user.team.Invite Role Visitor Tip'), - value: TeamMemberRoleEnum.visitor + label: userT('permission.Read'), + description: userT('permission.Read desc'), + value: ReadPermissionVal }, { - alias: t('user.team.Invite Role Admin Alias'), - label: t('user.team.Invite Role Admin Tip'), - value: TeamMemberRoleEnum.admin - } + label: userT('permission.Write'), + description: userT('permission.Write tip'), + value: WritePermissionVal + }, + ...(userInfo?.team?.permission.isOwner + ? [ + { + label: userT('permission.Manage'), + description: userT('permission.Manage tip'), + value: ManagePermissionVal + } + ] + : []) ], - [t] + [userInfo?.team?.permission.isOwner, userT] ); const [selectedInviteType, setSelectInviteType] = useState(inviteTypes[0].value); @@ -47,7 +65,7 @@ const InviteModal = ({ return postInviteTeamMember({ teamId, usernames: inviteUsernames, - role: selectedInviteType + permission: selectedInviteType }); }, onSuccess(res: InviteMemberResponse) { diff --git a/projects/app/src/components/support/user/team/TeamManageModal/components/MemberTable.tsx b/projects/app/src/components/support/user/team/TeamManageModal/components/MemberTable.tsx index 4528cdc5b..2c54b9ddb 100644 --- a/projects/app/src/components/support/user/team/TeamManageModal/components/MemberTable.tsx +++ b/projects/app/src/components/support/user/team/TeamManageModal/components/MemberTable.tsx @@ -1,47 +1,51 @@ import Avatar from '@/components/Avatar'; import MyIcon from '@fastgpt/web/components/common/Icon'; -import { Box, MenuButton, Table, TableContainer, Tbody, Td, Th, Thead, Tr } from '@chakra-ui/react'; +import { + Box, + HStack, + MenuButton, + Table, + TableContainer, + Tbody, + Td, + Th, + Thead, + Tr +} from '@chakra-ui/react'; import { TeamMemberRoleEnum, - TeamMemberRoleMap, TeamMemberStatusMap } from '@fastgpt/global/support/user/team/constant'; -import MyMenu from '@fastgpt/web/components/common/MyMenu'; import { useTranslation } from 'next-i18next'; import { useContextSelector } from 'use-context-selector'; import { useUserStore } from '@/web/support/user/useUserStore'; import { TeamModalContext } from '../context'; -import { useRequest } from '@fastgpt/web/hooks/useRequest'; -import { delRemoveMember } from '@/web/support/user/team/api'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import MyBox from '@fastgpt/web/components/common/MyBox'; +import PermissionTags from '@/components/support/permission/PermissionTags'; +import { TeamPermissionList } from '@fastgpt/global/support/permission/user/constant'; +import PermissionSelect from '@/components/support/permission/MemberManager/PermissionSelect'; +import { CollaboratorContext } from '@/components/support/permission/MemberManager/context'; +import { delRemoveMember } from '@/web/support/user/team/api'; function MemberTable() { const { userInfo } = useUserStore(); const { t } = useTranslation(); const { members, refetchMembers } = useContextSelector(TeamModalContext, (v) => v); + const { onUpdateCollaborators } = useContextSelector(CollaboratorContext, (v) => v); const { ConfirmModal: ConfirmRemoveMemberModal, openConfirm: openRemoveMember } = useConfirm({ type: 'delete' }); - const { mutate: onRemoveMember, isLoading: isRemovingMember } = useRequest({ - mutationFn: delRemoveMember, - onSuccess() { - refetchMembers(); - }, - successToast: t('user.team.Remove Member Success'), - errorToast: t('user.team.Remove Member Failed') - }); - return ( - + - + @@ -49,13 +53,20 @@ function MemberTable() { {members.map((item) => ( - + - @@ -63,9 +74,8 @@ function MemberTable() { {userInfo?.team.permission.hasManagePer && item.role !== TeamMemberRoleEnum.owner && item.tmbId !== userInfo?.team.tmbId && ( - } - menuList={[ - { - children: [ - { - label: t('user.team.Remove Member Tip'), - onClick: () => - openRemoveMember( - () => - onRemoveMember({ - teamId: item.teamId, - memberId: item.tmbId - }), - undefined, - t('user.team.Remove Member Confirm Tip', { - username: item.memberName - }) - )() - } - ] - } - ]} + onChange={(permission) => { + onUpdateCollaborators({ + tmbIds: [item.tmbId], + permission + }); + }} + onDelete={() => { + openRemoveMember( + () => delRemoveMember(item.tmbId).then(refetchMembers), + undefined, + t('user.team.Remove Member Confirm Tip', { + username: item.memberName + }) + )(); + }} /> )} diff --git a/projects/app/src/components/support/user/team/TeamManageModal/components/PermissionManage/AddManager.tsx b/projects/app/src/components/support/user/team/TeamManageModal/components/PermissionManage/AddManager.tsx index ca599e439..072aeb6ff 100644 --- a/projects/app/src/components/support/user/team/TeamManageModal/components/PermissionManage/AddManager.tsx +++ b/projects/app/src/components/support/user/team/TeamManageModal/components/PermissionManage/AddManager.tsx @@ -38,7 +38,7 @@ function AddManagerModal({ onClose, onSuccess }: { onClose: () => void; onSucces mutationFn: async () => { return updateMemberPermission({ permission: ManagePermissionVal, - memberIds: selected.map((item) => { + tmbIds: selected.map((item) => { return item.tmbId; }) }); diff --git a/projects/app/src/components/support/user/team/TeamManageModal/context.tsx b/projects/app/src/components/support/user/team/TeamManageModal/context.tsx index 4cceb7e71..cd5ba5bf6 100644 --- a/projects/app/src/components/support/user/team/TeamManageModal/context.tsx +++ b/projects/app/src/components/support/user/team/TeamManageModal/context.tsx @@ -1,14 +1,26 @@ -import React, { ReactNode, useState } from 'react'; +import React, { ReactNode, useCallback, useState } from 'react'; import { createContext } from 'use-context-selector'; import type { EditTeamFormDataType } from './components/EditInfoModal'; import dynamic from 'next/dynamic'; import { useQuery } from '@tanstack/react-query'; -import { getTeamList, getTeamMembers, putSwitchTeam } from '@/web/support/user/team/api'; +import { + delMemberPermission, + getTeamList, + getTeamMembers, + putSwitchTeam, + updateMemberPermission +} from '@/web/support/user/team/api'; import { TeamMemberStatusEnum } from '@fastgpt/global/support/user/team/constant'; import { useUserStore } from '@/web/support/user/useUserStore'; import type { TeamTmbItemType, TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; -import { useRequest } from '@fastgpt/web/hooks/useRequest'; +import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useTranslation } from 'next-i18next'; +import CollaboratorContextProvider from '@/components/support/permission/MemberManager/context'; +import { TeamPermissionList } from '@fastgpt/global/support/permission/user/constant'; +import { + CollaboratorItemType, + UpdateClbPermissionProps +} from '@fastgpt/global/support/permission/collaborator'; const EditInfoModal = dynamic(() => import('./components/EditInfoModal')); @@ -55,12 +67,35 @@ export const TeamModalContextProvider = ({ children }: { children: ReactNode }) // member action const { data: members = [], - refetch: refetchMembers, - isLoading: loadingMembers - } = useQuery(['getMembers', userInfo?.team?.teamId], () => { - if (!userInfo?.team?.teamId) return []; - return getTeamMembers(); - }); + runAsync: refetchMembers, + loading: loadingMembers + } = useRequest2( + () => { + if (!userInfo?.team?.teamId) return Promise.resolve([]); + return getTeamMembers(); + }, + { + manual: false, + refreshDeps: [userInfo?.team?.teamId] + } + ); + + const onGetClbList = useCallback(() => { + return refetchMembers().then((res) => + res.map((member) => ({ + teamId: member.teamId, + tmbId: member.tmbId, + permission: member.permission, + name: member.memberName, + avatar: member.avatar + })) + ); + }, [refetchMembers]); + const { runAsync: onUpdatePer, loading: isUpdatingPer } = useRequest2( + (props: UpdateClbPermissionProps) => { + return updateMemberPermission(props); + } + ); const { mutate: onSwitchTeam, isLoading: isSwitchingTeam } = useRequest({ mutationFn: async (teamId: string) => { @@ -70,7 +105,7 @@ export const TeamModalContextProvider = ({ children }: { children: ReactNode }) errorToast: t('user.team.Switch Team Failed') }); - const isLoading = isLoadingTeams || isSwitchingTeam || loadingMembers; + const isLoading = isLoadingTeams || isSwitchingTeam || loadingMembers || isUpdatingPer; const contextValue = { myTeams, @@ -86,16 +121,30 @@ export const TeamModalContextProvider = ({ children }: { children: ReactNode }) return ( - {children} - {!!editTeamData && ( - setEditTeamData(undefined)} - onSuccess={() => { - refetchTeams(); - initUserInfo(); - }} - /> + {userInfo?.team?.permission && ( + + {() => ( + <> + {children} + {!!editTeamData && ( + setEditTeamData(undefined)} + onSuccess={() => { + refetchTeams(); + initUserInfo(); + }} + /> + )} + + )} + )} ); diff --git a/projects/app/src/pages/api/common/system/getInitData.ts b/projects/app/src/pages/api/common/system/getInitData.ts index 62c783535..d9a3a614b 100644 --- a/projects/app/src/pages/api/common/system/getInitData.ts +++ b/projects/app/src/pages/api/common/system/getInitData.ts @@ -12,6 +12,7 @@ import { readConfigData } from '@/service/common/system'; import { exit } from 'process'; import { FastGPTProUrl } from '@fastgpt/service/common/system/constants'; import { initFastGPTConfig } from '@fastgpt/service/common/system/tools'; +import json5 from 'json5'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { await getInitConfig(); @@ -88,7 +89,7 @@ export async function initSystemConfig() { getFastGPTConfigFromDB(), readConfigData('config.json') ]); - const fileRes = JSON.parse(fileConfig) as FastGPTConfigFileType; + const fileRes = json5.parse(fileConfig) as FastGPTConfigFileType; // get config from database const config: FastGPTConfigFileType = { @@ -131,7 +132,7 @@ export function getSystemVersion() { if (process.env.NODE_ENV === 'development') { global.systemVersion = process.env.npm_package_version || '0.0.0'; } else { - const packageJson = JSON.parse(readFileSync('/app/package.json', 'utf-8')); + const packageJson = json5.parse(readFileSync('/app/package.json', 'utf-8')); global.systemVersion = packageJson?.version; } @@ -157,7 +158,7 @@ function getSystemPlugin() { const fileTemplates: (PluginTemplateType & { weight: number })[] = filterFiles.map((filename) => { const content = readFileSync(`${basePath}/${filename}`, 'utf-8'); return { - ...JSON.parse(content), + ...json5.parse(content), id: `${PluginSourceEnum.community}-${filename.replace('.json', '')}`, source: PluginSourceEnum.community }; diff --git a/projects/app/src/pages/api/core/app/del.ts b/projects/app/src/pages/api/core/app/del.ts index 396f86e48..794932155 100644 --- a/projects/app/src/pages/api/core/app/del.ts +++ b/projects/app/src/pages/api/core/app/del.ts @@ -8,8 +8,12 @@ import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun'; import { MongoAppVersion } from '@fastgpt/service/core/app/version/schema'; import { NextAPI } from '@/service/middleware/entry'; import { MongoChatInputGuide } from '@fastgpt/service/core/chat/inputGuide/schema'; -import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant'; +import { + OwnerPermissionVal, + PerResourceTypeEnum +} from '@fastgpt/global/support/permission/constant'; import { findAppAndAllChildren } from '@fastgpt/service/core/app/controller'; +import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema'; async function handler(req: NextApiRequest, res: NextApiResponse) { const { appId } = req.query as { appId: string }; @@ -27,8 +31,6 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { fields: '_id' }); - console.log(apps); - await mongoSessionRun(async (session) => { for await (const app of apps) { const appId = app._id; @@ -65,6 +67,14 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { }, { session } ); + await MongoResourcePermission.deleteMany( + { + resourceType: PerResourceTypeEnum.app, + teamId, + resourceId: appId + }, + { session } + ); // delete app await MongoApp.deleteOne( { diff --git a/projects/app/src/pages/api/core/chat/chatTest.ts b/projects/app/src/pages/api/core/chat/chatTest.ts index e6af6a537..09acb3e65 100644 --- a/projects/app/src/pages/api/core/chat/chatTest.ts +++ b/projects/app/src/pages/api/core/chat/chatTest.ts @@ -14,7 +14,6 @@ import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { dispatchWorkFlow } from '@fastgpt/service/core/workflow/dispatch'; import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { getUserChatInfoAndAuthTeamPoints } from '@/service/support/permission/auth/team'; -import { chatValue2RuntimePrompt } from '@fastgpt/global/core/chat/adapt'; import { RuntimeEdgeItemType } from '@fastgpt/global/core/workflow/type/edge'; import { RuntimeNodeItemType } from '@fastgpt/global/core/workflow/runtime/type'; import { removeEmptyUserInput } from '@fastgpt/global/core/chat/utils'; @@ -61,7 +60,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) } /* user auth */ - const [_, { teamId, tmbId }] = await Promise.all([ + const [{ app }, { teamId, tmbId }] = await Promise.all([ authApp({ req, authToken: true, appId, per: ReadPermissionVal }), authCert({ req, @@ -79,7 +78,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) teamId, tmbId, user, - appId, + app, runtimeNodes: nodes, runtimeEdges: edges, variables, diff --git a/projects/app/src/pages/api/core/workflow/debug.ts b/projects/app/src/pages/api/core/workflow/debug.ts index 53f067a0e..e28a97271 100644 --- a/projects/app/src/pages/api/core/workflow/debug.ts +++ b/projects/app/src/pages/api/core/workflow/debug.ts @@ -9,6 +9,7 @@ import { PostWorkflowDebugProps, PostWorkflowDebugResponse } from '@/global/core import { authPluginCrud } from '@fastgpt/service/support/permission/auth/plugin'; import { NextAPI } from '@/service/middleware/entry'; import { ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; +import { defaultApp } from '@/web/core/app/constants'; async function handler( req: NextApiRequest, @@ -45,6 +46,12 @@ async function handler( // auth balance const { user } = await getUserChatInfoAndAuthTeamPoints(tmbId); + const app = { + ...defaultApp, + teamId, + tmbId + }; + /* start process */ const { flowUsages, flowResponses, debugResponse } = await dispatchWorkFlow({ res, @@ -52,7 +59,7 @@ async function handler( teamId, tmbId, user, - appId, + app, runtimeNodes: nodes, runtimeEdges: edges, variables, diff --git a/projects/app/src/pages/api/proApi/[...path].ts b/projects/app/src/pages/api/proApi/[...path].ts index 7b9a36a58..e7dd2e1d2 100644 --- a/projects/app/src/pages/api/proApi/[...path].ts +++ b/projects/app/src/pages/api/proApi/[...path].ts @@ -13,6 +13,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) if (!requestPath) { throw new Error('url is empty'); } + if (!FastGPTProUrl) { + throw new Error('未配置商业版链接'); + } const parsedUrl = new URL(FastGPTProUrl); delete req.headers?.rootkey; diff --git a/projects/app/src/pages/api/v1/chat/completions.ts b/projects/app/src/pages/api/v1/chat/completions.ts index df9a7933d..2d49fabf3 100644 --- a/projects/app/src/pages/api/v1/chat/completions.ts +++ b/projects/app/src/pages/api/v1/chat/completions.ts @@ -191,7 +191,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { user, teamId: String(teamId), tmbId: String(tmbId), - appId: String(app._id), + app, chatId, responseChatItemId, runtimeNodes: storeNodes2RuntimeNodes(nodes, getDefaultEntryNodeIds(nodes)), diff --git a/projects/app/src/pages/app/detail/components/InfoModal.tsx b/projects/app/src/pages/app/detail/components/InfoModal.tsx index f6ceeec56..10925c291 100644 --- a/projects/app/src/pages/app/detail/components/InfoModal.tsx +++ b/projects/app/src/pages/app/detail/components/InfoModal.tsx @@ -20,7 +20,7 @@ import Avatar from '@/components/Avatar'; import MyModal from '@fastgpt/web/components/common/MyModal'; import { useTranslation } from 'next-i18next'; import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants'; -import { CollaboratorContextProvider } from '@/components/support/permission/MemberManager/context'; +import CollaboratorContextProvider from '@/components/support/permission/MemberManager/context'; import { postUpdateAppCollaborators, deleteAppCollaborators, @@ -35,6 +35,7 @@ import { import { PermissionValueType } from '@fastgpt/global/support/permission/type'; import DefaultPermissionList from '@/components/support/permission/DefaultPerList'; import MyIcon from '@fastgpt/web/components/common/Icon'; +import { UpdateClbPermissionProps } from '@fastgpt/global/support/permission/collaborator'; const InfoModal = ({ onClose }: { onClose: () => void }) => { const { t } = useTranslation(); @@ -123,7 +124,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => { [setValue, t, toast] ); - const onUpdateCollaborators = async (tmbIds: string[], permission: PermissionValueType) => { + const onUpdateCollaborators = async ({ tmbIds, permission }: UpdateClbPermissionProps) => { await postUpdateAppCollaborators({ tmbIds, permission, diff --git a/projects/app/src/pages/app/detail/components/Logs.tsx b/projects/app/src/pages/app/detail/components/Logs.tsx index 34c8aa82d..35ff23656 100644 --- a/projects/app/src/pages/app/detail/components/Logs.tsx +++ b/projects/app/src/pages/app/detail/components/Logs.tsx @@ -108,7 +108,7 @@ const Logs = ({ appId }: { appId: string }) => { - + {logs.map((item) => ( allDatasets.filter((item) => datasets.find((dataset) => dataset.datasetId === item._id)), + [allDatasets, datasets] + ); + useEffect(() => { + if (selectDatasets.length !== datasets.length) { + replaceDatasetList( + selectDatasets.map((item) => ({ + datasetId: item._id + })) + ); + } + }, [datasets, replaceDatasetList, selectDatasets]); const { isOpen: isOpenDatasetSelect, @@ -131,11 +144,6 @@ const EditForm = ({ const scheduledTriggerConfig = watch('chatConfig.scheduledTriggerConfig'); const searchMode = watch('dataset.searchMode'); - const selectDatasets = useMemo( - () => allDatasets.filter((item) => datasets.find((dataset) => dataset.datasetId === item._id)), - [allDatasets, datasets] - ); - const tokenLimit = useMemo(() => { return llmModelList.find((item) => item.model === selectLLMModel)?.quoteMaxToken || 3000; }, [selectLLMModel, llmModelList]); @@ -475,7 +483,7 @@ const EditForm = ({ vectorModel: item.vectorModel }))} onClose={onCloseKbSelect} - onChange={replaceKbList} + onChange={replaceDatasetList} /> )} {isOpenDatasetParams && ( diff --git a/projects/app/src/pages/app/list/component/List.tsx b/projects/app/src/pages/app/list/component/List.tsx index 3ac8340c5..539d78fa7 100644 --- a/projects/app/src/pages/app/list/component/List.tsx +++ b/projects/app/src/pages/app/list/component/List.tsx @@ -271,7 +271,13 @@ const ListItem = () => { permission: editPerApp.permission, onGetCollaboratorList: () => getCollaboratorList(editPerApp._id), permissionList: AppPermissionList, - onUpdateCollaborators: (tmbIds: string[], permission: number) => { + onUpdateCollaborators: ({ + tmbIds, + permission + }: { + tmbIds: string[]; + permission: number; + }) => { return postUpdateAppCollaborators({ tmbIds, permission, diff --git a/projects/app/src/pages/app/list/index.tsx b/projects/app/src/pages/app/list/index.tsx index 4f7e72907..ade675dc9 100644 --- a/projects/app/src/pages/app/list/index.tsx +++ b/projects/app/src/pages/app/list/index.tsx @@ -140,6 +140,7 @@ const MyApps = () => { {!!folderDetail && isPc && ( { @@ -163,7 +164,13 @@ const MyApps = () => { permission: folderDetail.permission, onGetCollaboratorList: () => getCollaboratorList(folderDetail._id), permissionList: AppPermissionList, - onUpdateCollaborators: (tmbIds: string[], permission: number) => { + onUpdateCollaborators: ({ + tmbIds, + permission + }: { + tmbIds: string[]; + permission: number; + }) => { return postUpdateAppCollaborators({ tmbIds, permission, diff --git a/projects/app/src/pages/dataset/detail/components/DataCard.tsx b/projects/app/src/pages/dataset/detail/components/DataCard.tsx index 825fcc2f8..8c55cbcd4 100644 --- a/projects/app/src/pages/dataset/detail/components/DataCard.tsx +++ b/projects/app/src/pages/dataset/detail/components/DataCard.tsx @@ -374,7 +374,6 @@ const DataCard = () => { e.stopPropagation(); openConfirm(async () => { try { - setIsLoading(true); await delOneDatasetDataById(item._id); getData(pageNum); } catch (error) { @@ -383,7 +382,6 @@ const DataCard = () => { status: 'error' }); } - setIsLoading(false); })(); }} /> @@ -411,14 +409,8 @@ const DataCard = () => { - {metadataList.map((item) => ( - + {metadataList.map((item, i) => ( + {item.label} diff --git a/projects/app/src/pages/dataset/detail/components/Slider.tsx b/projects/app/src/pages/dataset/detail/components/Slider.tsx index 2158aba53..131f7910f 100644 --- a/projects/app/src/pages/dataset/detail/components/Slider.tsx +++ b/projects/app/src/pages/dataset/detail/components/Slider.tsx @@ -41,7 +41,7 @@ const Slider = ({ currentTab }: { currentTab: TabEnum }) => { icon: 'common/overviewLight' }, { label: t('core.dataset.test.Search Test'), id: TabEnum.test, icon: 'kbTest' }, - ...(userInfo?.team.permission.hasWritePer && datasetDetail.isOwner + ...(userInfo?.team.permission.hasManagePer || datasetDetail.isOwner ? [{ label: t('common.Config'), id: TabEnum.info, icon: 'common/settingLight' }] : []) ]; diff --git a/projects/app/src/service/core/app/utils.ts b/projects/app/src/service/core/app/utils.ts index 1a21d0292..5d2ba7ae2 100644 --- a/projects/app/src/service/core/app/utils.ts +++ b/projects/app/src/service/core/app/utils.ts @@ -34,7 +34,7 @@ export const getScheduleTriggerApp = async () => { mode: 'chat', teamId: String(app.teamId), tmbId: String(app.tmbId), - appId: String(app._id), + app, runtimeNodes: storeNodes2RuntimeNodes(app.modules, getDefaultEntryNodeIds(app.modules)), runtimeEdges: initWorkflowEdgeStatus(app.edges), variables: {}, diff --git a/projects/app/src/web/support/user/team/api.ts b/projects/app/src/web/support/user/team/api.ts index 49fed15a0..282f3e7a1 100644 --- a/projects/app/src/web/support/user/team/api.ts +++ b/projects/app/src/web/support/user/team/api.ts @@ -1,12 +1,10 @@ import { GET, POST, PUT, DELETE } from '@/web/common/api/request'; +import { UpdateClbPermissionProps } from '@fastgpt/global/support/permission/collaborator'; import { CreateTeamProps, - DelMemberProps, InviteMemberProps, InviteMemberResponse, UpdateInviteProps, - UpdateTeamMemberPermissionProps, - UpdateTeamMemberProps, UpdateTeamProps } from '@fastgpt/global/support/user/team/controller.d'; import type { TeamTagItemType, TeamTagSchema } from '@fastgpt/global/support/user/team/type'; @@ -33,15 +31,15 @@ export const postInviteTeamMember = (data: InviteMemberProps) => POST(`/proApi/support/user/team/member/invite`, data); export const putUpdateMemberName = (name: string) => PUT(`/proApi/support/user/team/member/updateName`, { name }); -export const delRemoveMember = (props: DelMemberProps) => - DELETE(`/proApi/support/user/team/member/delete`, props); +export const delRemoveMember = (tmbId: string) => + DELETE(`/proApi/support/user/team/member/delete`, { tmbId }); export const updateInviteResult = (data: UpdateInviteProps) => PUT('/proApi/support/user/team/member/updateInvite', data); export const delLeaveTeam = (teamId: string) => DELETE('/proApi/support/user/team/member/leave', { teamId }); /* -------------- team collaborator -------------------- */ -export const updateMemberPermission = (data: UpdateTeamMemberPermissionProps) => +export const updateMemberPermission = (data: UpdateClbPermissionProps) => PUT('/proApi/support/user/team/collaborator/update', data); export const delMemberPermission = (tmbId: string) => DELETE('/proApi/support/user/team/collaborator/delete', { tmbId });
{t('common.Username')}{t('user.team.Role')}{t('common.Permission')} {t('common.Status')} {t('common.Action')}
- - - {item.memberName} - + + + + + {item.memberName} + + + + {t(TeamMemberRoleMap[item.role]?.label || '')} {t(TeamMemberStatusMap[item.status]?.label || '')} {appT('Mark Count')}