Group role (#2993)
* feat: app/dataset support group (#2898) * pref: member-group (#2862) * feat: group list ordered by updateTime * fix: transfer ownership of group when deleting member * fix: i18n fix * feat: can not set member as admin/owner when user is not active * fix: GroupInfoModal hover input do not change color * fix(fe): searchinput do not scroll * feat: app collaborator with group, remove default permission * feat: dataset collaborator with group, remove default permission * chore(test): pref mock * chore: remove useless code * chore: adjust * fix: add self as collaborator when creating folder * fix(fe): folder manage menu do not show when user has write permission only * fix: dataset folder create * feat: Add code comment * Pref: app move (#2952) * perf: app schema * doc --------- Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
This commit is contained in:
parent
74d58d562b
commit
f89452acdd
@ -23,7 +23,9 @@ weight: 812
|
|||||||
9. 新增 - 数据库连接和操作插件
|
9. 新增 - 数据库连接和操作插件
|
||||||
10. 新增 - Cookie 隐私协议提示
|
10. 新增 - Cookie 隐私协议提示
|
||||||
11. 新增 - HTTP 节点支持 JSONPath 表达式
|
11. 新增 - HTTP 节点支持 JSONPath 表达式
|
||||||
12. 修复 - 文件后缀判断,去除 query 影响。
|
12. 新增 - 应用和知识库支持成员组配置权限
|
||||||
13. 修复 - AI 响应为空时,会造成 LLM 历史记录合并。
|
13. 优化 - 循环节点支持选择外部节点的变量
|
||||||
14. 修复 - 用户交互节点未阻塞流程。
|
14. 修复 - 文件后缀判断,去除 query 影响。
|
||||||
15. 修复 - 新建 APP,有时候会导致空指针报错。
|
15. 修复 - AI 响应为空时,会造成 LLM 历史记录合并。
|
||||||
|
16. 修复 - 用户交互节点未阻塞流程。
|
||||||
|
17. 修复 - 新建 APP,有时候会导致空指针报错。
|
||||||
|
|||||||
10
packages/global/core/app/collaborator.d.ts
vendored
10
packages/global/core/app/collaborator.d.ts
vendored
@ -1,4 +1,8 @@
|
|||||||
import { UpdateClbPermissionProps } from '../../support/permission/collaborator';
|
import { RequireOnlyOne } from '../../common/type/utils';
|
||||||
|
import {
|
||||||
|
UpdateClbPermissionProps,
|
||||||
|
UpdatePermissionBody
|
||||||
|
} from '../../support/permission/collaborator';
|
||||||
import { PermissionValueType } from '../../support/permission/type';
|
import { PermissionValueType } from '../../support/permission/type';
|
||||||
|
|
||||||
export type UpdateAppCollaboratorBody = UpdateClbPermissionProps & {
|
export type UpdateAppCollaboratorBody = UpdateClbPermissionProps & {
|
||||||
@ -7,5 +11,7 @@ export type UpdateAppCollaboratorBody = UpdateClbPermissionProps & {
|
|||||||
|
|
||||||
export type AppCollaboratorDeleteParams = {
|
export type AppCollaboratorDeleteParams = {
|
||||||
appId: string;
|
appId: string;
|
||||||
|
} & RequireOnlyOne<{
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
};
|
groupId: string;
|
||||||
|
}>;
|
||||||
|
|||||||
11
packages/global/core/app/type.d.ts
vendored
11
packages/global/core/app/type.d.ts
vendored
@ -10,7 +10,6 @@ import { SelectedDatasetType } from '../workflow/api';
|
|||||||
import { DatasetSearchModeEnum } from '../dataset/constants';
|
import { DatasetSearchModeEnum } from '../dataset/constants';
|
||||||
import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
|
import { TeamTagSchema as TeamTagsSchemaType } from '@fastgpt/global/support/user/team/type.d';
|
||||||
import { StoreEdgeItemType } from '../workflow/type/edge';
|
import { StoreEdgeItemType } from '../workflow/type/edge';
|
||||||
import { PermissionSchemaType, PermissionValueType } from '../../support/permission/type';
|
|
||||||
import { AppPermission } from '../../support/permission/app/controller';
|
import { AppPermission } from '../../support/permission/app/controller';
|
||||||
import { ParentIdType } from '../../common/parentFolder/type';
|
import { ParentIdType } from '../../common/parentFolder/type';
|
||||||
import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant';
|
import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant';
|
||||||
@ -45,7 +44,11 @@ export type AppSchema = {
|
|||||||
|
|
||||||
inited?: boolean;
|
inited?: boolean;
|
||||||
teamTags: string[];
|
teamTags: string[];
|
||||||
} & PermissionSchemaType;
|
inheritPermission?: boolean;
|
||||||
|
|
||||||
|
// abandon
|
||||||
|
defaultPermission?: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type AppListItemType = {
|
export type AppListItemType = {
|
||||||
_id: string;
|
_id: string;
|
||||||
@ -57,7 +60,9 @@ export type AppListItemType = {
|
|||||||
updateTime: Date;
|
updateTime: Date;
|
||||||
pluginData?: AppSchema['pluginData'];
|
pluginData?: AppSchema['pluginData'];
|
||||||
permission: AppPermission;
|
permission: AppPermission;
|
||||||
} & PermissionSchemaType;
|
inheritPermission?: boolean;
|
||||||
|
private?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type AppDetailType = AppSchema & {
|
export type AppDetailType = AppSchema & {
|
||||||
permission: AppPermission;
|
permission: AppPermission;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { UpdateClbPermissionProps } from '../../support/permission/collaborator';
|
import { UpdateClbPermissionProps } from '../../support/permission/collaborator';
|
||||||
import { PermissionValueType } from '../../support/permission/type';
|
import { PermissionValueType } from '../../support/permission/type';
|
||||||
|
import { RequireOnlyOne } from '../../common/type/utils';
|
||||||
|
|
||||||
export type UpdateDatasetCollaboratorBody = UpdateClbPermissionProps & {
|
export type UpdateDatasetCollaboratorBody = UpdateClbPermissionProps & {
|
||||||
datasetId: string;
|
datasetId: string;
|
||||||
@ -7,5 +8,7 @@ export type UpdateDatasetCollaboratorBody = UpdateClbPermissionProps & {
|
|||||||
|
|
||||||
export type DatasetCollaboratorDeleteParams = {
|
export type DatasetCollaboratorDeleteParams = {
|
||||||
datasetId: string;
|
datasetId: string;
|
||||||
|
} & RequireOnlyOne<{
|
||||||
tmbId: string;
|
tmbId: string;
|
||||||
};
|
groupId: string;
|
||||||
|
}>;
|
||||||
|
|||||||
12
packages/global/core/dataset/type.d.ts
vendored
12
packages/global/core/dataset/type.d.ts
vendored
@ -1,4 +1,3 @@
|
|||||||
import { PermissionSchemaType } from '../../support/permission/type';
|
|
||||||
import type { LLMModelItemType, VectorModelItemType } from '../../core/ai/model.d';
|
import type { LLMModelItemType, VectorModelItemType } from '../../core/ai/model.d';
|
||||||
import { PermissionTypeEnum } from '../../support/permission/constant';
|
import { PermissionTypeEnum } from '../../support/permission/constant';
|
||||||
import { PushDatasetDataChunkProps } from './api';
|
import { PushDatasetDataChunkProps } from './api';
|
||||||
@ -32,8 +31,11 @@ export type DatasetSchemaType = {
|
|||||||
selector: string;
|
selector: string;
|
||||||
};
|
};
|
||||||
externalReadUrl?: string;
|
externalReadUrl?: string;
|
||||||
} & PermissionSchemaType;
|
inheritPermission: boolean;
|
||||||
// } & PermissionSchemaType;
|
|
||||||
|
// abandon
|
||||||
|
defaultPermission?: number;
|
||||||
|
};
|
||||||
|
|
||||||
export type DatasetCollectionSchemaType = {
|
export type DatasetCollectionSchemaType = {
|
||||||
_id: string;
|
_id: string;
|
||||||
@ -146,7 +148,9 @@ export type DatasetListItemType = {
|
|||||||
type: `${DatasetTypeEnum}`;
|
type: `${DatasetTypeEnum}`;
|
||||||
permission: DatasetPermission;
|
permission: DatasetPermission;
|
||||||
vectorModel: VectorModelItemType;
|
vectorModel: VectorModelItemType;
|
||||||
} & PermissionSchemaType;
|
inheritPermission: boolean;
|
||||||
|
private?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & {
|
export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel'> & {
|
||||||
vectorModel: VectorModelItemType;
|
vectorModel: VectorModelItemType;
|
||||||
|
|||||||
@ -4,11 +4,13 @@ import { PermissionValueType } from './type';
|
|||||||
|
|
||||||
export type CollaboratorItemType = {
|
export type CollaboratorItemType = {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
tmbId: string;
|
|
||||||
permission: Permission;
|
permission: Permission;
|
||||||
name: string;
|
name: string;
|
||||||
avatar: string;
|
avatar: string;
|
||||||
};
|
} & RequireOnlyOne<{
|
||||||
|
tmbId: string;
|
||||||
|
groupId: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
export type UpdateClbPermissionProps = {
|
export type UpdateClbPermissionProps = {
|
||||||
members?: string[];
|
members?: string[];
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
import { Permission } from './controller';
|
|
||||||
import { PermissionListType } from './type';
|
import { PermissionListType } from './type';
|
||||||
import { i18nT } from '../../../web/i18n/utils';
|
import { i18nT } from '../../../web/i18n/utils';
|
||||||
export enum AuthUserTypeEnum {
|
export enum AuthUserTypeEnum {
|
||||||
|
|||||||
5
packages/global/support/permission/type.d.ts
vendored
5
packages/global/support/permission/type.d.ts
vendored
@ -1,6 +1,7 @@
|
|||||||
import { RequireOnlyOne } from '../../common/type/utils';
|
import { RequireOnlyOne } from '../../common/type/utils';
|
||||||
import { TeamMemberWithUserSchema } from '../user/team/type';
|
import { TeamMemberWithUserSchema } from '../user/team/type';
|
||||||
import { AuthUserTypeEnum, PermissionKeyEnum, PerResourceTypeEnum } from './constant';
|
import { AuthUserTypeEnum, PermissionKeyEnum, PerResourceTypeEnum } from './constant';
|
||||||
|
import { MemberGroupSchemaType } from './memberGroup/type';
|
||||||
|
|
||||||
// PermissionValueType, the type of permission's value is a number, which is a bit field actually.
|
// PermissionValueType, the type of permission's value is a number, which is a bit field actually.
|
||||||
// It is spired by the permission system in Linux.
|
// It is spired by the permission system in Linux.
|
||||||
@ -33,6 +34,10 @@ export type ResourcePerWithTmbWithUser = Omit<ResourcePermissionType, 'tmbId'> &
|
|||||||
tmbId: TeamMemberWithUserSchema;
|
tmbId: TeamMemberWithUserSchema;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ResourcePerWithGroup = Omit<ResourcePermissionType, 'groupId'> & {
|
||||||
|
groupId: MemberGroupSchemaType;
|
||||||
|
};
|
||||||
|
|
||||||
export type PermissionSchemaType = {
|
export type PermissionSchemaType = {
|
||||||
defaultPermission: PermissionValueType;
|
defaultPermission: PermissionValueType;
|
||||||
inheritPermission: boolean;
|
inheritPermission: boolean;
|
||||||
|
|||||||
@ -5,8 +5,6 @@ import {
|
|||||||
TeamCollectionName,
|
TeamCollectionName,
|
||||||
TeamMemberCollectionName
|
TeamMemberCollectionName
|
||||||
} from '@fastgpt/global/support/user/team/constant';
|
} from '@fastgpt/global/support/user/team/constant';
|
||||||
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
|
|
||||||
import { getPermissionSchema } from '@fastgpt/global/support/permission/utils';
|
|
||||||
|
|
||||||
export const AppCollectionName = 'apps';
|
export const AppCollectionName = 'apps';
|
||||||
|
|
||||||
@ -111,8 +109,13 @@ const AppSchema = new Schema({
|
|||||||
inited: {
|
inited: {
|
||||||
type: Boolean
|
type: Boolean
|
||||||
},
|
},
|
||||||
|
inheritPermission: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
|
||||||
...getPermissionSchema(AppDefaultPermissionVal)
|
// abandoned
|
||||||
|
defaultPermission: Number
|
||||||
});
|
});
|
||||||
|
|
||||||
AppSchema.index({ teamId: 1, updateTime: -1 });
|
AppSchema.index({ teamId: 1, updateTime: -1 });
|
||||||
|
|||||||
@ -9,8 +9,6 @@ import {
|
|||||||
TeamCollectionName,
|
TeamCollectionName,
|
||||||
TeamMemberCollectionName
|
TeamMemberCollectionName
|
||||||
} from '@fastgpt/global/support/user/team/constant';
|
} from '@fastgpt/global/support/user/team/constant';
|
||||||
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
|
||||||
import { getPermissionSchema } from '@fastgpt/global/support/permission/utils';
|
|
||||||
import type { DatasetSchemaType } from '@fastgpt/global/core/dataset/type.d';
|
import type { DatasetSchemaType } from '@fastgpt/global/core/dataset/type.d';
|
||||||
|
|
||||||
export const DatasetCollectionName = 'datasets';
|
export const DatasetCollectionName = 'datasets';
|
||||||
@ -88,7 +86,13 @@ const DatasetSchema = new Schema({
|
|||||||
externalReadUrl: {
|
externalReadUrl: {
|
||||||
type: String
|
type: String
|
||||||
},
|
},
|
||||||
...getPermissionSchema(DatasetDefaultPermissionVal)
|
inheritPermission: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
|
||||||
|
// abandoned
|
||||||
|
defaultPermission: Number
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
|||||||
import { splitCombinePluginId } from '../../../core/app/plugin/controller';
|
import { splitCombinePluginId } from '../../../core/app/plugin/controller';
|
||||||
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
|
||||||
import { AuthModeType, AuthResponseType } from '../type';
|
import { AuthModeType, AuthResponseType } from '../type';
|
||||||
|
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
|
||||||
|
|
||||||
export const authPluginByTmbId = async ({
|
export const authPluginByTmbId = async ({
|
||||||
tmbId,
|
tmbId,
|
||||||
@ -60,7 +61,6 @@ export const authAppByTmbId = async ({
|
|||||||
if (isRoot) {
|
if (isRoot) {
|
||||||
return {
|
return {
|
||||||
...app,
|
...app,
|
||||||
defaultPermission: app.defaultPermission,
|
|
||||||
permission: new AppPermission({ isOwner: true })
|
permission: new AppPermission({ isOwner: true })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ export const authAppByTmbId = async ({
|
|||||||
|
|
||||||
const isOwner = tmbPer.isOwner || String(app.tmbId) === String(tmbId);
|
const isOwner = tmbPer.isOwner || String(app.tmbId) === String(tmbId);
|
||||||
|
|
||||||
const { Per, defaultPermission } = await (async () => {
|
const { Per } = await (async () => {
|
||||||
if (
|
if (
|
||||||
AppFolderTypeList.includes(app.type) ||
|
AppFolderTypeList.includes(app.type) ||
|
||||||
app.inheritPermission === false ||
|
app.inheritPermission === false ||
|
||||||
@ -86,10 +86,9 @@ export const authAppByTmbId = async ({
|
|||||||
resourceId: appId,
|
resourceId: appId,
|
||||||
resourceType: PerResourceTypeEnum.app
|
resourceType: PerResourceTypeEnum.app
|
||||||
});
|
});
|
||||||
const Per = new AppPermission({ per: rp ?? app.defaultPermission, isOwner });
|
const Per = new AppPermission({ per: rp ?? AppDefaultPermissionVal, isOwner });
|
||||||
return {
|
return {
|
||||||
Per,
|
Per
|
||||||
defaultPermission: app.defaultPermission
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// is not folder and inheritPermission is true and is not root folder.
|
// is not folder and inheritPermission is true and is not root folder.
|
||||||
@ -104,8 +103,7 @@ export const authAppByTmbId = async ({
|
|||||||
isOwner
|
isOwner
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
Per,
|
Per
|
||||||
defaultPermission: parent.defaultPermission
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
@ -116,7 +114,6 @@ export const authAppByTmbId = async ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...app,
|
...app,
|
||||||
defaultPermission,
|
|
||||||
permission: Per
|
permission: Per
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@ -10,12 +10,17 @@ import { MongoResourcePermission } from './schema';
|
|||||||
import { ClientSession } from 'mongoose';
|
import { ClientSession } from 'mongoose';
|
||||||
import {
|
import {
|
||||||
PermissionValueType,
|
PermissionValueType,
|
||||||
ResourcePermissionType
|
ResourcePermissionType,
|
||||||
|
ResourcePerWithGroup,
|
||||||
|
ResourcePerWithTmbWithUser
|
||||||
} from '@fastgpt/global/support/permission/type';
|
} from '@fastgpt/global/support/permission/type';
|
||||||
import { bucketNameMap } from '@fastgpt/global/common/file/constants';
|
import { bucketNameMap } from '@fastgpt/global/common/file/constants';
|
||||||
import { addMinutes } from 'date-fns';
|
import { addMinutes } from 'date-fns';
|
||||||
import { getGroupsByTmbId } from './memberGroup/controllers';
|
import { getGroupsByTmbId } from './memberGroup/controllers';
|
||||||
import { Permission } from '@fastgpt/global/support/permission/controller';
|
import { Permission } from '@fastgpt/global/support/permission/controller';
|
||||||
|
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||||
|
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||||
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
|
|
||||||
/** get resource permission for a team member
|
/** get resource permission for a team member
|
||||||
* If there is no permission for the team member, it will return undefined
|
* If there is no permission for the team member, it will return undefined
|
||||||
@ -123,20 +128,94 @@ export async function getResourceAllClbs({
|
|||||||
).lean();
|
).lean();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getResourceClbsAndGroups({
|
||||||
|
resourceId,
|
||||||
|
resourceType,
|
||||||
|
teamId,
|
||||||
|
session
|
||||||
|
}: {
|
||||||
|
resourceId: ParentIdType;
|
||||||
|
resourceType: Omit<`${PerResourceTypeEnum}`, 'team'>;
|
||||||
|
teamId: string;
|
||||||
|
session: ClientSession;
|
||||||
|
}) {
|
||||||
|
return MongoResourcePermission.find(
|
||||||
|
{
|
||||||
|
resourceId,
|
||||||
|
resourceType,
|
||||||
|
teamId
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
{ session }
|
||||||
|
).lean();
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getClbsAndGroupsWithInfo = async ({
|
||||||
|
resourceId,
|
||||||
|
resourceType,
|
||||||
|
teamId
|
||||||
|
}: {
|
||||||
|
resourceId: ParentIdType;
|
||||||
|
resourceType: Omit<`${PerResourceTypeEnum}`, 'team'>;
|
||||||
|
teamId: string;
|
||||||
|
}) =>
|
||||||
|
Promise.all([
|
||||||
|
(await MongoResourcePermission.find({
|
||||||
|
teamId,
|
||||||
|
resourceId,
|
||||||
|
resourceType,
|
||||||
|
tmbId: {
|
||||||
|
$exists: true
|
||||||
|
}
|
||||||
|
}).populate({
|
||||||
|
path: 'tmbId',
|
||||||
|
select: 'name userId',
|
||||||
|
populate: {
|
||||||
|
path: 'userId',
|
||||||
|
select: 'avatar'
|
||||||
|
}
|
||||||
|
})) as ResourcePerWithTmbWithUser[],
|
||||||
|
(await MongoResourcePermission.find({
|
||||||
|
teamId,
|
||||||
|
resourceId,
|
||||||
|
resourceType,
|
||||||
|
groupId: {
|
||||||
|
$exists: true
|
||||||
|
}
|
||||||
|
}).populate({
|
||||||
|
path: 'groupId',
|
||||||
|
select: 'name avatar'
|
||||||
|
})) as ResourcePerWithGroup[]
|
||||||
|
]);
|
||||||
|
|
||||||
export const delResourcePermissionById = (id: string) => {
|
export const delResourcePermissionById = (id: string) => {
|
||||||
return MongoResourcePermission.findByIdAndRemove(id);
|
return MongoResourcePermission.findByIdAndRemove(id);
|
||||||
};
|
};
|
||||||
export const delResourcePermission = ({
|
export const delResourcePermission = ({
|
||||||
session,
|
session,
|
||||||
|
tmbId,
|
||||||
|
groupId,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
resourceType: PerResourceTypeEnum;
|
resourceType: PerResourceTypeEnum;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
resourceId: string;
|
resourceId: string;
|
||||||
tmbId: string;
|
|
||||||
session?: ClientSession;
|
session?: ClientSession;
|
||||||
|
tmbId?: string;
|
||||||
|
groupId?: string;
|
||||||
}) => {
|
}) => {
|
||||||
return MongoResourcePermission.deleteOne(props, { session });
|
// tmbId or groupId only one and not both
|
||||||
|
if (!!tmbId === !!groupId) {
|
||||||
|
return Promise.reject(CommonErrEnum.missingParams);
|
||||||
|
}
|
||||||
|
return MongoResourcePermission.deleteOne(
|
||||||
|
{
|
||||||
|
...(tmbId ? { tmbId } : {}),
|
||||||
|
...(groupId ? { groupId } : {}),
|
||||||
|
...props
|
||||||
|
},
|
||||||
|
{ session }
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/* 下面代码等迁移 */
|
/* 下面代码等迁移 */
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import { MongoDatasetData } from '../../../core/dataset/data/schema';
|
|||||||
import { AuthModeType, AuthResponseType } from '../type';
|
import { AuthModeType, AuthResponseType } from '../type';
|
||||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||||
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||||
|
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
||||||
|
|
||||||
export const authDatasetByTmbId = async ({
|
export const authDatasetByTmbId = async ({
|
||||||
tmbId,
|
tmbId,
|
||||||
@ -62,7 +63,7 @@ export const authDatasetByTmbId = async ({
|
|||||||
const isOwner = tmbPer.isOwner || String(dataset.tmbId) === String(tmbId);
|
const isOwner = tmbPer.isOwner || String(dataset.tmbId) === String(tmbId);
|
||||||
|
|
||||||
// get dataset permission or inherit permission from parent folder.
|
// get dataset permission or inherit permission from parent folder.
|
||||||
const { Per, defaultPermission } = await (async () => {
|
const { Per } = await (async () => {
|
||||||
if (
|
if (
|
||||||
dataset.type === DatasetTypeEnum.folder ||
|
dataset.type === DatasetTypeEnum.folder ||
|
||||||
dataset.inheritPermission === false ||
|
dataset.inheritPermission === false ||
|
||||||
@ -78,12 +79,11 @@ export const authDatasetByTmbId = async ({
|
|||||||
resourceType: PerResourceTypeEnum.dataset
|
resourceType: PerResourceTypeEnum.dataset
|
||||||
});
|
});
|
||||||
const Per = new DatasetPermission({
|
const Per = new DatasetPermission({
|
||||||
per: rp ?? dataset.defaultPermission,
|
per: rp ?? DatasetDefaultPermissionVal,
|
||||||
isOwner
|
isOwner
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
Per,
|
Per
|
||||||
defaultPermission: dataset.defaultPermission
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// is not folder and inheritPermission is true and is not root folder.
|
// is not folder and inheritPermission is true and is not root folder.
|
||||||
@ -100,8 +100,7 @@ export const authDatasetByTmbId = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Per,
|
Per
|
||||||
defaultPermission: parent.defaultPermission
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
@ -112,7 +111,6 @@ export const authDatasetByTmbId = async ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...dataset,
|
...dataset,
|
||||||
defaultPermission,
|
|
||||||
permission: Per
|
permission: Per
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
@ -179,14 +177,15 @@ export async function authDatasetCollection({
|
|||||||
tmbId,
|
tmbId,
|
||||||
datasetId: collection.datasetId._id,
|
datasetId: collection.datasetId._id,
|
||||||
per,
|
per,
|
||||||
isRoot: isRootFromHeader || isRoot
|
isRoot: isRootFromHeader
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
teamId,
|
teamId,
|
||||||
tmbId,
|
tmbId,
|
||||||
collection,
|
collection,
|
||||||
permission: dataset.permission
|
permission: dataset.permission,
|
||||||
|
isRoot: isRootFromHeader
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +230,8 @@ export async function authDatasetFile({
|
|||||||
teamId,
|
teamId,
|
||||||
tmbId,
|
tmbId,
|
||||||
file,
|
file,
|
||||||
permission
|
permission,
|
||||||
|
isRoot
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { mongoSessionRun } from '../../common/mongo/sessionRun';
|
import { mongoSessionRun } from '../../common/mongo/sessionRun';
|
||||||
import { MongoResourcePermission } from './schema';
|
import { MongoResourcePermission } from './schema';
|
||||||
import { ClientSession, Model } from 'mongoose';
|
import { ClientSession, Model } from 'mongoose';
|
||||||
import { NullPermission, PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||||
import { getResourceAllClbs } from './controller';
|
import { getResourceClbsAndGroups } from './controller';
|
||||||
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||||
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||||
|
|
||||||
@ -28,7 +28,6 @@ export async function syncChildrenPermission({
|
|||||||
resourceModel,
|
resourceModel,
|
||||||
session,
|
session,
|
||||||
|
|
||||||
defaultPermission,
|
|
||||||
collaborators
|
collaborators
|
||||||
}: {
|
}: {
|
||||||
resource: SyncChildrenPermissionResourceType;
|
resource: SyncChildrenPermissionResourceType;
|
||||||
@ -42,7 +41,6 @@ export async function syncChildrenPermission({
|
|||||||
// should be provided when inheritPermission is true
|
// should be provided when inheritPermission is true
|
||||||
session: ClientSession;
|
session: ClientSession;
|
||||||
|
|
||||||
defaultPermission?: PermissionValueType;
|
|
||||||
collaborators?: UpdateCollaboratorItem[];
|
collaborators?: UpdateCollaboratorItem[];
|
||||||
}) {
|
}) {
|
||||||
// only folder has permission
|
// only folder has permission
|
||||||
@ -76,19 +74,6 @@ export async function syncChildrenPermission({
|
|||||||
}
|
}
|
||||||
if (!children.length) return;
|
if (!children.length) return;
|
||||||
|
|
||||||
// Sync default permission
|
|
||||||
if (defaultPermission !== undefined) {
|
|
||||||
await resourceModel.updateMany(
|
|
||||||
{
|
|
||||||
_id: { $in: children }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
defaultPermission
|
|
||||||
},
|
|
||||||
{ session }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// sync the resource permission
|
// sync the resource permission
|
||||||
if (collaborators) {
|
if (collaborators) {
|
||||||
// Update the collaborators of all children
|
// Update the collaborators of all children
|
||||||
@ -124,28 +109,20 @@ export async function resumeInheritPermission({
|
|||||||
const isFolder = folderTypeList.includes(resource.type);
|
const isFolder = folderTypeList.includes(resource.type);
|
||||||
|
|
||||||
const fn = async (session: ClientSession) => {
|
const fn = async (session: ClientSession) => {
|
||||||
const parentResource = await resourceModel
|
|
||||||
.findById(resource.parentId, 'defaultPermission')
|
|
||||||
.lean<SyncChildrenPermissionResourceType & { defaultPermission: PermissionValueType }>()
|
|
||||||
.session(session);
|
|
||||||
|
|
||||||
const parentDefaultPermissionVal = parentResource?.defaultPermission ?? NullPermission;
|
|
||||||
|
|
||||||
// update the resource permission
|
// update the resource permission
|
||||||
await resourceModel.updateOne(
|
await resourceModel.updateOne(
|
||||||
{
|
{
|
||||||
_id: resource._id
|
_id: resource._id
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
inheritPermission: true,
|
inheritPermission: true
|
||||||
defaultPermission: parentDefaultPermissionVal
|
|
||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
|
|
||||||
// Folder resource, need to sync children
|
// Folder resource, need to sync children
|
||||||
if (isFolder) {
|
if (isFolder) {
|
||||||
const parentClbs = await getResourceAllClbs({
|
const parentClbsAndGroups = await getResourceClbsAndGroups({
|
||||||
resourceId: resource.parentId,
|
resourceId: resource.parentId,
|
||||||
teamId: resource.teamId,
|
teamId: resource.teamId,
|
||||||
resourceType,
|
resourceType,
|
||||||
@ -155,7 +132,7 @@ export async function resumeInheritPermission({
|
|||||||
// sync self
|
// sync self
|
||||||
await syncCollaborators({
|
await syncCollaborators({
|
||||||
resourceType,
|
resourceType,
|
||||||
collaborators: parentClbs,
|
collaborators: parentClbsAndGroups,
|
||||||
teamId: resource.teamId,
|
teamId: resource.teamId,
|
||||||
resourceId: resource._id,
|
resourceId: resource._id,
|
||||||
session
|
session
|
||||||
@ -169,8 +146,7 @@ export async function resumeInheritPermission({
|
|||||||
folderTypeList,
|
folderTypeList,
|
||||||
resourceType,
|
resourceType,
|
||||||
session,
|
session,
|
||||||
defaultPermission: parentDefaultPermissionVal,
|
collaborators: parentClbsAndGroups
|
||||||
collaborators: parentClbs
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Not folder, delete all clb
|
// Not folder, delete all clb
|
||||||
@ -215,6 +191,7 @@ export async function syncCollaborators({
|
|||||||
resourceId,
|
resourceId,
|
||||||
resourceType: resourceType,
|
resourceType: resourceType,
|
||||||
tmbId: item.tmbId,
|
tmbId: item.tmbId,
|
||||||
|
groupId: item.groupId,
|
||||||
permission: item.permission
|
permission: item.permission
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
|
|||||||
@ -64,7 +64,7 @@ export const getGroupsByTmbId = async ({
|
|||||||
groupId: {
|
groupId: {
|
||||||
$exists: true
|
$exists: true
|
||||||
},
|
},
|
||||||
role: role ? { $in: role } : undefined
|
...(role ? { role: { $in: role } } : {})
|
||||||
})
|
})
|
||||||
.populate('groupId')
|
.populate('groupId')
|
||||||
.lean()
|
.lean()
|
||||||
|
|||||||
@ -28,5 +28,6 @@ export type AuthResponseType<T extends Permission = Permission> = {
|
|||||||
authType?: `${AuthUserTypeEnum}`;
|
authType?: `${AuthUserTypeEnum}`;
|
||||||
appId?: string;
|
appId?: string;
|
||||||
apikey?: string;
|
apikey?: string;
|
||||||
|
isRoot: boolean;
|
||||||
permission: T;
|
permission: T;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { TeamPermission } from '@fastgpt/global/support/permission/user/controll
|
|||||||
|
|
||||||
/* auth user role */
|
/* auth user role */
|
||||||
export async function authUserPer(props: AuthModeType): Promise<
|
export async function authUserPer(props: AuthModeType): Promise<
|
||||||
AuthResponseType & {
|
AuthResponseType<TeamPermission> & {
|
||||||
tmb: TeamTmbItemType;
|
tmb: TeamTmbItemType;
|
||||||
}
|
}
|
||||||
> {
|
> {
|
||||||
|
|||||||
@ -71,6 +71,7 @@
|
|||||||
"modules.Title is required": "模块名不能为空",
|
"modules.Title is required": "模块名不能为空",
|
||||||
"month.unit": "号",
|
"month.unit": "号",
|
||||||
"move_app": "移动应用",
|
"move_app": "移动应用",
|
||||||
|
"move.hint": "移动后,所选应用/文件夹将继承新文件夹的权限设置,原先的权限设置失效。",
|
||||||
"not_json_file": "请选择JSON文件",
|
"not_json_file": "请选择JSON文件",
|
||||||
"or_drag_JSON": "或拖入JSON文件",
|
"or_drag_JSON": "或拖入JSON文件",
|
||||||
"paste_config": "粘贴配置",
|
"paste_config": "粘贴配置",
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
"Folder": "文件夹",
|
"Folder": "文件夹",
|
||||||
"Login": "登录",
|
"Login": "登录",
|
||||||
"Move": "移动",
|
"Move": "移动",
|
||||||
|
"move.confirm": "确认移动",
|
||||||
"Name": "名称",
|
"Name": "名称",
|
||||||
"None": "无",
|
"None": "无",
|
||||||
"Rename": "重命名",
|
"Rename": "重命名",
|
||||||
@ -82,6 +83,8 @@
|
|||||||
"code_error.team_error.un_auth": "无权操作该团队",
|
"code_error.team_error.un_auth": "无权操作该团队",
|
||||||
"code_error.team_error.user_not_active": "用户未接受或已离开团队",
|
"code_error.team_error.user_not_active": "用户未接受或已离开团队",
|
||||||
"code_error.team_error.website_sync_not_enough": "无权使用Web站点同步~",
|
"code_error.team_error.website_sync_not_enough": "无权使用Web站点同步~",
|
||||||
|
"code_error.team_error.group_name_duplicate": "群组名称重复",
|
||||||
|
"code_error.team_error.user_not_active": "用户未接受或已离开团队",
|
||||||
"code_error.token_error_code.403": "登录状态无效,请重新登录",
|
"code_error.token_error_code.403": "登录状态无效,请重新登录",
|
||||||
"code_error.user_error.balance_not_enough": "账号余额不足~",
|
"code_error.user_error.balance_not_enough": "账号余额不足~",
|
||||||
"code_error.user_error.bin_visitor": "您的身份校验未通过",
|
"code_error.user_error.bin_visitor": "您的身份校验未通过",
|
||||||
@ -915,7 +918,7 @@
|
|||||||
"permission.Permission config": "权限配置",
|
"permission.Permission config": "权限配置",
|
||||||
"permission.Private": "私有",
|
"permission.Private": "私有",
|
||||||
"permission.Private Tip": "仅自己可用",
|
"permission.Private Tip": "仅自己可用",
|
||||||
"permission.Public": "团队",
|
"permission.Public": "协作",
|
||||||
"permission.Public Tip": "团队所有成员可使用",
|
"permission.Public Tip": "团队所有成员可使用",
|
||||||
"permission.Remove InheritPermission Confirm": "此操作会导致权限继承失效,是否进行?",
|
"permission.Remove InheritPermission Confirm": "此操作会导致权限继承失效,是否进行?",
|
||||||
"permission.Resume InheritPermission Confirm": "是否恢复为继承父级文件夹的权限?",
|
"permission.Resume InheritPermission Confirm": "是否恢复为继承父级文件夹的权限?",
|
||||||
@ -1194,7 +1197,7 @@
|
|||||||
"user.team.invite.Reject Confirm": "确认拒绝该邀请?",
|
"user.team.invite.Reject Confirm": "确认拒绝该邀请?",
|
||||||
"user.team.invite.accept": "接受",
|
"user.team.invite.accept": "接受",
|
||||||
"user.team.invite.reject": "拒绝",
|
"user.team.invite.reject": "拒绝",
|
||||||
"user.team.member.Confirm Leave": "确认离开该团队?",
|
"user.team.member.Confirm Leave": "确认离开该团队?\n退出后,您在该团队所有的资源( 应用、知识库、文件夹、管理的群组等)均转让给团队所有者。",
|
||||||
"user.team.member.active": "已加入",
|
"user.team.member.active": "已加入",
|
||||||
"user.team.member.reject": "拒绝",
|
"user.team.member.reject": "拒绝",
|
||||||
"user.team.member.waiting": "待接受",
|
"user.team.member.waiting": "待接受",
|
||||||
|
|||||||
@ -34,5 +34,6 @@
|
|||||||
"website_dataset_desc": "Web 站点同步允许你直接使用一个网页链接构建知识库",
|
"website_dataset_desc": "Web 站点同步允许你直接使用一个网页链接构建知识库",
|
||||||
"permission.des.read": "可查看知识库内容",
|
"permission.des.read": "可查看知识库内容",
|
||||||
"permission.des.write": "可增加和变更知识库内容",
|
"permission.des.write": "可增加和变更知识库内容",
|
||||||
"permission.des.manage": "可管理整个知识库数据和信息"
|
"permission.des.manage": "可管理整个知识库数据和信息",
|
||||||
|
"move.hint": "移动后,所选知识库/文件夹将继承新文件夹的权限设置,原先的权限设置失效。"
|
||||||
}
|
}
|
||||||
77
pnpm-lock.yaml
generated
77
pnpm-lock.yaml
generated
@ -560,7 +560,7 @@ importers:
|
|||||||
version: 1.77.8
|
version: 1.77.8
|
||||||
ts-jest:
|
ts-jest:
|
||||||
specifier: ^29.1.0
|
specifier: ^29.1.0
|
||||||
version: 29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0))(typescript@5.5.3)
|
version: 29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.11)(typescript@5.5.3)))(typescript@5.5.3)
|
||||||
use-context-selector:
|
use-context-selector:
|
||||||
specifier: ^1.4.4
|
specifier: ^1.4.4
|
||||||
version: 1.4.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)
|
version: 1.4.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(scheduler@0.23.2)
|
||||||
@ -568,12 +568,18 @@ importers:
|
|||||||
specifier: ^4.3.5
|
specifier: ^4.3.5
|
||||||
version: 4.5.4(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1)
|
version: 4.5.4(@types/react@18.3.1)(immer@9.0.21)(react@18.3.1)
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@faker-js/faker':
|
||||||
|
specifier: ^9.0.3
|
||||||
|
version: 9.0.3
|
||||||
'@shelf/jest-mongodb':
|
'@shelf/jest-mongodb':
|
||||||
specifier: ^4.3.2
|
specifier: ^4.3.2
|
||||||
version: 4.3.2(jest-environment-node@29.7.0)(mongodb@6.9.0(socks@2.8.3))
|
version: 4.3.2(jest-environment-node@29.7.0)(mongodb@6.9.0(socks@2.8.3))
|
||||||
'@svgr/webpack':
|
'@svgr/webpack':
|
||||||
specifier: ^6.5.1
|
specifier: ^6.5.1
|
||||||
version: 6.5.1
|
version: 6.5.1
|
||||||
|
'@types/faker':
|
||||||
|
specifier: ^6.6.9
|
||||||
|
version: 6.6.9
|
||||||
'@types/formidable':
|
'@types/formidable':
|
||||||
specifier: ^2.0.5
|
specifier: ^2.0.5
|
||||||
version: 2.0.6
|
version: 2.0.6
|
||||||
@ -694,7 +700,7 @@ importers:
|
|||||||
version: 6.3.4
|
version: 6.3.4
|
||||||
ts-jest:
|
ts-jest:
|
||||||
specifier: ^29.1.0
|
specifier: ^29.1.0
|
||||||
version: 29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0))(typescript@5.5.3)
|
version: 29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.11)(typescript@5.5.3)))(typescript@5.5.3)
|
||||||
ts-loader:
|
ts-loader:
|
||||||
specifier: ^9.4.3
|
specifier: ^9.4.3
|
||||||
version: 9.5.1(typescript@5.5.3)(webpack@5.92.1)
|
version: 9.5.1(typescript@5.5.3)(webpack@5.92.1)
|
||||||
@ -1991,7 +1997,7 @@ packages:
|
|||||||
'@emotion/use-insertion-effect-with-fallbacks@1.0.1':
|
'@emotion/use-insertion-effect-with-fallbacks@1.0.1':
|
||||||
resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==}
|
resolution: {integrity: sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: 18.3.1
|
react: '>=16.8.0'
|
||||||
|
|
||||||
'@emotion/utils@1.2.1':
|
'@emotion/utils@1.2.1':
|
||||||
resolution: {integrity: sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==}
|
resolution: {integrity: sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==}
|
||||||
@ -2287,6 +2293,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==}
|
resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==}
|
||||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||||
|
|
||||||
|
'@faker-js/faker@9.0.3':
|
||||||
|
resolution: {integrity: sha512-lWrrK4QNlFSU+13PL9jMbMKLJYXDFu3tQfayBsMXX7KL/GiQeqfB1CzHkqD5UHBUtPAuPo6XwGbMFNdVMZObRA==}
|
||||||
|
engines: {node: '>=18.0.0', npm: '>=9.0.0'}
|
||||||
|
|
||||||
'@fastify/accept-negotiator@1.1.0':
|
'@fastify/accept-negotiator@1.1.0':
|
||||||
resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==}
|
resolution: {integrity: sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
@ -2610,8 +2620,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==}
|
resolution: {integrity: sha512-RFkU9/i7cN2bsq/iTkurMWOEErmYcY6JiQI3Jn+WeR/FGISH8JbHERjpS9oRuSOPvDMJI0Z8nJeKkbOs9sBYQw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
monaco-editor: '>= 0.25.0 < 1'
|
monaco-editor: '>= 0.25.0 < 1'
|
||||||
react: 18.3.1
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
react-dom: 18.3.1
|
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
|
|
||||||
'@mongodb-js/saslprep@1.1.7':
|
'@mongodb-js/saslprep@1.1.7':
|
||||||
resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==}
|
resolution: {integrity: sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==}
|
||||||
@ -2962,8 +2972,8 @@ packages:
|
|||||||
'@reactflow/node-resizer@2.2.14':
|
'@reactflow/node-resizer@2.2.14':
|
||||||
resolution: {integrity: sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==}
|
resolution: {integrity: sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: 18.3.1
|
react: '>=17'
|
||||||
react-dom: 18.3.1
|
react-dom: '>=17'
|
||||||
|
|
||||||
'@reactflow/node-toolbar@1.3.14':
|
'@reactflow/node-toolbar@1.3.14':
|
||||||
resolution: {integrity: sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==}
|
resolution: {integrity: sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==}
|
||||||
@ -3332,6 +3342,10 @@ packages:
|
|||||||
'@types/express@4.17.21':
|
'@types/express@4.17.21':
|
||||||
resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==}
|
resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==}
|
||||||
|
|
||||||
|
'@types/faker@6.6.9':
|
||||||
|
resolution: {integrity: sha512-Y9YYm5L//8ooiiknO++4Gr539zzdI0j3aXnOBjo1Vk+kTvffY10GuE2wn78AFPECwZ5MYGTjiDVw1naLLdDimw==}
|
||||||
|
deprecated: This is a stub types definition. faker provides its own type definitions, so you do not need this installed.
|
||||||
|
|
||||||
'@types/formidable@2.0.6':
|
'@types/formidable@2.0.6':
|
||||||
resolution: {integrity: sha512-L4HcrA05IgQyNYJj6kItuIkXrInJvsXTPC5B1i64FggWKKqSL+4hgt7asiSNva75AoLQjq29oPxFfU4GAQ6Z2w==}
|
resolution: {integrity: sha512-L4HcrA05IgQyNYJj6kItuIkXrInJvsXTPC5B1i64FggWKKqSL+4hgt7asiSNva75AoLQjq29oPxFfU4GAQ6Z2w==}
|
||||||
|
|
||||||
@ -5193,6 +5207,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
|
resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
|
faker@6.6.6:
|
||||||
|
resolution: {integrity: sha512-9tCqYEDHI5RYFQigXFwF1hnCwcWCOJl/hmll0lr5D2Ljjb0o4wphb69wikeJDz5qCEzXCoPvG6ss5SDP6IfOdg==}
|
||||||
|
|
||||||
fast-content-type-parse@1.1.0:
|
fast-content-type-parse@1.1.0:
|
||||||
resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==}
|
resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==}
|
||||||
|
|
||||||
@ -7054,8 +7071,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@opentelemetry/api': ^1.1.0
|
'@opentelemetry/api': ^1.1.0
|
||||||
'@playwright/test': ^1.41.2
|
'@playwright/test': ^1.41.2
|
||||||
react: 18.3.1
|
react: ^18.2.0
|
||||||
react-dom: 18.3.1
|
react-dom: ^18.2.0
|
||||||
sass: ^1.3.0
|
sass: ^1.3.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
'@opentelemetry/api':
|
'@opentelemetry/api':
|
||||||
@ -7707,8 +7724,8 @@ packages:
|
|||||||
react-photo-view@1.2.6:
|
react-photo-view@1.2.6:
|
||||||
resolution: {integrity: sha512-Fq17yxkMIv0oFp7HOJr39HgCZRP6A9K5T5rixJ4flSUYT2OO3V8vNxEExjhIKgIrfmTu+mDnHYEsI9RRWi1JHw==}
|
resolution: {integrity: sha512-Fq17yxkMIv0oFp7HOJr39HgCZRP6A9K5T5rixJ4flSUYT2OO3V8vNxEExjhIKgIrfmTu+mDnHYEsI9RRWi1JHw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: 18.3.1
|
react: '>=16.8.0'
|
||||||
react-dom: 18.3.1
|
react-dom: '>=16.8.0'
|
||||||
|
|
||||||
react-redux@7.2.9:
|
react-redux@7.2.9:
|
||||||
resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==}
|
resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==}
|
||||||
@ -7726,8 +7743,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==}
|
resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': 18.3.1
|
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
react: 18.3.1
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
@ -7746,8 +7763,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': 18.3.1
|
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
react: 18.3.1
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
@ -8762,8 +8779,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==}
|
resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': 18.3.1
|
'@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
react: 18.3.1
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
@ -8813,8 +8830,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
|
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@types/react': 18.3.1
|
'@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0
|
||||||
react: 18.3.1
|
react: ^16.8.0 || ^17.0.0 || ^18.0.0
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
@ -11121,6 +11138,8 @@ snapshots:
|
|||||||
|
|
||||||
'@eslint/js@8.56.0': {}
|
'@eslint/js@8.56.0': {}
|
||||||
|
|
||||||
|
'@faker-js/faker@9.0.3': {}
|
||||||
|
|
||||||
'@fastify/accept-negotiator@1.1.0': {}
|
'@fastify/accept-negotiator@1.1.0': {}
|
||||||
|
|
||||||
'@fastify/ajv-compiler@3.6.0':
|
'@fastify/ajv-compiler@3.6.0':
|
||||||
@ -12345,6 +12364,10 @@ snapshots:
|
|||||||
'@types/qs': 6.9.15
|
'@types/qs': 6.9.15
|
||||||
'@types/serve-static': 1.15.7
|
'@types/serve-static': 1.15.7
|
||||||
|
|
||||||
|
'@types/faker@6.6.9':
|
||||||
|
dependencies:
|
||||||
|
faker: 6.6.6
|
||||||
|
|
||||||
'@types/formidable@2.0.6':
|
'@types/formidable@2.0.6':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 20.14.11
|
'@types/node': 20.14.11
|
||||||
@ -14384,7 +14407,7 @@ snapshots:
|
|||||||
eslint: 8.56.0
|
eslint: 8.56.0
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0)
|
eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0)
|
||||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0)
|
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0)
|
||||||
eslint-plugin-jsx-a11y: 6.9.0(eslint@8.56.0)
|
eslint-plugin-jsx-a11y: 6.9.0(eslint@8.56.0)
|
||||||
eslint-plugin-react: 7.34.4(eslint@8.56.0)
|
eslint-plugin-react: 7.34.4(eslint@8.56.0)
|
||||||
eslint-plugin-react-hooks: 4.6.2(eslint@8.56.0)
|
eslint-plugin-react-hooks: 4.6.2(eslint@8.56.0)
|
||||||
@ -14407,8 +14430,8 @@ snapshots:
|
|||||||
debug: 4.3.5
|
debug: 4.3.5
|
||||||
enhanced-resolve: 5.17.0
|
enhanced-resolve: 5.17.0
|
||||||
eslint: 8.56.0
|
eslint: 8.56.0
|
||||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0)
|
eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0)
|
||||||
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0)
|
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0)
|
||||||
fast-glob: 3.3.2
|
fast-glob: 3.3.2
|
||||||
get-tsconfig: 4.7.5
|
get-tsconfig: 4.7.5
|
||||||
is-core-module: 2.14.0
|
is-core-module: 2.14.0
|
||||||
@ -14419,7 +14442,7 @@ snapshots:
|
|||||||
- eslint-import-resolver-webpack
|
- eslint-import-resolver-webpack
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0):
|
eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@ -14430,7 +14453,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0):
|
eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
array-includes: 3.1.8
|
array-includes: 3.1.8
|
||||||
array.prototype.findlastindex: 1.2.5
|
array.prototype.findlastindex: 1.2.5
|
||||||
@ -14440,7 +14463,7 @@ snapshots:
|
|||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 8.56.0
|
eslint: 8.56.0
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0)
|
eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.5.3))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(eslint@8.56.0))(eslint@8.56.0))(eslint@8.56.0)
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
is-core-module: 2.14.0
|
is-core-module: 2.14.0
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@ -14693,6 +14716,8 @@ snapshots:
|
|||||||
iconv-lite: 0.4.24
|
iconv-lite: 0.4.24
|
||||||
tmp: 0.0.33
|
tmp: 0.0.33
|
||||||
|
|
||||||
|
faker@6.6.6: {}
|
||||||
|
|
||||||
fast-content-type-parse@1.1.0: {}
|
fast-content-type-parse@1.1.0: {}
|
||||||
|
|
||||||
fast-decode-uri-component@1.0.1: {}
|
fast-decode-uri-component@1.0.1: {}
|
||||||
@ -18832,7 +18857,7 @@ snapshots:
|
|||||||
|
|
||||||
ts-dedent@2.2.0: {}
|
ts-dedent@2.2.0: {}
|
||||||
|
|
||||||
ts-jest@29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0))(typescript@5.5.3):
|
ts-jest@29.2.2(@babel/core@7.24.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.9))(jest@29.7.0(@types/node@20.14.11)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@20.14.11)(typescript@5.5.3)))(typescript@5.5.3):
|
||||||
dependencies:
|
dependencies:
|
||||||
bs-logger: 0.2.6
|
bs-logger: 0.2.6
|
||||||
ejs: 3.1.10
|
ejs: 3.1.10
|
||||||
|
|||||||
@ -34,12 +34,7 @@ const config = {
|
|||||||
// coverageProvider: "babel",
|
// coverageProvider: "babel",
|
||||||
|
|
||||||
// A list of reporter names that Jest uses when writing coverage reports
|
// A list of reporter names that Jest uses when writing coverage reports
|
||||||
// coverageReporters: [
|
coverageReporters: ['json', 'text', 'lcov', 'clover'],
|
||||||
// "json",
|
|
||||||
// "text",
|
|
||||||
// "lcov",
|
|
||||||
// "clover"
|
|
||||||
// ],
|
|
||||||
|
|
||||||
// An object that configures minimum threshold enforcement for coverage results
|
// An object that configures minimum threshold enforcement for coverage results
|
||||||
// coverageThreshold: undefined,
|
// coverageThreshold: undefined,
|
||||||
@ -163,7 +158,7 @@ const config = {
|
|||||||
// testRunner: "jest-circus/runner",
|
// testRunner: "jest-circus/runner",
|
||||||
|
|
||||||
// A map from regular expressions to paths to transformers
|
// A map from regular expressions to paths to transformers
|
||||||
// transform: undefined,
|
transform: {},
|
||||||
|
|
||||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||||
transformIgnorePatterns: [`/node_modules/(?!${esModules})`]
|
transformIgnorePatterns: [`/node_modules/(?!${esModules})`]
|
||||||
|
|||||||
@ -71,8 +71,10 @@
|
|||||||
"zustand": "^4.3.5"
|
"zustand": "^4.3.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@faker-js/faker": "^9.0.3",
|
||||||
"@shelf/jest-mongodb": "^4.3.2",
|
"@shelf/jest-mongodb": "^4.3.2",
|
||||||
"@svgr/webpack": "^6.5.1",
|
"@svgr/webpack": "^6.5.1",
|
||||||
|
"@types/faker": "^6.6.9",
|
||||||
"@types/formidable": "^2.0.5",
|
"@types/formidable": "^2.0.5",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"@types/jsonwebtoken": "^9.0.3",
|
"@types/jsonwebtoken": "^9.0.3",
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { Box, Button, Flex, ModalBody, ModalFooter } from '@chakra-ui/react';
|
import { Box, Button, Flex, ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||||
@ -11,6 +11,7 @@ import { useMemoizedFn, useMount } from 'ahooks';
|
|||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { FolderIcon } from '@fastgpt/global/common/file/image/constants';
|
import { FolderIcon } from '@fastgpt/global/common/file/image/constants';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
|
import LightTip from '@fastgpt/web/components/common/LightTip';
|
||||||
|
|
||||||
type FolderItemType = {
|
type FolderItemType = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -27,9 +28,10 @@ type Props = {
|
|||||||
server: (e: GetResourceFolderListProps) => Promise<GetResourceFolderListItemResponse[]>;
|
server: (e: GetResourceFolderListProps) => Promise<GetResourceFolderListItemResponse[]>;
|
||||||
onConfirm: (id: ParentIdType) => Promise<any>;
|
onConfirm: (id: ParentIdType) => Promise<any>;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
moveHint?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MoveModal = ({ moveResourceId, title, server, onConfirm, onClose }: Props) => {
|
const MoveModal = ({ moveResourceId, title, server, onConfirm, onClose, moveHint }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [selectedId, setSelectedId] = React.useState<string>();
|
const [selectedId, setSelectedId] = React.useState<string>();
|
||||||
const [requestingIdList, setRequestingIdList] = useState<ParentIdType[]>([]);
|
const [requestingIdList, setRequestingIdList] = useState<ParentIdType[]>([]);
|
||||||
@ -170,6 +172,7 @@ const MoveModal = ({ moveResourceId, title, server, onConfirm, onClose }: Props)
|
|||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
<ModalBody flex={'1 0 0'} overflow={'auto'} minH={'400px'}>
|
<ModalBody flex={'1 0 0'} overflow={'auto'} minH={'400px'}>
|
||||||
|
{moveHint && <LightTip text={moveHint} />}
|
||||||
<RenderList list={folderList} />
|
<RenderList list={folderList} />
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import { Box, Button, Flex, HStack } from '@chakra-ui/react';
|
import { Box, Button, Flex, HStack } from '@chakra-ui/react';
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { FolderIcon } from '@fastgpt/global/common/file/image/constants';
|
import { FolderIcon } from '@fastgpt/global/common/file/image/constants';
|
||||||
@ -40,7 +39,7 @@ const FolderSlideCard = ({
|
|||||||
deleteTip: string;
|
deleteTip: string;
|
||||||
onDelete: () => void;
|
onDelete: () => void;
|
||||||
|
|
||||||
defaultPer: {
|
defaultPer?: {
|
||||||
value: PermissionValueType;
|
value: PermissionValueType;
|
||||||
defaultValue: PermissionValueType;
|
defaultValue: PermissionValueType;
|
||||||
onChange: (v: PermissionValueType) => Promise<any>;
|
onChange: (v: PermissionValueType) => Promise<any>;
|
||||||
@ -54,7 +53,6 @@ const FolderSlideCard = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { feConfigs } = useSystemStore();
|
const { feConfigs } = useSystemStore();
|
||||||
const { toast } = useToast();
|
|
||||||
|
|
||||||
const { ConfirmModal, openConfirm } = useConfirm({
|
const { ConfirmModal, openConfirm } = useConfirm({
|
||||||
type: 'delete',
|
type: 'delete',
|
||||||
@ -136,7 +134,7 @@ const FolderSlideCard = ({
|
|||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{managePer.permission.hasManagePer && (
|
{managePer.permission.hasManagePer && !!defaultPer && (
|
||||||
<Box mt={5}>
|
<Box mt={5}>
|
||||||
<Box fontSize={'sm'} color={'myGray.500'}>
|
<Box fontSize={'sm'} color={'myGray.500'}>
|
||||||
{t('common:permission.Default permission')}
|
{t('common:permission.Default permission')}
|
||||||
|
|||||||
@ -1,11 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
|
||||||
import CollaboratorContextProvider, { MemberManagerInputPropsType } from '../MemberManager/context';
|
import CollaboratorContextProvider, { MemberManagerInputPropsType } from '../MemberManager/context';
|
||||||
import { Box, Button, Flex, HStack, ModalBody, useDisclosure } from '@chakra-ui/react';
|
import { Box, Button, Flex, HStack, ModalBody, useDisclosure } from '@chakra-ui/react';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import DefaultPermissionList from '../DefaultPerList';
|
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import ResumeInherit from '../ResumeInheritText';
|
import ResumeInherit from '../ResumeInheritText';
|
||||||
import { ChangeOwnerModal } from '../ChangeOwnerModal';
|
import { ChangeOwnerModal } from '../ChangeOwnerModal';
|
||||||
@ -14,11 +12,6 @@ export type ConfigPerModalProps = {
|
|||||||
avatar?: string;
|
avatar?: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
defaultPer: {
|
|
||||||
value: PermissionValueType;
|
|
||||||
defaultValue: PermissionValueType;
|
|
||||||
onChange: (v: PermissionValueType) => Promise<any>;
|
|
||||||
};
|
|
||||||
managePer: MemberManagerInputPropsType;
|
managePer: MemberManagerInputPropsType;
|
||||||
isInheritPermission?: boolean;
|
isInheritPermission?: boolean;
|
||||||
resumeInheritPermission?: () => void;
|
resumeInheritPermission?: () => void;
|
||||||
@ -30,7 +23,6 @@ export type ConfigPerModalProps = {
|
|||||||
const ConfigPerModal = ({
|
const ConfigPerModal = ({
|
||||||
avatar,
|
avatar,
|
||||||
name,
|
name,
|
||||||
defaultPer,
|
|
||||||
managePer,
|
managePer,
|
||||||
isInheritPermission,
|
isInheritPermission,
|
||||||
resumeInheritPermission,
|
resumeInheritPermission,
|
||||||
@ -66,17 +58,6 @@ const ConfigPerModal = ({
|
|||||||
<ResumeInherit onResume={resumeInheritPermission} />
|
<ResumeInherit onResume={resumeInheritPermission} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Box mt={5}>
|
|
||||||
<Box fontSize={'sm'}>{t('common:permission.Default permission')}</Box>
|
|
||||||
<DefaultPermissionList
|
|
||||||
mt="1"
|
|
||||||
per={defaultPer.value}
|
|
||||||
defaultPer={defaultPer.defaultValue}
|
|
||||||
isInheritPermission={isInheritPermission}
|
|
||||||
onChange={(v) => defaultPer.onChange(v)}
|
|
||||||
hasParent={hasParent}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Box mt={4}>
|
<Box mt={4}>
|
||||||
<CollaboratorContextProvider
|
<CollaboratorContextProvider
|
||||||
{...managePer}
|
{...managePer}
|
||||||
|
|||||||
@ -1,35 +1,22 @@
|
|||||||
import React, { useMemo } from 'react';
|
import React from 'react';
|
||||||
import { PermissionTypeEnum, PermissionTypeMap } from '@fastgpt/global/support/permission/constant';
|
import { PermissionTypeMap } from '@fastgpt/global/support/permission/constant';
|
||||||
import { Box, StackProps, HStack } from '@chakra-ui/react';
|
import { Box, StackProps, HStack } from '@chakra-ui/react';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
|
||||||
import { Permission } from '@fastgpt/global/support/permission/controller';
|
|
||||||
|
|
||||||
const PermissionIconText = ({
|
const PermissionIconText = ({
|
||||||
permission,
|
|
||||||
defaultPermission,
|
|
||||||
w = '1rem',
|
w = '1rem',
|
||||||
fontSize = 'mini',
|
fontSize = 'mini',
|
||||||
iconColor = 'myGray.500',
|
iconColor = 'myGray.500',
|
||||||
|
private: Private = false,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
permission?: `${PermissionTypeEnum}`;
|
private?: boolean;
|
||||||
defaultPermission?: PermissionValueType;
|
|
||||||
iconColor?: string;
|
iconColor?: string;
|
||||||
} & StackProps) => {
|
} & StackProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const per = useMemo(() => {
|
const per = Private ? 'private' : 'public';
|
||||||
if (permission) return permission;
|
|
||||||
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]);
|
|
||||||
|
|
||||||
return PermissionTypeMap[per] ? (
|
return PermissionTypeMap[per] ? (
|
||||||
<HStack spacing={1} fontSize={fontSize} {...props}>
|
<HStack spacing={1} fontSize={fontSize} {...props}>
|
||||||
|
|||||||
@ -2,12 +2,11 @@ import {
|
|||||||
Flex,
|
Flex,
|
||||||
Box,
|
Box,
|
||||||
ModalBody,
|
ModalBody,
|
||||||
InputGroup,
|
|
||||||
InputLeftElement,
|
|
||||||
Input,
|
|
||||||
Checkbox,
|
Checkbox,
|
||||||
ModalFooter,
|
ModalFooter,
|
||||||
Button
|
Button,
|
||||||
|
Grid,
|
||||||
|
HStack
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
@ -18,63 +17,76 @@ import PermissionSelect from './PermissionSelect';
|
|||||||
import PermissionTags from './PermissionTags';
|
import PermissionTags from './PermissionTags';
|
||||||
import { CollaboratorContext } from './context';
|
import { CollaboratorContext } from './context';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
|
||||||
import { ChevronDownIcon } from '@chakra-ui/icons';
|
import { ChevronDownIcon } from '@chakra-ui/icons';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
|
||||||
|
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||||
|
|
||||||
export type AddModalPropsType = {
|
export type AddModalPropsType = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
mode?: 'member' | 'all';
|
||||||
};
|
};
|
||||||
|
|
||||||
function AddMemberModal({ onClose }: AddModalPropsType) {
|
function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { userInfo, loadAndGetTeamMembers } = useUserStore();
|
const { userInfo, loadAndGetTeamMembers, loadAndGetGroups, myGroups } = useUserStore();
|
||||||
|
|
||||||
const { permissionList, collaboratorList, onUpdateCollaborators, getPerLabelList } =
|
const { permissionList, collaboratorList, onUpdateCollaborators, getPerLabelList, permission } =
|
||||||
useContextSelector(CollaboratorContext, (v) => v);
|
useContextSelector(CollaboratorContext, (v) => v);
|
||||||
const [searchText, setSearchText] = useState<string>('');
|
const [searchText, setSearchText] = useState<string>('');
|
||||||
|
|
||||||
const { data: members = [], loading: loadingMembers } = useRequest2(
|
const { data: [members = [], groups = []] = [], loading: loadingMembersAndGroups } = useRequest2(
|
||||||
async () => {
|
async () => {
|
||||||
if (!userInfo?.team?.teamId) return [];
|
if (!userInfo?.team?.teamId) return [[], []];
|
||||||
const members = await loadAndGetTeamMembers(true);
|
return await Promise.all([loadAndGetTeamMembers(true), loadAndGetGroups(true)]);
|
||||||
return members;
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
manual: false,
|
manual: false,
|
||||||
refreshDeps: [userInfo?.team?.teamId]
|
refreshDeps: [userInfo?.team?.teamId]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const filterMembers = useMemo(() => {
|
const filterMembers = useMemo(() => {
|
||||||
return members.filter((item) => {
|
return members.filter((item) => {
|
||||||
// if (item.permission.isOwner) return false;
|
|
||||||
if (item.tmbId === userInfo?.team?.tmbId) return false;
|
if (item.tmbId === userInfo?.team?.tmbId) return false;
|
||||||
if (!searchText) return true;
|
if (!searchText) return true;
|
||||||
return item.memberName.includes(searchText);
|
return item.memberName.includes(searchText);
|
||||||
});
|
});
|
||||||
}, [members, searchText, userInfo?.team?.tmbId]);
|
}, [members, searchText, userInfo?.team?.tmbId]);
|
||||||
|
|
||||||
|
const filterGroups = useMemo(() => {
|
||||||
|
if (mode !== 'all') return [];
|
||||||
|
return groups.filter((item) => {
|
||||||
|
if (permission.isOwner) return true; // owner can see all groups
|
||||||
|
if (myGroups.find((i) => String(i._id) === String(item._id))) return false;
|
||||||
|
if (!searchText) return true;
|
||||||
|
return item.name.includes(searchText);
|
||||||
|
});
|
||||||
|
}, [groups, searchText, myGroups, mode, permission]);
|
||||||
|
|
||||||
const [selectedMemberIdList, setSelectedMembers] = useState<string[]>([]);
|
const [selectedMemberIdList, setSelectedMembers] = useState<string[]>([]);
|
||||||
|
const [selectedGroupIdList, setSelectedGroupIdList] = useState<string[]>([]);
|
||||||
const [selectedPermission, setSelectedPermission] = useState(permissionList['read'].value);
|
const [selectedPermission, setSelectedPermission] = useState(permissionList['read'].value);
|
||||||
const perLabel = useMemo(() => {
|
const perLabel = useMemo(() => {
|
||||||
return getPerLabelList(selectedPermission).join('、');
|
return getPerLabelList(selectedPermission).join('、');
|
||||||
}, [getPerLabelList, selectedPermission]);
|
}, [getPerLabelList, selectedPermission]);
|
||||||
|
|
||||||
const { mutate: onConfirm, isLoading: isUpdating } = useRequest({
|
const { runAsync: onConfirm, loading: isUpdating } = useRequest2(
|
||||||
mutationFn: () => {
|
() =>
|
||||||
return onUpdateCollaborators({
|
onUpdateCollaborators({
|
||||||
members: selectedMemberIdList,
|
members: selectedMemberIdList,
|
||||||
|
groups: selectedGroupIdList,
|
||||||
permission: selectedPermission
|
permission: selectedPermission
|
||||||
});
|
}),
|
||||||
},
|
{
|
||||||
successToast: t('common:common.Add Success'),
|
successToast: t('common:common.Add Success'),
|
||||||
errorToast: 'Error',
|
errorToast: 'Error',
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
onClose();
|
onClose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyModal
|
<MyModal
|
||||||
@ -83,17 +95,15 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
|
|||||||
iconSrc="modal/AddClb"
|
iconSrc="modal/AddClb"
|
||||||
title={t('user:team.add_collaborator')}
|
title={t('user:team.add_collaborator')}
|
||||||
minW="800px"
|
minW="800px"
|
||||||
|
isCentered
|
||||||
|
isLoading={loadingMembersAndGroups}
|
||||||
>
|
>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<MyBox
|
<Grid
|
||||||
isLoading={loadingMembers}
|
|
||||||
display={'grid'}
|
|
||||||
minH="400px"
|
|
||||||
border="1px solid"
|
border="1px solid"
|
||||||
borderColor="myGray.200"
|
borderColor="myGray.200"
|
||||||
borderRadius="0.5rem"
|
borderRadius="0.5rem"
|
||||||
gridTemplateColumns="55% 45%"
|
gridTemplateColumns="1fr 1fr"
|
||||||
fontSize={'sm'}
|
|
||||||
>
|
>
|
||||||
<Flex
|
<Flex
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
@ -102,17 +112,53 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
|
|||||||
p="4"
|
p="4"
|
||||||
minH="200px"
|
minH="200px"
|
||||||
>
|
>
|
||||||
<InputGroup alignItems="center" size="sm">
|
<SearchInput
|
||||||
<InputLeftElement>
|
placeholder={t('user:search_user')}
|
||||||
<MyIcon name="common/searchLight" w="16px" color={'myGray.500'} />
|
bgColor="myGray.50"
|
||||||
</InputLeftElement>
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
<Input
|
/>
|
||||||
placeholder={t('user:search_user')}
|
|
||||||
bgColor="myGray.50"
|
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
|
||||||
onChange={(e) => setSearchText(e.target.value)}
|
{filterGroups.map((group) => {
|
||||||
/>
|
const onChange = () => {
|
||||||
</InputGroup>
|
if (selectedGroupIdList.includes(group._id)) {
|
||||||
<Flex flexDirection="column" mt="2">
|
setSelectedGroupIdList(selectedGroupIdList.filter((v) => v !== group._id));
|
||||||
|
} else {
|
||||||
|
setSelectedGroupIdList([...selectedGroupIdList, group._id]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const collaborator = collaboratorList.find((v) => v.groupId === group._id);
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
|
key={group._id}
|
||||||
|
py="2"
|
||||||
|
px="3"
|
||||||
|
borderRadius="sm"
|
||||||
|
alignItems="center"
|
||||||
|
_hover={{
|
||||||
|
bgColor: 'myGray.50',
|
||||||
|
cursor: 'pointer',
|
||||||
|
...(!selectedGroupIdList.includes(group._id)
|
||||||
|
? { svg: { color: 'myGray.50' } }
|
||||||
|
: {})
|
||||||
|
}}
|
||||||
|
onClick={onChange}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
isChecked={selectedGroupIdList.includes(group._id)}
|
||||||
|
icon={<MyIcon name={'common/check'} w={'12px'} />}
|
||||||
|
/>
|
||||||
|
<MyAvatar src={group.avatar} w="1.5rem" borderRadius={'50%'} />
|
||||||
|
<Box ml="2" w="full">
|
||||||
|
{group.name === DefaultGroupName ? userInfo?.team.teamName : group.name}
|
||||||
|
</Box>
|
||||||
|
{!!collaborator && (
|
||||||
|
<PermissionTags permission={collaborator.permission.value} />
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
})}
|
||||||
{filterMembers.map((member) => {
|
{filterMembers.map((member) => {
|
||||||
const onChange = () => {
|
const onChange = () => {
|
||||||
if (selectedMemberIdList.includes(member.tmbId)) {
|
if (selectedMemberIdList.includes(member.tmbId)) {
|
||||||
@ -123,10 +169,10 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
|
|||||||
};
|
};
|
||||||
const collaborator = collaboratorList.find((v) => v.tmbId === member.tmbId);
|
const collaborator = collaboratorList.find((v) => v.tmbId === member.tmbId);
|
||||||
return (
|
return (
|
||||||
<Flex
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
key={member.tmbId}
|
key={member.tmbId}
|
||||||
mt="1"
|
py="2"
|
||||||
py="1"
|
|
||||||
px="3"
|
px="3"
|
||||||
borderRadius="sm"
|
borderRadius="sm"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
@ -137,51 +183,87 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
|
|||||||
? { svg: { color: 'myGray.50' } }
|
? { svg: { color: 'myGray.50' } }
|
||||||
: {})
|
: {})
|
||||||
}}
|
}}
|
||||||
|
onClick={onChange}
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
mr="3"
|
|
||||||
isChecked={selectedMemberIdList.includes(member.tmbId)}
|
isChecked={selectedMemberIdList.includes(member.tmbId)}
|
||||||
icon={<MyIcon name={'common/check'} w={'12px'} />}
|
icon={<MyIcon name={'common/check'} w={'12px'} />}
|
||||||
onChange={onChange}
|
|
||||||
/>
|
/>
|
||||||
<Flex
|
<MyAvatar src={member.avatar} w="1.5rem" borderRadius={'50%'} />
|
||||||
flexDirection="row"
|
<Box w="full" ml="2">
|
||||||
onClick={onChange}
|
{member.memberName}
|
||||||
w="full"
|
</Box>
|
||||||
justifyContent="space-between"
|
{!!collaborator && (
|
||||||
>
|
<PermissionTags permission={collaborator.permission.value} />
|
||||||
<Flex flexDirection="row" alignItems="center">
|
)}
|
||||||
<MyAvatar src={member.avatar} w="32px" />
|
</HStack>
|
||||||
<Box ml="2">{member.memberName}</Box>
|
|
||||||
</Flex>
|
|
||||||
{!!collaborator && (
|
|
||||||
<PermissionTags permission={collaborator.permission.value} />
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex p="4" flexDirection="column">
|
<Flex p="4" flexDirection="column">
|
||||||
<Box>
|
<Box>
|
||||||
{t('user:has_chosen') + ': '}+ {selectedMemberIdList.length}
|
{t('user:has_chosen') + ': '}{' '}
|
||||||
|
{selectedMemberIdList.length + selectedGroupIdList.length}
|
||||||
</Box>
|
</Box>
|
||||||
<Flex flexDirection="column" mt="2">
|
<Flex flexDirection="column" mt="2" overflow={'auto'} maxH="400px">
|
||||||
|
{selectedGroupIdList.map((groupId) => {
|
||||||
|
const onChange = () => {
|
||||||
|
if (selectedGroupIdList.includes(groupId)) {
|
||||||
|
setSelectedGroupIdList(selectedGroupIdList.filter((v) => v !== groupId));
|
||||||
|
} else {
|
||||||
|
setSelectedGroupIdList([...selectedGroupIdList, groupId]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const group = groups.find((v) => String(v._id) === groupId);
|
||||||
|
return (
|
||||||
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
|
key={groupId}
|
||||||
|
py="2"
|
||||||
|
px="3"
|
||||||
|
borderRadius="sm"
|
||||||
|
alignItems="center"
|
||||||
|
_hover={{
|
||||||
|
bgColor: 'myGray.50',
|
||||||
|
cursor: 'pointer',
|
||||||
|
...(!selectedGroupIdList.includes(groupId)
|
||||||
|
? { svg: { color: 'myGray.50' } }
|
||||||
|
: {})
|
||||||
|
}}
|
||||||
|
onClick={onChange}
|
||||||
|
>
|
||||||
|
<MyAvatar src={group?.avatar} w="1.5rem" borderRadius={'50%'} />
|
||||||
|
<Box w="full" ml="2">
|
||||||
|
{group?.name === DefaultGroupName ? userInfo?.team.teamName : group?.name}
|
||||||
|
</Box>
|
||||||
|
<MyIcon
|
||||||
|
name="common/closeLight"
|
||||||
|
w="16px"
|
||||||
|
cursor={'pointer'}
|
||||||
|
_hover={{
|
||||||
|
color: 'red.600'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</HStack>
|
||||||
|
);
|
||||||
|
})}
|
||||||
{selectedMemberIdList.map((tmbId) => {
|
{selectedMemberIdList.map((tmbId) => {
|
||||||
const member = filterMembers.find((v) => v.tmbId === tmbId);
|
const member = filterMembers.find((v) => v.tmbId === tmbId);
|
||||||
return member ? (
|
return member ? (
|
||||||
<Flex
|
<HStack
|
||||||
|
justifyContent="space-between"
|
||||||
key={tmbId}
|
key={tmbId}
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
justifyContent="space-between"
|
|
||||||
py="2"
|
py="2"
|
||||||
px={3}
|
px={3}
|
||||||
borderRadius={'md'}
|
borderRadius={'md'}
|
||||||
_hover={{ bg: 'myGray.50' }}
|
_hover={{ bg: 'myGray.50' }}
|
||||||
_notLast={{ mb: 2 }}
|
onClick={() =>
|
||||||
|
setSelectedMembers(selectedMemberIdList.filter((v) => v !== tmbId))
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<Avatar src={member.avatar} w="24px" />
|
<MyAvatar src={member.avatar} w="1.5rem" borderRadius="50%" />
|
||||||
<Box w="full" ml={2}>
|
<Box w="full" ml={2}>
|
||||||
{member.memberName}
|
{member.memberName}
|
||||||
</Box>
|
</Box>
|
||||||
@ -192,16 +274,13 @@ function AddMemberModal({ onClose }: AddModalPropsType) {
|
|||||||
_hover={{
|
_hover={{
|
||||||
color: 'red.600'
|
color: 'red.600'
|
||||||
}}
|
}}
|
||||||
onClick={() =>
|
|
||||||
setSelectedMembers(selectedMemberIdList.filter((v) => v !== tmbId))
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</Flex>
|
</HStack>
|
||||||
) : null;
|
) : null;
|
||||||
})}
|
})}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
</MyBox>
|
</Grid>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<PermissionSelect
|
<PermissionSelect
|
||||||
|
|||||||
@ -8,11 +8,12 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
|
|||||||
import { CollaboratorContext } from './context';
|
import { CollaboratorContext } from './context';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
||||||
import Loading from '@fastgpt/web/components/common/MyLoading';
|
import Loading from '@fastgpt/web/components/common/MyLoading';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||||
|
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||||
export type ManageModalProps = {
|
export type ManageModalProps = {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
};
|
};
|
||||||
@ -23,21 +24,12 @@ function ManageModal({ onClose }: ManageModalProps) {
|
|||||||
const { permission, collaboratorList, onUpdateCollaborators, onDelOneCollaborator } =
|
const { permission, collaboratorList, onUpdateCollaborators, onDelOneCollaborator } =
|
||||||
useContextSelector(CollaboratorContext, (v) => v);
|
useContextSelector(CollaboratorContext, (v) => v);
|
||||||
|
|
||||||
const { runAsync: onDelete, loading: isDeleting } = useRequest2((tmbId: string) =>
|
const { runAsync: onDelete, loading: isDeleting } = useRequest2(onDelOneCollaborator);
|
||||||
onDelOneCollaborator(tmbId)
|
|
||||||
);
|
|
||||||
|
|
||||||
const { runAsync: onUpdate, loading: isUpdating } = useRequest2(
|
const { runAsync: onUpdate, loading: isUpdating } = useRequest2(onUpdateCollaborators, {
|
||||||
({ tmbId, per }: { tmbId: string; per: PermissionValueType }) =>
|
successToast: t('common.Update Success'),
|
||||||
onUpdateCollaborators({
|
errorToast: 'Error'
|
||||||
members: [tmbId],
|
});
|
||||||
permission: per
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
successToast: t('common.Update Success'),
|
|
||||||
errorToast: 'Error'
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const loading = isDeleting || isUpdating;
|
const loading = isDeleting || isUpdating;
|
||||||
|
|
||||||
@ -74,7 +66,7 @@ function ManageModal({ onClose }: ManageModalProps) {
|
|||||||
<Td border="none">
|
<Td border="none">
|
||||||
<Flex alignItems="center">
|
<Flex alignItems="center">
|
||||||
<Avatar src={item.avatar} w="24px" mr={2} />
|
<Avatar src={item.avatar} w="24px" mr={2} />
|
||||||
{item.name}
|
{item.name === DefaultGroupName ? userInfo?.team.teamName : item.name}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Td>
|
</Td>
|
||||||
<Td border="none">
|
<Td border="none">
|
||||||
@ -89,14 +81,18 @@ function ManageModal({ onClose }: ManageModalProps) {
|
|||||||
<MyIcon name={'edit'} w={'16px'} _hover={{ color: 'primary.600' }} />
|
<MyIcon name={'edit'} w={'16px'} _hover={{ color: 'primary.600' }} />
|
||||||
}
|
}
|
||||||
value={item.permission.value}
|
value={item.permission.value}
|
||||||
onChange={(per) => {
|
onChange={(permission) => {
|
||||||
onUpdate({
|
onUpdate({
|
||||||
tmbId: item.tmbId,
|
members: item.tmbId ? [item.tmbId] : undefined,
|
||||||
per
|
groups: item.groupId ? [item.groupId] : undefined,
|
||||||
|
permission
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
onDelete={() => {
|
onDelete={() => {
|
||||||
onDelete(item.tmbId);
|
onDelete({
|
||||||
|
tmbId: item.tmbId,
|
||||||
|
groupId: item.groupId
|
||||||
|
} as RequireOnlyOne<{ tmbId: string; groupId: string }>);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -6,11 +6,14 @@ import { CollaboratorContext } from './context';
|
|||||||
import Tag, { TagProps } from '@fastgpt/web/components/common/Tag';
|
import Tag, { TagProps } from '@fastgpt/web/components/common/Tag';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||||
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
|
|
||||||
export type MemberListCardProps = BoxProps & { tagStyle?: Omit<TagProps, 'children'> };
|
export type MemberListCardProps = BoxProps & { tagStyle?: Omit<TagProps, 'children'> };
|
||||||
|
|
||||||
const MemberListCard = ({ tagStyle, ...props }: MemberListCardProps) => {
|
const MemberListCard = ({ tagStyle, ...props }: MemberListCardProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { userInfo } = useUserStore();
|
||||||
|
|
||||||
const { collaboratorList, isFetchingCollaborator } = useContextSelector(
|
const { collaboratorList, isFetchingCollaborator } = useContextSelector(
|
||||||
CollaboratorContext,
|
CollaboratorContext,
|
||||||
@ -27,10 +30,15 @@ const MemberListCard = ({ tagStyle, ...props }: MemberListCardProps) => {
|
|||||||
<Flex gap="2" flexWrap={'wrap'}>
|
<Flex gap="2" flexWrap={'wrap'}>
|
||||||
{collaboratorList?.map((member) => {
|
{collaboratorList?.map((member) => {
|
||||||
return (
|
return (
|
||||||
<Tag key={member.tmbId} type={'fill'} colorSchema="white" {...tagStyle}>
|
<Tag
|
||||||
|
key={member.tmbId || member.groupId}
|
||||||
|
type={'fill'}
|
||||||
|
colorSchema="white"
|
||||||
|
{...tagStyle}
|
||||||
|
>
|
||||||
<Avatar src={member.avatar} w="1.25rem" />
|
<Avatar src={member.avatar} w="1.25rem" />
|
||||||
<Box fontSize={'sm'} ml={1}>
|
<Box fontSize={'sm'} ml={1}>
|
||||||
{member.name}
|
{member.name === DefaultGroupName ? userInfo?.team.teamName : member.name}
|
||||||
</Box>
|
</Box>
|
||||||
</Tag>
|
</Tag>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
|||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
|
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||||
const AddMemberModal = dynamic(() => import('./AddMemberModal'));
|
const AddMemberModal = dynamic(() => import('./AddMemberModal'));
|
||||||
const ManageModal = dynamic(() => import('./ManageModal'));
|
const ManageModal = dynamic(() => import('./ManageModal'));
|
||||||
|
|
||||||
@ -22,10 +23,12 @@ export type MemberManagerInputPropsType = {
|
|||||||
permission: Permission;
|
permission: Permission;
|
||||||
onGetCollaboratorList: () => Promise<CollaboratorItemType[]>;
|
onGetCollaboratorList: () => Promise<CollaboratorItemType[]>;
|
||||||
permissionList: PermissionListType;
|
permissionList: PermissionListType;
|
||||||
onUpdateCollaborators: (props: any) => any; // TODO: type. should be UpdatePermissionBody after app and dataset permission refactored
|
onUpdateCollaborators: (props: UpdateClbPermissionProps) => Promise<any>;
|
||||||
onDelOneCollaborator: (tmbId: string) => any;
|
onDelOneCollaborator: (props: RequireOnlyOne<{ tmbId: string; groupId: string }>) => Promise<any>;
|
||||||
refreshDeps?: any[];
|
refreshDeps?: any[];
|
||||||
|
mode?: 'member' | 'all';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type MemberManagerPropsType = MemberManagerInputPropsType & {
|
export type MemberManagerPropsType = MemberManagerInputPropsType & {
|
||||||
collaboratorList: CollaboratorItemType[];
|
collaboratorList: CollaboratorItemType[];
|
||||||
refetchCollaboratorList: () => void;
|
refetchCollaboratorList: () => void;
|
||||||
@ -72,7 +75,8 @@ const CollaboratorContextProvider = ({
|
|||||||
refetchResource,
|
refetchResource,
|
||||||
refreshDeps = [],
|
refreshDeps = [],
|
||||||
isInheritPermission,
|
isInheritPermission,
|
||||||
hasParent
|
hasParent,
|
||||||
|
mode = 'member'
|
||||||
}: MemberManagerInputPropsType & {
|
}: MemberManagerInputPropsType & {
|
||||||
children: (props: ChildrenProps) => ReactNode;
|
children: (props: ChildrenProps) => ReactNode;
|
||||||
refetchResource?: () => void;
|
refetchResource?: () => void;
|
||||||
@ -83,8 +87,10 @@ const CollaboratorContextProvider = ({
|
|||||||
await onUpdateCollaborators(props);
|
await onUpdateCollaborators(props);
|
||||||
refetchCollaboratorList();
|
refetchCollaboratorList();
|
||||||
};
|
};
|
||||||
const onDelOneCollaboratorThen = async (tmbId: string) => {
|
const onDelOneCollaboratorThen = async (
|
||||||
await onDelOneCollaborator(tmbId);
|
props: RequireOnlyOne<{ tmbId: string; groupId: string }>
|
||||||
|
) => {
|
||||||
|
await onDelOneCollaborator(props);
|
||||||
refetchCollaboratorList();
|
refetchCollaboratorList();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -197,6 +203,7 @@ const CollaboratorContextProvider = ({
|
|||||||
onCloseAddMember();
|
onCloseAddMember();
|
||||||
refetchResource?.();
|
refetchResource?.();
|
||||||
}}
|
}}
|
||||||
|
mode={mode}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isOpenManageModal && (
|
{isOpenManageModal && (
|
||||||
|
|||||||
@ -1,14 +1,5 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import {
|
import { Box, Checkbox, Flex, Grid, HStack } from '@chakra-ui/react';
|
||||||
Box,
|
|
||||||
Checkbox,
|
|
||||||
Flex,
|
|
||||||
Grid,
|
|
||||||
HStack,
|
|
||||||
Input,
|
|
||||||
InputGroup,
|
|
||||||
InputLeftElement
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
@ -16,6 +7,7 @@ import { Control, Controller } from 'react-hook-form';
|
|||||||
import { RequireAtLeastOne } from '@fastgpt/global/common/type/utils';
|
import { RequireAtLeastOne } from '@fastgpt/global/common/type/utils';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||||
|
import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
|
||||||
|
|
||||||
type memberType = {
|
type memberType = {
|
||||||
type: 'member';
|
type: 'member';
|
||||||
@ -120,19 +112,14 @@ function SelectMember({
|
|||||||
h={'100%'}
|
h={'100%'}
|
||||||
>
|
>
|
||||||
<Flex flexDirection="column" p="4" h={'100%'} overflow={'auto'}>
|
<Flex flexDirection="column" p="4" h={'100%'} overflow={'auto'}>
|
||||||
<InputGroup alignItems="center" size={'sm'}>
|
<SearchInput
|
||||||
<InputLeftElement>
|
placeholder={t('user:search_user')}
|
||||||
<MyIcon name="common/searchLight" w="16px" color={'myGray.500'} />
|
fontSize="sm"
|
||||||
</InputLeftElement>
|
bg={'myGray.50'}
|
||||||
<Input
|
onChange={(e) => {
|
||||||
placeholder={t('user:search_user')}
|
setSearchKey(e.target.value);
|
||||||
fontSize="sm"
|
}}
|
||||||
bg={'myGray.50'}
|
/>
|
||||||
onChange={(e) => {
|
|
||||||
setSearchKey(e.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</InputGroup>
|
|
||||||
<Flex flexDirection="column" mt={3}>
|
<Flex flexDirection="column" mt={3}>
|
||||||
{filtered.map((member) => {
|
{filtered.map((member) => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
1
projects/app/src/global/core/app/api.d.ts
vendored
1
projects/app/src/global/core/app/api.d.ts
vendored
@ -12,7 +12,6 @@ export type AppUpdateParams = {
|
|||||||
edges?: AppSchema['edges'];
|
edges?: AppSchema['edges'];
|
||||||
chatConfig?: AppSchema['chatConfig'];
|
chatConfig?: AppSchema['chatConfig'];
|
||||||
teamTags?: AppSchema['teamTags'];
|
teamTags?: AppSchema['teamTags'];
|
||||||
defaultPermission?: AppSchema['defaultPermission'];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PostPublishAppProps = {
|
export type PostPublishAppProps = {
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { MongoMemoryServer } from 'mongodb-memory-server';
|
import { MongoMemoryReplSet } from 'mongodb-memory-server';
|
||||||
import mongoose from 'mongoose';
|
import mongoose from 'mongoose';
|
||||||
import { MockParseHeaderCert } from '@/test/utils';
|
import { parseHeaderCertMock } from '@/test/utils';
|
||||||
import { initMockData } from './db/init';
|
import { initMockData, root } from './db/init';
|
||||||
|
import { faker } from '@faker-js/faker/locale/zh_CN';
|
||||||
|
|
||||||
jest.mock('nanoid', () => {
|
jest.mock('nanoid', () => {
|
||||||
return {
|
return {
|
||||||
@ -13,24 +14,40 @@ jest.mock('@fastgpt/global/common/string/tools', () => {
|
|||||||
return {
|
return {
|
||||||
hashStr(str: string) {
|
hashStr(str: string) {
|
||||||
return str;
|
return str;
|
||||||
|
},
|
||||||
|
getNanoid() {
|
||||||
|
return faker.string.alphanumeric(12);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
jest.mock('@fastgpt/service/common/system/log', jest.fn());
|
jest.mock('@fastgpt/service/common/system/log', () => ({
|
||||||
|
addLog: {
|
||||||
|
log: jest.fn(),
|
||||||
|
warn: jest.fn((...prop) => {
|
||||||
|
console.warn(prop);
|
||||||
|
}),
|
||||||
|
error: jest.fn((...prop) => {
|
||||||
|
console.error(prop);
|
||||||
|
}),
|
||||||
|
info: jest.fn(),
|
||||||
|
debug: jest.fn()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
jest.mock('@fastgpt/service/support/permission/controller', () => {
|
jest.setMock(
|
||||||
return {
|
'@fastgpt/service/support/permission/controller',
|
||||||
parseHeaderCert: MockParseHeaderCert,
|
(() => {
|
||||||
getResourcePermission: jest.requireActual('@fastgpt/service/support/permission/controller')
|
const origin = jest.requireActual<
|
||||||
.getResourcePermission,
|
typeof import('@fastgpt/service/support/permission/controller')
|
||||||
getResourceAllClbs: jest.requireActual('@fastgpt/service/support/permission/controller')
|
>('@fastgpt/service/support/permission/controller');
|
||||||
.getResourceAllClbs
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const parse = jest.createMockFromModule('@fastgpt/service/support/permission/controller') as any;
|
return {
|
||||||
parse.parseHeaderCert = MockParseHeaderCert;
|
...origin,
|
||||||
|
parseHeaderCert: parseHeaderCertMock
|
||||||
|
};
|
||||||
|
})()
|
||||||
|
);
|
||||||
|
|
||||||
jest.mock('@/service/middleware/entry', () => {
|
jest.mock('@/service/middleware/entry', () => {
|
||||||
return {
|
return {
|
||||||
@ -59,11 +76,30 @@ jest.mock('@/service/middleware/entry', () => {
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
// 新建一个内存数据库,然后让 mongoose 连接这个数据库
|
// 新建一个内存数据库,然后让 mongoose 连接这个数据库
|
||||||
if (!global.mongod || !global.mongodb) {
|
if (!global.mongod || !global.mongodb) {
|
||||||
const mongod = await MongoMemoryServer.create();
|
const replSet = new MongoMemoryReplSet({
|
||||||
global.mongod = mongod;
|
instanceOpts: [
|
||||||
|
{
|
||||||
|
storageEngine: 'wiredTiger'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
storageEngine: 'wiredTiger'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
replSet.start();
|
||||||
|
await replSet.waitUntilRunning();
|
||||||
|
const uri = replSet.getUri();
|
||||||
|
// const mongod = await MongoMemoryServer.create({
|
||||||
|
// instance: {
|
||||||
|
// replSet: 'testset'
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// global.mongod = mongod;
|
||||||
|
global.replSet = replSet;
|
||||||
global.mongodb = mongoose;
|
global.mongodb = mongoose;
|
||||||
|
|
||||||
await global.mongodb.connect(mongod.getUri(), {
|
await global.mongodb.connect(uri, {
|
||||||
|
dbName: 'fastgpt_test',
|
||||||
bufferCommands: true,
|
bufferCommands: true,
|
||||||
maxConnecting: 50,
|
maxConnecting: 50,
|
||||||
maxPoolSize: 50,
|
maxPoolSize: 50,
|
||||||
@ -77,6 +113,7 @@ beforeAll(async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await initMockData();
|
await initMockData();
|
||||||
|
console.log(root);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -84,6 +121,9 @@ afterAll(async () => {
|
|||||||
if (global.mongodb) {
|
if (global.mongodb) {
|
||||||
await global.mongodb.disconnect();
|
await global.mongodb.disconnect();
|
||||||
}
|
}
|
||||||
|
if (global.replSet) {
|
||||||
|
await global.replSet.stop();
|
||||||
|
}
|
||||||
if (global.mongod) {
|
if (global.mongod) {
|
||||||
await global.mongod.stop();
|
await global.mongod.stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||||
|
import { MongoMemberGroupModel } from '@fastgpt/service/support/permission/memberGroup/memberGroupSchema';
|
||||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
||||||
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
||||||
import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
|
import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
|
||||||
@ -13,37 +14,43 @@ export const root = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const initMockData = async () => {
|
export const initMockData = async () => {
|
||||||
const initRootUser = async () => {
|
const [rootUser] = await MongoUser.create([
|
||||||
// init root user
|
{
|
||||||
const rootUser = await MongoUser.create({
|
|
||||||
username: 'root',
|
username: 'root',
|
||||||
password: '123456'
|
password: '123456'
|
||||||
});
|
}
|
||||||
|
]);
|
||||||
const rootTeam = await MongoTeam.create({
|
root.uid = String(rootUser._id);
|
||||||
name: 'root-default-team',
|
const [rootTeam] = await MongoTeam.create([
|
||||||
ownerId: rootUser._id
|
{
|
||||||
});
|
name: 'root Team'
|
||||||
|
}
|
||||||
const rootTeamMember = await MongoTeamMember.create({
|
]);
|
||||||
|
root.teamId = String(rootTeam._id);
|
||||||
|
const [rootTmb] = await MongoTeamMember.create([
|
||||||
|
{
|
||||||
teamId: rootTeam._id,
|
teamId: rootTeam._id,
|
||||||
|
name: 'owner',
|
||||||
|
role: 'owner',
|
||||||
userId: rootUser._id,
|
userId: rootUser._id,
|
||||||
name: 'root-default-team-member',
|
status: 'active'
|
||||||
status: 'active',
|
}
|
||||||
role: TeamMemberRoleEnum.owner
|
]);
|
||||||
});
|
root.tmbId = String(rootTmb._id);
|
||||||
const rootApp = await MongoApp.create({
|
await MongoMemberGroupModel.create([
|
||||||
name: 'root-default-app',
|
{
|
||||||
|
name: DefaultGroupName,
|
||||||
|
teamId: rootTeam._id
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
const [rootApp] = await MongoApp.create([
|
||||||
|
{
|
||||||
|
name: 'root Test App',
|
||||||
teamId: rootTeam._id,
|
teamId: rootTeam._id,
|
||||||
tmbId: rootTeam._id,
|
tmbId: rootTmb._id
|
||||||
type: 'advanced'
|
}
|
||||||
});
|
]);
|
||||||
|
|
||||||
root.uid = rootUser._id;
|
root.appId = String(rootApp._id);
|
||||||
root.tmbId = rootTeamMember._id;
|
|
||||||
root.teamId = rootTeam._id;
|
|
||||||
root.appId = rootApp._id;
|
|
||||||
};
|
|
||||||
|
|
||||||
await initRootUser();
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,11 @@
|
|||||||
import { MongoMemoryServer } from 'mongodb-memory-server';
|
import type { MongoMemoryReplSet, MongoMemoryServer } from 'mongodb-memory-server';
|
||||||
declare global {
|
declare global {
|
||||||
var mongod: MongoMemoryServer | undefined;
|
var mongod: MongoMemoryServer | undefined;
|
||||||
|
var replSet: MongoMemoryReplSet | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RequestResponse<T = any> = {
|
||||||
|
code: number;
|
||||||
|
error?: string;
|
||||||
|
data?: T;
|
||||||
|
};
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
|||||||
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||||
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||||
import {
|
import {
|
||||||
|
OwnerPermissionVal,
|
||||||
PerResourceTypeEnum,
|
PerResourceTypeEnum,
|
||||||
WritePermissionVal
|
WritePermissionVal
|
||||||
} from '@fastgpt/global/support/permission/constant';
|
} from '@fastgpt/global/support/permission/constant';
|
||||||
@ -11,11 +12,12 @@ import { NextAPI } from '@/service/middleware/entry';
|
|||||||
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||||
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
||||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||||
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
|
|
||||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
import { syncCollaborators } from '@fastgpt/service/support/permission/inheritPermission';
|
import { syncCollaborators } from '@fastgpt/service/support/permission/inheritPermission';
|
||||||
import { getResourceAllClbs } from '@fastgpt/service/support/permission/controller';
|
import { getResourceClbsAndGroups } from '@fastgpt/service/support/permission/controller';
|
||||||
|
import { TeamWritePermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
||||||
|
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
|
||||||
|
|
||||||
export type CreateAppFolderBody = {
|
export type CreateAppFolderBody = {
|
||||||
parentId?: ParentIdType;
|
parentId?: ParentIdType;
|
||||||
@ -31,20 +33,21 @@ async function handler(req: ApiRequestProps<CreateAppFolderBody>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 凭证校验
|
// 凭证校验
|
||||||
const { teamId, tmbId } = await authUserPer({ req, authToken: true, per: WritePermissionVal });
|
const { teamId, tmbId } = await authUserPer({
|
||||||
const parentApp = await (async () => {
|
req,
|
||||||
if (parentId) {
|
authToken: true,
|
||||||
// if it is not a root folder
|
per: TeamWritePermissionVal
|
||||||
return (
|
});
|
||||||
await authApp({
|
|
||||||
req,
|
if (parentId) {
|
||||||
appId: parentId,
|
// if it is not a root folder
|
||||||
per: WritePermissionVal,
|
await authApp({
|
||||||
authToken: true
|
req,
|
||||||
})
|
appId: parentId,
|
||||||
).app; // check the parent folder permission
|
per: WritePermissionVal,
|
||||||
}
|
authToken: true
|
||||||
})();
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Create app
|
// Create app
|
||||||
await mongoSessionRun(async (session) => {
|
await mongoSessionRun(async (session) => {
|
||||||
@ -55,13 +58,11 @@ async function handler(req: ApiRequestProps<CreateAppFolderBody>) {
|
|||||||
intro,
|
intro,
|
||||||
teamId,
|
teamId,
|
||||||
tmbId,
|
tmbId,
|
||||||
type: AppTypeEnum.folder,
|
type: AppTypeEnum.folder
|
||||||
// inheritPermission: !!parentApp ? true : false,
|
|
||||||
defaultPermission: !!parentApp ? parentApp.defaultPermission : AppDefaultPermissionVal
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
const parentClbs = await getResourceAllClbs({
|
const parentClbsAndGroups = await getResourceClbsAndGroups({
|
||||||
teamId,
|
teamId,
|
||||||
resourceId: parentId,
|
resourceId: parentId,
|
||||||
resourceType: PerResourceTypeEnum.app,
|
resourceType: PerResourceTypeEnum.app,
|
||||||
@ -72,9 +73,25 @@ async function handler(req: ApiRequestProps<CreateAppFolderBody>) {
|
|||||||
resourceType: PerResourceTypeEnum.app,
|
resourceType: PerResourceTypeEnum.app,
|
||||||
teamId,
|
teamId,
|
||||||
resourceId: app._id,
|
resourceId: app._id,
|
||||||
collaborators: parentClbs,
|
collaborators: parentClbsAndGroups,
|
||||||
session
|
session
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// Create default permission
|
||||||
|
await MongoResourcePermission.create(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
resourceType: PerResourceTypeEnum.app,
|
||||||
|
teamId,
|
||||||
|
resourceId: app._id,
|
||||||
|
tmbId,
|
||||||
|
permission: OwnerPermissionVal
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{
|
||||||
|
session
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,8 @@ import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/
|
|||||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||||
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||||
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
|
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
|
||||||
|
import { getGroupPer } from '@fastgpt/service/support/permission/controller';
|
||||||
|
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
|
||||||
|
|
||||||
export type ListAppBody = {
|
export type ListAppBody = {
|
||||||
parentId?: ParentIdType;
|
parentId?: ParentIdType;
|
||||||
@ -31,7 +33,7 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
|
|||||||
app: ParentApp,
|
app: ParentApp,
|
||||||
tmbId,
|
tmbId,
|
||||||
teamId,
|
teamId,
|
||||||
permission: tmbPer
|
permission: myPer
|
||||||
} = await (async () => {
|
} = await (async () => {
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
return await authApp({
|
return await authApp({
|
||||||
@ -87,10 +89,17 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
/* temp: get all apps and per */
|
/* temp: get all apps and per */
|
||||||
const [myApps, rpList] = await Promise.all([
|
const myGroupIds = (
|
||||||
|
await getGroupsByTmbId({
|
||||||
|
tmbId,
|
||||||
|
teamId
|
||||||
|
})
|
||||||
|
).map((item) => String(item._id));
|
||||||
|
|
||||||
|
const [myApps, perList] = await Promise.all([
|
||||||
MongoApp.find(
|
MongoApp.find(
|
||||||
findAppsQuery,
|
findAppsQuery,
|
||||||
'_id parentId avatar type name intro tmbId updateTime pluginData defaultPermission inheritPermission'
|
'_id parentId avatar type name intro tmbId updateTime pluginData inheritPermission'
|
||||||
)
|
)
|
||||||
.sort({
|
.sort({
|
||||||
updateTime: -1
|
updateTime: -1
|
||||||
@ -98,41 +107,67 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
|
|||||||
.limit(searchKey ? 20 : 1000)
|
.limit(searchKey ? 20 : 1000)
|
||||||
.lean(),
|
.lean(),
|
||||||
MongoResourcePermission.find({
|
MongoResourcePermission.find({
|
||||||
resourceType: PerResourceTypeEnum.app,
|
$and: [
|
||||||
teamId,
|
{
|
||||||
tmbId
|
resourceType: PerResourceTypeEnum.app,
|
||||||
|
teamId,
|
||||||
|
resourceId: {
|
||||||
|
$exists: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ $or: [{ tmbId }, { groupId: { $in: myGroupIds } }] }
|
||||||
|
]
|
||||||
}).lean()
|
}).lean()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const filterApps = myApps
|
const filterApps = myApps
|
||||||
.map((app) => {
|
.map((app) => {
|
||||||
const Per = (() => {
|
const { Per, privateApp } = (() => {
|
||||||
// Inherit app
|
// Inherit app
|
||||||
if (app.inheritPermission && ParentApp && !AppFolderTypeList.includes(app.type)) {
|
if (app.inheritPermission && ParentApp && !AppFolderTypeList.includes(app.type)) {
|
||||||
// get its parent's permission as its permission
|
const tmbPer = perList.find(
|
||||||
app.defaultPermission = ParentApp.defaultPermission;
|
(item) => String(item.resourceId) === String(ParentApp._id) && !!item.tmbId
|
||||||
const perVal = rpList.find(
|
|
||||||
(item) => String(item.resourceId) === String(ParentApp._id)
|
|
||||||
)?.permission;
|
)?.permission;
|
||||||
|
const groupPer = getGroupPer(
|
||||||
|
perList
|
||||||
|
.filter(
|
||||||
|
(item) =>
|
||||||
|
String(item.resourceId) === String(ParentApp._id) &&
|
||||||
|
myGroupIds.includes(String(item.groupId))
|
||||||
|
)
|
||||||
|
.map((item) => item.permission)
|
||||||
|
);
|
||||||
|
|
||||||
return new AppPermission({
|
return {
|
||||||
per: perVal ?? app.defaultPermission,
|
Per: new AppPermission({
|
||||||
isOwner: String(app.tmbId) === String(tmbId) || tmbPer.isOwner
|
per: tmbPer ?? groupPer ?? AppDefaultPermissionVal,
|
||||||
});
|
isOwner: String(app.tmbId) === String(tmbId) || myPer.isOwner
|
||||||
|
}),
|
||||||
|
privateApp: !tmbPer && !groupPer
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
const perVal = rpList.find(
|
const tmbPer = perList.find(
|
||||||
(item) => String(item.resourceId) === String(app._id)
|
(item) => String(item.resourceId) === String(app._id) && !!item.tmbId
|
||||||
)?.permission;
|
)?.permission;
|
||||||
return new AppPermission({
|
const group = perList.filter(
|
||||||
per: perVal ?? app.defaultPermission,
|
(item) =>
|
||||||
isOwner: String(app.tmbId) === String(tmbId) || tmbPer.isOwner
|
String(item.resourceId) === String(app._id) &&
|
||||||
});
|
myGroupIds.includes(String(item.groupId))
|
||||||
|
);
|
||||||
|
const groupPer = getGroupPer(group.map((item) => item.permission));
|
||||||
|
return {
|
||||||
|
Per: new AppPermission({
|
||||||
|
per: tmbPer ?? groupPer ?? AppDefaultPermissionVal,
|
||||||
|
isOwner: String(app.tmbId) === String(tmbId) || myPer.isOwner
|
||||||
|
}),
|
||||||
|
privateApp: !tmbPer && !groupPer
|
||||||
|
};
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...app,
|
...app,
|
||||||
permission: Per
|
permission: Per,
|
||||||
|
privateApp: privateApp
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter((app) => app.permission.hasReadPer);
|
.filter((app) => app.permission.hasReadPer);
|
||||||
@ -148,9 +183,9 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
|
|||||||
intro: app.intro,
|
intro: app.intro,
|
||||||
updateTime: app.updateTime,
|
updateTime: app.updateTime,
|
||||||
permission: app.permission,
|
permission: app.permission,
|
||||||
defaultPermission: app.defaultPermission || AppDefaultPermissionVal,
|
|
||||||
pluginData: app.pluginData,
|
pluginData: app.pluginData,
|
||||||
inheritPermission: app.inheritPermission ?? true
|
inheritPermission: app.inheritPermission ?? true,
|
||||||
|
private: app.privateApp
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { NextAPI } from '@/service/middleware/entry';
|
|||||||
import {
|
import {
|
||||||
ManagePermissionVal,
|
ManagePermissionVal,
|
||||||
PerResourceTypeEnum,
|
PerResourceTypeEnum,
|
||||||
|
ReadPermissionVal,
|
||||||
WritePermissionVal
|
WritePermissionVal
|
||||||
} from '@fastgpt/global/support/permission/constant';
|
} from '@fastgpt/global/support/permission/constant';
|
||||||
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
||||||
@ -18,62 +19,78 @@ import {
|
|||||||
import { AppFolderTypeList } from '@fastgpt/global/core/app/constants';
|
import { AppFolderTypeList } from '@fastgpt/global/core/app/constants';
|
||||||
import { ClientSession } from 'mongoose';
|
import { ClientSession } from 'mongoose';
|
||||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
import { getResourceClbsAndGroups } from '@fastgpt/service/support/permission/controller';
|
||||||
import { getResourceAllClbs } from '@fastgpt/service/support/permission/controller';
|
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||||
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
|
import { TeamWritePermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
||||||
|
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||||
|
|
||||||
/*
|
export type AppUpdateQuery = {
|
||||||
修改默认权限
|
appId: string;
|
||||||
1. 继承态目录:关闭继承态,修改权限,同步子目录默认权限
|
};
|
||||||
2. 继承态资源:关闭继承态,修改权限, 复制父级协作者。
|
|
||||||
3. 非继承目录:修改权限,同步子目录默认权限
|
|
||||||
4. 非继承资源:修改权限
|
|
||||||
|
|
||||||
移动
|
export type AppUpdateBody = AppUpdateParams;
|
||||||
1. 继承态目录:改 parentId, 修改成父的默认权限,同步子目录默认权限和协作者
|
|
||||||
2. 继承态资源:改 parentId
|
|
||||||
3. 非继承:改 parentId
|
|
||||||
*/
|
|
||||||
|
|
||||||
async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>) {
|
// 更新应用接口
|
||||||
const {
|
// 包括如下功能:
|
||||||
parentId,
|
// 1. 更新应用的信息(包括名称,类型,头像,介绍等)
|
||||||
name,
|
// 2. 更新应用的编排信息
|
||||||
avatar,
|
// 3. 移动应用
|
||||||
type,
|
// 操作权限:
|
||||||
intro,
|
// 1. 更新信息和工作流编排需要有应用的写权限
|
||||||
nodes,
|
// 2. 移动应用需要有
|
||||||
edges,
|
// (1) 父目录的管理权限
|
||||||
chatConfig,
|
// (2) 目标目录的管理权限
|
||||||
teamTags,
|
// (3) 如果从根目录移动或移动到根目录,需要有团队的应用创建权限
|
||||||
defaultPermission
|
async function handler(req: ApiRequestProps<AppUpdateBody, AppUpdateQuery>) {
|
||||||
} = req.body as AppUpdateParams;
|
const { parentId, name, avatar, type, intro, nodes, edges, chatConfig, teamTags } = req.body;
|
||||||
|
|
||||||
const { appId } = req.query as { appId: string };
|
const { appId } = req.query;
|
||||||
|
|
||||||
if (!appId) {
|
if (!appId) {
|
||||||
Promise.reject(CommonErrEnum.missingParams);
|
Promise.reject(CommonErrEnum.missingParams);
|
||||||
}
|
}
|
||||||
|
const isMove = parentId !== undefined;
|
||||||
|
|
||||||
const { app } = await (async () => {
|
// this step is to get the app and its permission, and we will check the permission manually for
|
||||||
if (defaultPermission !== undefined) {
|
// different cases
|
||||||
// if defaultPermission or inheritPermission is set, then need manage permission
|
const { app, permission } = await authApp({
|
||||||
return authApp({ req, authToken: true, appId, per: ManagePermissionVal });
|
req,
|
||||||
} else {
|
authToken: true,
|
||||||
return authApp({ req, authToken: true, appId, per: WritePermissionVal });
|
appId,
|
||||||
|
per: ReadPermissionVal
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!app) {
|
||||||
|
Promise.reject(AppErrEnum.unExist);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isMove) {
|
||||||
|
if (parentId) {
|
||||||
|
// move to a folder, check the target folder's permission
|
||||||
|
await authApp({ req, authToken: true, appId: parentId, per: ManagePermissionVal });
|
||||||
}
|
}
|
||||||
})();
|
if (app.parentId) {
|
||||||
|
// move from a folder, check the (old) folder's permission
|
||||||
|
await authApp({ req, authToken: true, appId: app.parentId, per: ManagePermissionVal });
|
||||||
|
}
|
||||||
|
if (parentId === null || !app.parentId) {
|
||||||
|
// move to root or move from root
|
||||||
|
await authUserPer({
|
||||||
|
req,
|
||||||
|
authToken: true,
|
||||||
|
per: TeamWritePermissionVal
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// is not move, write permission of the app.
|
||||||
|
if (!permission.hasWritePer) {
|
||||||
|
return Promise.reject(AppErrEnum.unAuthApp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// format nodes data
|
const onUpdate = async (session?: ClientSession) => {
|
||||||
// 1. dataset search limit, less than model quoteMaxToken
|
// format nodes data
|
||||||
const isDefaultPermissionChanged =
|
// 1. dataset search limit, less than model quoteMaxToken
|
||||||
defaultPermission !== undefined && defaultPermission !== app.defaultPermission;
|
|
||||||
const isFolder = AppFolderTypeList.includes(app.type);
|
|
||||||
|
|
||||||
const onUpdate = async (
|
|
||||||
session?: ClientSession,
|
|
||||||
updatedDefaultPermission?: PermissionValueType
|
|
||||||
) => {
|
|
||||||
const { nodes: formatNodes } = beforeUpdateAppFormat({ nodes });
|
const { nodes: formatNodes } = beforeUpdateAppFormat({ nodes });
|
||||||
|
|
||||||
return MongoApp.findByIdAndUpdate(
|
return MongoApp.findByIdAndUpdate(
|
||||||
@ -84,12 +101,6 @@ async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>)
|
|||||||
...(type && { type }),
|
...(type && { type }),
|
||||||
...(avatar && { avatar }),
|
...(avatar && { avatar }),
|
||||||
...(intro !== undefined && { intro }),
|
...(intro !== undefined && { intro }),
|
||||||
// update default permission(Maybe move update)
|
|
||||||
...(updatedDefaultPermission !== undefined && {
|
|
||||||
defaultPermission: updatedDefaultPermission
|
|
||||||
}),
|
|
||||||
// Not root, update default permission
|
|
||||||
...(app.parentId && isDefaultPermissionChanged && { inheritPermission: false }),
|
|
||||||
...(teamTags && { teamTags }),
|
...(teamTags && { teamTags }),
|
||||||
...(formatNodes && {
|
...(formatNodes && {
|
||||||
modules: formatNodes
|
modules: formatNodes
|
||||||
@ -97,34 +108,19 @@ async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>)
|
|||||||
...(edges && {
|
...(edges && {
|
||||||
edges
|
edges
|
||||||
}),
|
}),
|
||||||
...(chatConfig && { chatConfig })
|
...(chatConfig && { chatConfig }),
|
||||||
|
...(isMove && { inheritPermission: true })
|
||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Move
|
// Move
|
||||||
if (parentId !== undefined) {
|
if (isMove) {
|
||||||
await mongoSessionRun(async (session) => {
|
await mongoSessionRun(async (session) => {
|
||||||
// Auth
|
|
||||||
const parentDefaultPermission = await (async () => {
|
|
||||||
if (parentId) {
|
|
||||||
const { app: parentApp } = await authApp({
|
|
||||||
req,
|
|
||||||
authToken: true,
|
|
||||||
appId: parentId,
|
|
||||||
per: WritePermissionVal
|
|
||||||
});
|
|
||||||
|
|
||||||
return parentApp.defaultPermission;
|
|
||||||
}
|
|
||||||
|
|
||||||
return AppDefaultPermissionVal;
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Inherit folder: Sync children permission and it's clbs
|
// Inherit folder: Sync children permission and it's clbs
|
||||||
if (isFolder && app.inheritPermission) {
|
if (AppFolderTypeList.includes(app.type)) {
|
||||||
const parentClbs = await getResourceAllClbs({
|
const parentClbsAndGroups = await getResourceClbsAndGroups({
|
||||||
teamId: app.teamId,
|
teamId: app.teamId,
|
||||||
resourceId: parentId,
|
resourceId: parentId,
|
||||||
resourceType: PerResourceTypeEnum.app,
|
resourceType: PerResourceTypeEnum.app,
|
||||||
@ -134,7 +130,7 @@ async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>)
|
|||||||
await syncCollaborators({
|
await syncCollaborators({
|
||||||
resourceId: app._id,
|
resourceId: app._id,
|
||||||
resourceType: PerResourceTypeEnum.app,
|
resourceType: PerResourceTypeEnum.app,
|
||||||
collaborators: parentClbs,
|
collaborators: parentClbsAndGroups,
|
||||||
session,
|
session,
|
||||||
teamId: app.teamId
|
teamId: app.teamId
|
||||||
});
|
});
|
||||||
@ -144,53 +140,12 @@ async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>)
|
|||||||
resourceType: PerResourceTypeEnum.app,
|
resourceType: PerResourceTypeEnum.app,
|
||||||
resourceModel: MongoApp,
|
resourceModel: MongoApp,
|
||||||
folderTypeList: AppFolderTypeList,
|
folderTypeList: AppFolderTypeList,
|
||||||
defaultPermission: parentDefaultPermission,
|
collaborators: parentClbsAndGroups,
|
||||||
collaborators: parentClbs,
|
|
||||||
session
|
session
|
||||||
});
|
});
|
||||||
|
|
||||||
return onUpdate(session, parentDefaultPermission);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return onUpdate(session);
|
return onUpdate(session);
|
||||||
});
|
});
|
||||||
} else if (isDefaultPermissionChanged) {
|
|
||||||
// Update default permission
|
|
||||||
await mongoSessionRun(async (session) => {
|
|
||||||
if (isFolder) {
|
|
||||||
// Sync children default permission
|
|
||||||
await syncChildrenPermission({
|
|
||||||
resource: {
|
|
||||||
_id: app._id,
|
|
||||||
type: app.type,
|
|
||||||
teamId: app.teamId,
|
|
||||||
parentId: app.parentId
|
|
||||||
},
|
|
||||||
folderTypeList: AppFolderTypeList,
|
|
||||||
resourceModel: MongoApp,
|
|
||||||
resourceType: PerResourceTypeEnum.app,
|
|
||||||
session,
|
|
||||||
defaultPermission
|
|
||||||
});
|
|
||||||
} else if (app.inheritPermission && app.parentId) {
|
|
||||||
// Inherit app
|
|
||||||
const parentClbs = await getResourceAllClbs({
|
|
||||||
teamId: app.teamId,
|
|
||||||
resourceId: app.parentId,
|
|
||||||
resourceType: PerResourceTypeEnum.app,
|
|
||||||
session
|
|
||||||
});
|
|
||||||
await syncCollaborators({
|
|
||||||
resourceId: app._id,
|
|
||||||
resourceType: PerResourceTypeEnum.app,
|
|
||||||
collaborators: parentClbs,
|
|
||||||
session,
|
|
||||||
teamId: app.teamId
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return onUpdate(session, defaultPermission);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
return onUpdate();
|
return onUpdate();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import type { NextApiRequest } from 'next';
|
|||||||
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
||||||
import { getVectorModel } from '@fastgpt/service/core/ai/model';
|
import { getVectorModel } from '@fastgpt/service/core/ai/model';
|
||||||
import type { DatasetSimpleItemType } from '@fastgpt/global/core/dataset/type.d';
|
import type { DatasetSimpleItemType } from '@fastgpt/global/core/dataset/type.d';
|
||||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
|
||||||
import { NextAPI } from '@/service/middleware/entry';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
import {
|
import {
|
||||||
PerResourceTypeEnum,
|
PerResourceTypeEnum,
|
||||||
@ -11,6 +10,9 @@ import {
|
|||||||
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
|
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
|
||||||
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
|
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
|
||||||
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||||
|
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
||||||
|
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
|
||||||
|
import { getGroupPer } from '@fastgpt/service/support/permission/controller';
|
||||||
|
|
||||||
/* get all dataset by teamId or tmbId */
|
/* get all dataset by teamId or tmbId */
|
||||||
async function handler(req: NextApiRequest): Promise<DatasetSimpleItemType[]> {
|
async function handler(req: NextApiRequest): Promise<DatasetSimpleItemType[]> {
|
||||||
@ -25,7 +27,14 @@ async function handler(req: NextApiRequest): Promise<DatasetSimpleItemType[]> {
|
|||||||
per: ReadPermissionVal
|
per: ReadPermissionVal
|
||||||
});
|
});
|
||||||
|
|
||||||
const [myDatasets, rpList] = await Promise.all([
|
const myGroupIds = (
|
||||||
|
await getGroupsByTmbId({
|
||||||
|
tmbId,
|
||||||
|
teamId
|
||||||
|
})
|
||||||
|
).map((item) => String(item._id));
|
||||||
|
|
||||||
|
const [myDatasets, perList] = await Promise.all([
|
||||||
MongoDataset.find({
|
MongoDataset.find({
|
||||||
teamId
|
teamId
|
||||||
})
|
})
|
||||||
@ -34,39 +43,59 @@ async function handler(req: NextApiRequest): Promise<DatasetSimpleItemType[]> {
|
|||||||
})
|
})
|
||||||
.lean(),
|
.lean(),
|
||||||
MongoResourcePermission.find({
|
MongoResourcePermission.find({
|
||||||
resourceType: PerResourceTypeEnum.dataset,
|
$and: [
|
||||||
teamId,
|
{
|
||||||
tmbId
|
resourceType: PerResourceTypeEnum.dataset,
|
||||||
|
teamId,
|
||||||
|
resourceId: {
|
||||||
|
$exists: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ $or: [{ tmbId }, { groupId: { $in: myGroupIds } }] }
|
||||||
|
]
|
||||||
}).lean()
|
}).lean()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const filterDatasets = myDatasets
|
const filterDatasets = myDatasets
|
||||||
.map((dataset) => {
|
.map((dataset) => {
|
||||||
const perVal = (() => {
|
const perVal = (() => {
|
||||||
const perVal = rpList.find(
|
const parentDataset = myDatasets.find(
|
||||||
(item) => String(item.resourceId) === String(dataset._id)
|
(item) => String(item._id) === String(dataset.parentId)
|
||||||
)?.permission;
|
);
|
||||||
if (perVal) {
|
|
||||||
return perVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataset.inheritPermission && dataset.parentId) {
|
if (dataset.inheritPermission && dataset.parentId && parentDataset) {
|
||||||
const parentDataset = myDatasets.find(
|
const tmbPer = perList.find(
|
||||||
(item) => String(item._id) === String(dataset.parentId)
|
(item) => String(item.resourceId) === String(parentDataset._id) && !!item.tmbId
|
||||||
|
)?.permission;
|
||||||
|
const groupPer = getGroupPer(
|
||||||
|
perList
|
||||||
|
.filter(
|
||||||
|
(item) =>
|
||||||
|
String(item.resourceId) === String(parentDataset._id) &&
|
||||||
|
myGroupIds.includes(String(item.groupId))
|
||||||
|
)
|
||||||
|
.map((item) => item.permission)
|
||||||
);
|
);
|
||||||
if (parentDataset) {
|
return tmbPer ?? groupPer ?? DatasetDefaultPermissionVal;
|
||||||
const parentPerVal =
|
} else {
|
||||||
rpList.find((item) => String(item.resourceId) === String(parentDataset._id))
|
const tmbPer = perList.find(
|
||||||
?.permission ?? parentDataset.defaultPermission;
|
(item) => String(item.resourceId) === String(dataset._id) && !!item.tmbId
|
||||||
if (parentPerVal) {
|
)?.permission;
|
||||||
return parentPerVal;
|
const groupPer = getGroupPer(
|
||||||
}
|
perList
|
||||||
}
|
.filter(
|
||||||
|
(item) =>
|
||||||
|
String(item.resourceId) === String(dataset._id) &&
|
||||||
|
myGroupIds.includes(String(item.groupId))
|
||||||
|
)
|
||||||
|
.map((item) => item.permission)
|
||||||
|
);
|
||||||
|
return tmbPer ?? groupPer ?? DatasetDefaultPermissionVal;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const Per = new DatasetPermission({
|
const Per = new DatasetPermission({
|
||||||
per: perVal ?? dataset.defaultPermission,
|
per: perVal ?? DatasetDefaultPermissionVal,
|
||||||
isOwner: String(dataset.tmbId) === tmbId || tmbPer.isOwner
|
isOwner: String(dataset.tmbId) === tmbId || tmbPer.isOwner
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
|||||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||||
import {
|
import {
|
||||||
|
OwnerPermissionVal,
|
||||||
PerResourceTypeEnum,
|
PerResourceTypeEnum,
|
||||||
WritePermissionVal
|
WritePermissionVal
|
||||||
} from '@fastgpt/global/support/permission/constant';
|
} from '@fastgpt/global/support/permission/constant';
|
||||||
@ -12,9 +13,9 @@ import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
|||||||
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
||||||
import { FolderImgUrl } from '@fastgpt/global/common/file/image/constants';
|
import { FolderImgUrl } from '@fastgpt/global/common/file/image/constants';
|
||||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||||
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
import { getResourceClbsAndGroups } from '@fastgpt/service/support/permission/controller';
|
||||||
import { getResourceAllClbs } from '@fastgpt/service/support/permission/controller';
|
|
||||||
import { syncCollaborators } from '@fastgpt/service/support/permission/inheritPermission';
|
import { syncCollaborators } from '@fastgpt/service/support/permission/inheritPermission';
|
||||||
|
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
|
||||||
export type DatasetFolderCreateQuery = {};
|
export type DatasetFolderCreateQuery = {};
|
||||||
export type DatasetFolderCreateBody = {
|
export type DatasetFolderCreateBody = {
|
||||||
parentId?: string;
|
parentId?: string;
|
||||||
@ -38,35 +39,28 @@ async function handler(
|
|||||||
authToken: true
|
authToken: true
|
||||||
});
|
});
|
||||||
|
|
||||||
const parentFolder = await (async () => {
|
if (parentId) {
|
||||||
if (parentId) {
|
await authDataset({
|
||||||
return (
|
datasetId: parentId,
|
||||||
await authDataset({
|
per: WritePermissionVal,
|
||||||
datasetId: parentId,
|
req,
|
||||||
per: WritePermissionVal,
|
authToken: true
|
||||||
req,
|
});
|
||||||
authToken: true
|
}
|
||||||
})
|
|
||||||
).dataset;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
await mongoSessionRun(async (session) => {
|
await mongoSessionRun(async (session) => {
|
||||||
const app = await MongoDataset.create({
|
const dataset = await MongoDataset.create({
|
||||||
...parseParentIdInMongo(parentId),
|
...parseParentIdInMongo(parentId),
|
||||||
avatar: FolderImgUrl,
|
avatar: FolderImgUrl,
|
||||||
name,
|
name,
|
||||||
intro,
|
intro,
|
||||||
teamId,
|
teamId,
|
||||||
tmbId,
|
tmbId,
|
||||||
type: DatasetTypeEnum.folder,
|
type: DatasetTypeEnum.folder
|
||||||
defaultPermission: !!parentFolder
|
|
||||||
? parentFolder.defaultPermission
|
|
||||||
: DatasetDefaultPermissionVal
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
const parentClbs = await getResourceAllClbs({
|
const parentClbsAndGroups = await getResourceClbsAndGroups({
|
||||||
teamId,
|
teamId,
|
||||||
resourceId: parentId,
|
resourceId: parentId,
|
||||||
resourceType: PerResourceTypeEnum.dataset,
|
resourceType: PerResourceTypeEnum.dataset,
|
||||||
@ -76,11 +70,26 @@ async function handler(
|
|||||||
await syncCollaborators({
|
await syncCollaborators({
|
||||||
resourceType: PerResourceTypeEnum.dataset,
|
resourceType: PerResourceTypeEnum.dataset,
|
||||||
teamId,
|
teamId,
|
||||||
resourceId: app._id,
|
resourceId: dataset._id,
|
||||||
collaborators: parentClbs,
|
collaborators: parentClbsAndGroups,
|
||||||
session
|
session
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!parentId) {
|
||||||
|
await MongoResourcePermission.create(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
resourceType: PerResourceTypeEnum.dataset,
|
||||||
|
teamId,
|
||||||
|
resourceId: dataset._id,
|
||||||
|
tmbId,
|
||||||
|
permission: OwnerPermissionVal
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{ session }
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@ -16,6 +16,8 @@ import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils'
|
|||||||
import { ApiRequestProps } from '@fastgpt/service/type/next';
|
import { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||||
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
|
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
|
||||||
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
|
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
|
||||||
|
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
|
||||||
|
import { getGroupPer } from '@fastgpt/service/support/permission/controller';
|
||||||
|
|
||||||
export type GetDatasetListBody = {
|
export type GetDatasetListBody = {
|
||||||
parentId: ParentIdType;
|
parentId: ParentIdType;
|
||||||
@ -30,7 +32,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
|
|||||||
dataset: parentDataset,
|
dataset: parentDataset,
|
||||||
teamId,
|
teamId,
|
||||||
tmbId,
|
tmbId,
|
||||||
permission: tmbPer
|
permission: myPer
|
||||||
} = await (async () => {
|
} = await (async () => {
|
||||||
if (parentId) {
|
if (parentId) {
|
||||||
return await authDataset({
|
return await authDataset({
|
||||||
@ -76,44 +78,84 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
|
|||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const [myDatasets, rpList] = await Promise.all([
|
const myGroupIds = (
|
||||||
|
await getGroupsByTmbId({
|
||||||
|
tmbId,
|
||||||
|
teamId
|
||||||
|
})
|
||||||
|
).map((item) => String(item._id));
|
||||||
|
|
||||||
|
const [myDatasets, perList] = await Promise.all([
|
||||||
MongoDataset.find(findDatasetQuery)
|
MongoDataset.find(findDatasetQuery)
|
||||||
.sort({
|
.sort({
|
||||||
updateTime: -1
|
updateTime: -1
|
||||||
})
|
})
|
||||||
.lean(),
|
.lean(),
|
||||||
MongoResourcePermission.find({
|
MongoResourcePermission.find({
|
||||||
resourceType: PerResourceTypeEnum.dataset,
|
$and: [
|
||||||
teamId,
|
{
|
||||||
tmbId
|
resourceType: PerResourceTypeEnum.dataset,
|
||||||
|
teamId,
|
||||||
|
resourceId: {
|
||||||
|
$exists: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ $or: [{ tmbId }, { groupId: { $in: myGroupIds } }] }
|
||||||
|
]
|
||||||
}).lean()
|
}).lean()
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const filterDatasets = myDatasets
|
const filterDatasets = myDatasets
|
||||||
.map((dataset) => {
|
.map((dataset) => {
|
||||||
const Per = (() => {
|
const { Per, privateDataset } = (() => {
|
||||||
|
// inherit
|
||||||
if (dataset.inheritPermission && parentDataset && dataset.type !== DatasetTypeEnum.folder) {
|
if (dataset.inheritPermission && parentDataset && dataset.type !== DatasetTypeEnum.folder) {
|
||||||
dataset.defaultPermission = parentDataset.defaultPermission;
|
const tmbPer = perList.find(
|
||||||
const perVal = rpList.find(
|
(item) => String(item.resourceId) === String(parentDataset._id) && !!item.tmbId
|
||||||
(item) => String(item.resourceId) === String(parentDataset._id)
|
|
||||||
)?.permission;
|
)?.permission;
|
||||||
return new DatasetPermission({
|
const groupPer = getGroupPer(
|
||||||
per: perVal ?? parentDataset.defaultPermission,
|
perList
|
||||||
isOwner: String(parentDataset.tmbId) === tmbId || tmbPer.isOwner
|
.filter(
|
||||||
});
|
(item) =>
|
||||||
|
String(item.resourceId) === String(parentDataset._id) &&
|
||||||
|
myGroupIds.includes(String(item.groupId))
|
||||||
|
)
|
||||||
|
.map((item) => item.permission)
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
Per: new DatasetPermission({
|
||||||
|
per: tmbPer ?? groupPer ?? DatasetDefaultPermissionVal,
|
||||||
|
isOwner: String(parentDataset.tmbId) === tmbId || myPer.isOwner
|
||||||
|
}),
|
||||||
|
privateDataset: !tmbPer && !groupPer
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
const perVal = rpList.find(
|
const tmbPer = perList.find(
|
||||||
(item) => String(item.resourceId) === String(dataset._id)
|
(item) =>
|
||||||
|
String(item.resourceId) === String(dataset._id) && !!item.tmbId && !!item.permission
|
||||||
)?.permission;
|
)?.permission;
|
||||||
return new DatasetPermission({
|
const groupPer = getGroupPer(
|
||||||
per: perVal ?? dataset.defaultPermission,
|
perList
|
||||||
isOwner: String(dataset.tmbId) === tmbId || tmbPer.isOwner
|
.filter(
|
||||||
});
|
(item) =>
|
||||||
|
String(item.resourceId) === String(dataset._id) &&
|
||||||
|
myGroupIds.includes(String(item.groupId))
|
||||||
|
)
|
||||||
|
.map((item) => item.permission)
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
Per: new DatasetPermission({
|
||||||
|
per: tmbPer ?? groupPer ?? DatasetDefaultPermissionVal,
|
||||||
|
isOwner: String(dataset.tmbId) === tmbId || myPer.isOwner
|
||||||
|
}),
|
||||||
|
privateDataset: !tmbPer && !groupPer
|
||||||
|
};
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
return {
|
return {
|
||||||
...dataset,
|
...dataset,
|
||||||
permission: Per
|
permission: Per,
|
||||||
|
privateDataset
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.filter((app) => app.permission.hasReadPer);
|
.filter((app) => app.permission.hasReadPer);
|
||||||
@ -127,10 +169,10 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
|
|||||||
type: item.type,
|
type: item.type,
|
||||||
permission: item.permission,
|
permission: item.permission,
|
||||||
vectorModel: getVectorModel(item.vectorModel),
|
vectorModel: getVectorModel(item.vectorModel),
|
||||||
defaultPermission: item.defaultPermission ?? DatasetDefaultPermissionVal,
|
|
||||||
inheritPermission: item.inheritPermission,
|
inheritPermission: item.inheritPermission,
|
||||||
tmbId: item.tmbId,
|
tmbId: item.tmbId,
|
||||||
updateTime: item.updateTime
|
updateTime: item.updateTime,
|
||||||
|
private: item.privateDataset
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@ -5,63 +5,86 @@ import { NextAPI } from '@/service/middleware/entry';
|
|||||||
import {
|
import {
|
||||||
ManagePermissionVal,
|
ManagePermissionVal,
|
||||||
PerResourceTypeEnum,
|
PerResourceTypeEnum,
|
||||||
WritePermissionVal
|
ReadPermissionVal
|
||||||
} from '@fastgpt/global/support/permission/constant';
|
} from '@fastgpt/global/support/permission/constant';
|
||||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||||
import { ClientSession } from 'mongoose';
|
import { ClientSession } from 'mongoose';
|
||||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
|
||||||
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
||||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||||
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
import { getResourceClbsAndGroups } from '@fastgpt/service/support/permission/controller';
|
||||||
import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type';
|
|
||||||
import { getResourceAllClbs } from '@fastgpt/service/support/permission/controller';
|
|
||||||
import {
|
import {
|
||||||
syncChildrenPermission,
|
syncChildrenPermission,
|
||||||
syncCollaborators
|
syncCollaborators
|
||||||
} from '@fastgpt/service/support/permission/inheritPermission';
|
} from '@fastgpt/service/support/permission/inheritPermission';
|
||||||
|
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||||
|
import { TeamWritePermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
||||||
|
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
|
||||||
|
|
||||||
export type DatasetUpdateQuery = {};
|
export type DatasetUpdateQuery = {};
|
||||||
export type DatasetUpdateResponse = any;
|
export type DatasetUpdateResponse = any;
|
||||||
|
|
||||||
|
// 更新知识库接口
|
||||||
|
// 包括如下功能:
|
||||||
|
// 1. 更新应用的信息(包括名称,类型,头像,介绍等)
|
||||||
|
// 2. 更新数据库的配置信息
|
||||||
|
// 3. 移动知识库
|
||||||
|
// 操作权限:
|
||||||
|
// 1. 更新信息和配置编排需要有知识库的写权限
|
||||||
|
// 2. 移动应用需要有
|
||||||
|
// (1) 父目录的管理权限
|
||||||
|
// (2) 目标目录的管理权限
|
||||||
|
// (3) 如果从根目录移动或移动到根目录,需要有团队的应用创建权限
|
||||||
async function handler(
|
async function handler(
|
||||||
req: ApiRequestProps<DatasetUpdateBody, DatasetUpdateQuery>,
|
req: ApiRequestProps<DatasetUpdateBody, DatasetUpdateQuery>,
|
||||||
_res: ApiResponseType<any>
|
_res: ApiResponseType<any>
|
||||||
): Promise<DatasetUpdateResponse> {
|
): Promise<DatasetUpdateResponse> {
|
||||||
const {
|
const { id, parentId, name, avatar, intro, agentModel, websiteConfig, externalReadUrl, status } =
|
||||||
id,
|
req.body;
|
||||||
parentId,
|
|
||||||
name,
|
|
||||||
avatar,
|
|
||||||
intro,
|
|
||||||
agentModel,
|
|
||||||
websiteConfig,
|
|
||||||
externalReadUrl,
|
|
||||||
defaultPermission,
|
|
||||||
status
|
|
||||||
} = req.body;
|
|
||||||
|
|
||||||
if (!id) {
|
if (!id) {
|
||||||
return Promise.reject(CommonErrEnum.missingParams);
|
return Promise.reject(CommonErrEnum.missingParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { dataset } = (await (async () => {
|
const isMove = parentId !== undefined;
|
||||||
if (defaultPermission !== undefined) {
|
|
||||||
return await authDataset({ req, authToken: true, datasetId: id, per: ManagePermissionVal });
|
const { dataset, permission } = await authDataset({
|
||||||
} else {
|
req,
|
||||||
return await authDataset({ req, authToken: true, datasetId: id, per: WritePermissionVal });
|
authToken: true,
|
||||||
}
|
datasetId: id,
|
||||||
})()) as { dataset: DatasetSchemaType };
|
per: ReadPermissionVal
|
||||||
|
});
|
||||||
|
if (isMove) {
|
||||||
|
if (parentId) {
|
||||||
|
// move to a folder, check the target folder's permission
|
||||||
|
await authDataset({ req, authToken: true, datasetId: parentId, per: ManagePermissionVal });
|
||||||
|
}
|
||||||
|
if (dataset.parentId) {
|
||||||
|
// move from a folder, check the (old) folder's permission
|
||||||
|
await authDataset({
|
||||||
|
req,
|
||||||
|
authToken: true,
|
||||||
|
datasetId: dataset.parentId,
|
||||||
|
per: ManagePermissionVal
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (parentId === null || !dataset.parentId) {
|
||||||
|
// move to root or move from root
|
||||||
|
await authUserPer({
|
||||||
|
req,
|
||||||
|
authToken: true,
|
||||||
|
per: TeamWritePermissionVal
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// is not move
|
||||||
|
if (!permission.hasWritePer) return Promise.reject(DatasetErrEnum.unAuthDataset);
|
||||||
|
}
|
||||||
|
|
||||||
const isDefaultPermissionChanged =
|
|
||||||
defaultPermission !== undefined && dataset.defaultPermission !== defaultPermission;
|
|
||||||
const isFolder = dataset.type === DatasetTypeEnum.folder;
|
const isFolder = dataset.type === DatasetTypeEnum.folder;
|
||||||
|
|
||||||
const onUpdate = async (
|
const onUpdate = async (session?: ClientSession) => {
|
||||||
session?: ClientSession,
|
|
||||||
updatedDefaultPermission?: PermissionValueType
|
|
||||||
) => {
|
|
||||||
await MongoDataset.findByIdAndUpdate(
|
await MongoDataset.findByIdAndUpdate(
|
||||||
id,
|
id,
|
||||||
{
|
{
|
||||||
@ -73,35 +96,16 @@ async function handler(
|
|||||||
...(status && { status }),
|
...(status && { status }),
|
||||||
...(intro !== undefined && { intro }),
|
...(intro !== undefined && { intro }),
|
||||||
...(externalReadUrl !== undefined && { externalReadUrl }),
|
...(externalReadUrl !== undefined && { externalReadUrl }),
|
||||||
// move
|
...(isMove && { inheritPermission: true })
|
||||||
...(updatedDefaultPermission !== undefined && {
|
|
||||||
defaultPermission: updatedDefaultPermission
|
|
||||||
}),
|
|
||||||
// update the defaultPermission
|
|
||||||
...(dataset.parentId && isDefaultPermissionChanged && { inheritPermission: false })
|
|
||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// move
|
if (isMove) {
|
||||||
if (parentId !== undefined) {
|
|
||||||
await mongoSessionRun(async (session) => {
|
await mongoSessionRun(async (session) => {
|
||||||
const parentDefaultPermission = await (async () => {
|
|
||||||
if (parentId) {
|
|
||||||
const { dataset: parentDataset } = await authDataset({
|
|
||||||
req,
|
|
||||||
authToken: true,
|
|
||||||
datasetId: parentId,
|
|
||||||
per: WritePermissionVal
|
|
||||||
});
|
|
||||||
return parentDataset.defaultPermission;
|
|
||||||
}
|
|
||||||
return DatasetDefaultPermissionVal;
|
|
||||||
})();
|
|
||||||
|
|
||||||
if (isFolder && dataset.inheritPermission) {
|
if (isFolder && dataset.inheritPermission) {
|
||||||
const parentClbs = await getResourceAllClbs({
|
const parentClbsAndGroups = await getResourceClbsAndGroups({
|
||||||
teamId: dataset.teamId,
|
teamId: dataset.teamId,
|
||||||
resourceId: parentId,
|
resourceId: parentId,
|
||||||
resourceType: PerResourceTypeEnum.dataset,
|
resourceType: PerResourceTypeEnum.dataset,
|
||||||
@ -112,7 +116,7 @@ async function handler(
|
|||||||
teamId: dataset.teamId,
|
teamId: dataset.teamId,
|
||||||
resourceId: id,
|
resourceId: id,
|
||||||
resourceType: PerResourceTypeEnum.dataset,
|
resourceType: PerResourceTypeEnum.dataset,
|
||||||
collaborators: parentClbs,
|
collaborators: parentClbsAndGroups,
|
||||||
session
|
session
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -121,48 +125,13 @@ async function handler(
|
|||||||
resourceType: PerResourceTypeEnum.dataset,
|
resourceType: PerResourceTypeEnum.dataset,
|
||||||
resourceModel: MongoDataset,
|
resourceModel: MongoDataset,
|
||||||
folderTypeList: [DatasetTypeEnum.folder],
|
folderTypeList: [DatasetTypeEnum.folder],
|
||||||
collaborators: parentClbs,
|
collaborators: parentClbsAndGroups,
|
||||||
defaultPermission: parentDefaultPermission,
|
|
||||||
session
|
session
|
||||||
});
|
});
|
||||||
return onUpdate(session, parentDefaultPermission);
|
return onUpdate(session);
|
||||||
}
|
}
|
||||||
return onUpdate(session);
|
return onUpdate(session);
|
||||||
});
|
});
|
||||||
} else if (isDefaultPermissionChanged) {
|
|
||||||
await mongoSessionRun(async (session) => {
|
|
||||||
if (isFolder) {
|
|
||||||
await syncChildrenPermission({
|
|
||||||
defaultPermission,
|
|
||||||
resource: {
|
|
||||||
_id: dataset._id,
|
|
||||||
type: dataset.type,
|
|
||||||
teamId: dataset.teamId,
|
|
||||||
parentId: dataset.parentId
|
|
||||||
},
|
|
||||||
resourceType: PerResourceTypeEnum.dataset,
|
|
||||||
resourceModel: MongoDataset,
|
|
||||||
folderTypeList: [DatasetTypeEnum.folder],
|
|
||||||
session
|
|
||||||
});
|
|
||||||
} else if (dataset.inheritPermission && dataset.parentId) {
|
|
||||||
const parentClbs = await getResourceAllClbs({
|
|
||||||
teamId: dataset.teamId,
|
|
||||||
resourceId: parentId,
|
|
||||||
resourceType: PerResourceTypeEnum.dataset,
|
|
||||||
session
|
|
||||||
});
|
|
||||||
|
|
||||||
await syncCollaborators({
|
|
||||||
teamId: dataset.teamId,
|
|
||||||
resourceId: id,
|
|
||||||
resourceType: PerResourceTypeEnum.dataset,
|
|
||||||
collaborators: parentClbs,
|
|
||||||
session
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return onUpdate(session, defaultPermission);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
return onUpdate();
|
return onUpdate();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { getTestRequest } from '@/test/utils';
|
|
||||||
import '../../__mocks__/base';
|
import '../../__mocks__/base';
|
||||||
|
import { getTestRequest } from '@/test/utils';
|
||||||
import handler, { OutLinkUpdateBody, OutLinkUpdateQuery } from './update';
|
import handler, { OutLinkUpdateBody, OutLinkUpdateQuery } from './update';
|
||||||
import { root } from '../../__mocks__/db/init';
|
|
||||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
|
import { root } from '../../__mocks__/db/init';
|
||||||
|
|
||||||
test('Update Outlink', async () => {
|
beforeAll(async () => {
|
||||||
const outlink = await MongoOutLink.create({
|
await MongoOutLink.create({
|
||||||
shareId: 'aaa',
|
shareId: 'aaa',
|
||||||
appId: root.appId,
|
appId: root.appId,
|
||||||
tmbId: root.tmbId,
|
tmbId: root.tmbId,
|
||||||
@ -14,8 +14,13 @@ test('Update Outlink', async () => {
|
|||||||
type: 'share',
|
type: 'share',
|
||||||
name: 'aaa'
|
name: 'aaa'
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
await outlink.save();
|
test('Update Outlink', async () => {
|
||||||
|
const outlink = await MongoOutLink.findOne({ name: 'aaa' }).lean();
|
||||||
|
if (!outlink) {
|
||||||
|
throw new Error('Outlink not found');
|
||||||
|
}
|
||||||
|
|
||||||
const res = (await handler(
|
const res = (await handler(
|
||||||
...getTestRequest<OutLinkUpdateQuery, OutLinkUpdateBody>({
|
...getTestRequest<OutLinkUpdateQuery, OutLinkUpdateBody>({
|
||||||
@ -27,6 +32,7 @@ test('Update Outlink', async () => {
|
|||||||
})
|
})
|
||||||
)) as any;
|
)) as any;
|
||||||
|
|
||||||
|
console.log(res);
|
||||||
expect(res.code).toBe(200);
|
expect(res.code).toBe(200);
|
||||||
|
|
||||||
const link = await MongoOutLink.findById(outlink._id).lean();
|
const link = await MongoOutLink.findById(outlink._id).lean();
|
||||||
|
|||||||
@ -28,16 +28,13 @@ import {
|
|||||||
} from '@/web/core/app/api/collaborator';
|
} from '@/web/core/app/api/collaborator';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { AppContext } from '@/pages/app/detail/components/context';
|
import { AppContext } from '@/pages/app/detail/components/context';
|
||||||
import {
|
import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant';
|
||||||
AppDefaultPermissionVal,
|
|
||||||
AppPermissionList
|
|
||||||
} from '@fastgpt/global/support/permission/app/constant';
|
|
||||||
import DefaultPermissionList from '@/components/support/permission/DefaultPerList';
|
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { resumeInheritPer } from '@/web/core/app/api';
|
import { resumeInheritPer } from '@/web/core/app/api';
|
||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
import ResumeInherit from '@/components/support/permission/ResumeInheritText';
|
import ResumeInherit from '@/components/support/permission/ResumeInheritText';
|
||||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||||
|
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||||
|
|
||||||
const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -67,8 +64,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
await updateAppDetail({
|
await updateAppDetail({
|
||||||
name: data.name,
|
name: data.name,
|
||||||
avatar: data.avatar,
|
avatar: data.avatar,
|
||||||
intro: data.intro,
|
intro: data.intro
|
||||||
defaultPermission: data.defaultPermission
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -129,24 +125,25 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
|
|
||||||
const onUpdateCollaborators = ({
|
const onUpdateCollaborators = ({
|
||||||
members,
|
members,
|
||||||
|
groups,
|
||||||
permission
|
permission
|
||||||
}: {
|
}: {
|
||||||
members: string[];
|
members?: string[];
|
||||||
|
groups?: string[];
|
||||||
permission: PermissionValueType;
|
permission: PermissionValueType;
|
||||||
}) => {
|
}) =>
|
||||||
return postUpdateAppCollaborators({
|
postUpdateAppCollaborators({
|
||||||
members,
|
members,
|
||||||
|
groups,
|
||||||
permission,
|
permission,
|
||||||
appId: appDetail._id
|
appId: appDetail._id
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const onDelCollaborator = (tmbId: string) => {
|
const onDelCollaborator = async (props: RequireOnlyOne<{ tmbId: string; groupId: string }>) =>
|
||||||
return deleteAppCollaborators({
|
deleteAppCollaborators({
|
||||||
appId: appDetail._id,
|
appId: appDetail._id,
|
||||||
tmbId
|
...props
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
const { runAsync: resumeInheritPermission } = useRequest2(() => resumeInheritPer(appDetail._id), {
|
const { runAsync: resumeInheritPermission } = useRequest2(() => resumeInheritPer(appDetail._id), {
|
||||||
errorToast: t('common:resume_failed'),
|
errorToast: t('common:resume_failed'),
|
||||||
@ -204,33 +201,19 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
<ResumeInherit onResume={resumeInheritPermission} />
|
<ResumeInherit onResume={resumeInheritPermission} />
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Box mt="4">
|
|
||||||
<Box fontSize={'sm'}>{t('common:permission.Default permission')}</Box>
|
|
||||||
<DefaultPermissionList
|
|
||||||
mt="2"
|
|
||||||
per={appDetail.defaultPermission}
|
|
||||||
defaultPer={AppDefaultPermissionVal}
|
|
||||||
isInheritPermission={appDetail.inheritPermission}
|
|
||||||
onChange={(v) => {
|
|
||||||
setValue('defaultPermission', v);
|
|
||||||
return handleSubmit((data) => saveSubmitSuccess(data), saveSubmitError)();
|
|
||||||
}}
|
|
||||||
hasParent={!!appDetail.parentId}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Box mt={6}>
|
<Box mt={6}>
|
||||||
<CollaboratorContextProvider
|
<CollaboratorContextProvider
|
||||||
|
mode="all"
|
||||||
permission={appDetail.permission}
|
permission={appDetail.permission}
|
||||||
onGetCollaboratorList={() => getCollaboratorList(appDetail._id)}
|
onGetCollaboratorList={() => getCollaboratorList(appDetail._id)}
|
||||||
permissionList={AppPermissionList}
|
permissionList={AppPermissionList}
|
||||||
onUpdateCollaborators={(props) => {
|
onUpdateCollaborators={async (props) =>
|
||||||
if (props.members) {
|
onUpdateCollaborators({
|
||||||
return onUpdateCollaborators({
|
permission: props.permission,
|
||||||
permission: props.permission,
|
members: props.members,
|
||||||
members: props.members
|
groups: props.groups
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}}
|
|
||||||
onDelOneCollaborator={onDelCollaborator}
|
onDelOneCollaborator={onDelCollaborator}
|
||||||
refreshDeps={[appDetail.inheritPermission]}
|
refreshDeps={[appDetail.inheritPermission]}
|
||||||
isInheritPermission={appDetail.inheritPermission}
|
isInheritPermission={appDetail.inheritPermission}
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import {
|
|||||||
Button,
|
Button,
|
||||||
IconButton,
|
IconButton,
|
||||||
HStack,
|
HStack,
|
||||||
Modal,
|
|
||||||
ModalBody,
|
ModalBody,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
ModalFooter
|
ModalFooter
|
||||||
@ -19,26 +18,23 @@ import TagsEditModal from '../TagsEditModal';
|
|||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { AppContext } from '@/pages/app/detail/components/context';
|
import { AppContext } from '@/pages/app/detail/components/context';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import PermissionIconText from '@/components/support/permission/IconText';
|
|
||||||
import MyTag from '@fastgpt/web/components/common/Tag/index';
|
|
||||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { postTransition2Workflow } from '@/web/core/app/api/app';
|
import { postTransition2Workflow } from '@/web/core/app/api/app';
|
||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
|
||||||
|
|
||||||
const AppCard = () => {
|
const AppCard = () => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { appT } = useI18n();
|
const { appT } = useI18n();
|
||||||
const { isPc } = useSystem();
|
|
||||||
|
|
||||||
const { appDetail, setAppDetail, onOpenInfoEdit, onDelApp } = useContextSelector(
|
const { appDetail, setAppDetail, onOpenInfoEdit, onDelApp } = useContextSelector(
|
||||||
AppContext,
|
AppContext,
|
||||||
(v) => v
|
(v) => v
|
||||||
);
|
);
|
||||||
|
|
||||||
const appId = appDetail._id;
|
const appId = appDetail._id;
|
||||||
const { feConfigs } = useSystemStore();
|
const { feConfigs } = useSystemStore();
|
||||||
const [TeamTagsSet, setTeamTagsSet] = useState<AppSchema>();
|
const [TeamTagsSet, setTeamTagsSet] = useState<AppSchema>();
|
||||||
@ -150,15 +146,15 @@ const AppCard = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Box flex={1} />
|
<Box flex={1} />
|
||||||
{isPc && (
|
{/* {isPc && ( */}
|
||||||
<MyTag
|
{/* <MyTag */}
|
||||||
type="borderFill"
|
{/* type="borderFill" */}
|
||||||
colorSchema="gray"
|
{/* colorSchema="gray" */}
|
||||||
onClick={() => (appDetail.permission.hasManagePer ? onOpenInfoEdit() : undefined)}
|
{/* onClick={() => (appDetail.permission.hasManagePer ? onOpenInfoEdit() : undefined)} */}
|
||||||
>
|
{/* > */}
|
||||||
<PermissionIconText defaultPermission={appDetail.defaultPermission} />
|
{/* <PermissionIconText defaultPermission={appDetail.defaultPermission} /> */}
|
||||||
</MyTag>
|
{/* </MyTag> */}
|
||||||
)}
|
{/* )} */}
|
||||||
</HStack>
|
</HStack>
|
||||||
</Box>
|
</Box>
|
||||||
{TeamTagsSet && <TagsEditModal onClose={() => setTeamTagsSet(undefined)} />}
|
{TeamTagsSet && <TagsEditModal onClose={() => setTeamTagsSet(undefined)} />}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Dispatch, ReactNode, SetStateAction, useCallback, useEffect, useState } from 'react';
|
import { Dispatch, ReactNode, SetStateAction, useCallback, useState } from 'react';
|
||||||
import { createContext } from 'use-context-selector';
|
import { createContext } from 'use-context-selector';
|
||||||
import { defaultApp } from '@/web/core/app/constants';
|
import { defaultApp } from '@/web/core/app/constants';
|
||||||
import { delAppById, getAppDetailById, putAppById } from '@/web/core/app/api';
|
import { delAppById, getAppDetailById, putAppById } from '@/web/core/app/api';
|
||||||
|
|||||||
@ -17,10 +17,7 @@ import { useFolderDrag } from '@/components/common/folder/useFolderDrag';
|
|||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import type { EditResourceInfoFormType } from '@/components/common/Modal/EditResourceModal';
|
import type { EditResourceInfoFormType } from '@/components/common/Modal/EditResourceModal';
|
||||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||||
import {
|
import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant';
|
||||||
AppDefaultPermissionVal,
|
|
||||||
AppPermissionList
|
|
||||||
} from '@fastgpt/global/support/permission/app/constant';
|
|
||||||
import {
|
import {
|
||||||
deleteAppCollaborators,
|
deleteAppCollaborators,
|
||||||
getCollaboratorList,
|
getCollaboratorList,
|
||||||
@ -38,6 +35,7 @@ import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
|
|||||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||||
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
import { useChatStore } from '@/web/core/chat/context/storeChat';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
|
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||||
const HttpEditModal = dynamic(() => import('./HttpPluginEditModal'));
|
const HttpEditModal = dynamic(() => import('./HttpPluginEditModal'));
|
||||||
|
|
||||||
const ListItem = () => {
|
const ListItem = () => {
|
||||||
@ -49,11 +47,16 @@ const ListItem = () => {
|
|||||||
const { loadAndGetTeamMembers } = useUserStore();
|
const { loadAndGetTeamMembers } = useUserStore();
|
||||||
const { lastChatAppId, setLastChatAppId } = useChatStore();
|
const { lastChatAppId, setLastChatAppId } = useChatStore();
|
||||||
|
|
||||||
|
const { openConfirm: openMoveConfirm, ConfirmModal: MoveConfirmModal } = useConfirm({
|
||||||
|
type: 'common',
|
||||||
|
title: t('common:move.confirm'),
|
||||||
|
content: t('app:move.hint')
|
||||||
|
});
|
||||||
|
|
||||||
const { myApps, loadMyApps, onUpdateApp, setMoveAppId, folderDetail } = useContextSelector(
|
const { myApps, loadMyApps, onUpdateApp, setMoveAppId, folderDetail } = useContextSelector(
|
||||||
AppListContext,
|
AppListContext,
|
||||||
(v) => v
|
(v) => v
|
||||||
);
|
);
|
||||||
const [loadingAppId, setLoadingAppId] = useState<string>();
|
|
||||||
|
|
||||||
const [editedApp, setEditedApp] = useState<EditResourceInfoFormType>();
|
const [editedApp, setEditedApp] = useState<EditResourceInfoFormType>();
|
||||||
const [editHttpPlugin, setEditHttpPlugin] = useState<EditHttpPluginProps>();
|
const [editHttpPlugin, setEditHttpPlugin] = useState<EditHttpPluginProps>();
|
||||||
@ -64,17 +67,20 @@ const ListItem = () => {
|
|||||||
[editPerAppIndex, myApps]
|
[editPerAppIndex, myApps]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const parentApp = useMemo(() => myApps.find((item) => item._id === parentId), [parentId, myApps]);
|
||||||
|
|
||||||
|
const { runAsync: onPutAppById } = useRequest2(putAppById, {
|
||||||
|
onSuccess() {
|
||||||
|
loadMyApps();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const { getBoxProps } = useFolderDrag({
|
const { getBoxProps } = useFolderDrag({
|
||||||
activeStyles: {
|
activeStyles: {
|
||||||
borderColor: 'primary.600'
|
borderColor: 'primary.600'
|
||||||
},
|
},
|
||||||
onDrop: async (dragId: string, targetId: string) => {
|
onDrop: (dragId: string, targetId: string) => {
|
||||||
setLoadingAppId(dragId);
|
openMoveConfirm(async () => onPutAppById(dragId, { parentId: targetId }))();
|
||||||
try {
|
|
||||||
await putAppById(dragId, { parentId: targetId });
|
|
||||||
loadMyApps();
|
|
||||||
} catch (error) {}
|
|
||||||
setLoadingAppId(undefined);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -152,7 +158,6 @@ const ListItem = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<MyBox
|
<MyBox
|
||||||
isLoading={loadingAppId === app._id}
|
|
||||||
lineHeight={1.5}
|
lineHeight={1.5}
|
||||||
h="100%"
|
h="100%"
|
||||||
pt={5}
|
pt={5}
|
||||||
@ -233,7 +238,7 @@ const ListItem = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
<PermissionIconText
|
<PermissionIconText
|
||||||
defaultPermission={app.defaultPermission}
|
private={app.private}
|
||||||
color={'myGray.500'}
|
color={'myGray.500'}
|
||||||
iconColor={'myGray.400'}
|
iconColor={'myGray.400'}
|
||||||
w={'0.875rem'}
|
w={'0.875rem'}
|
||||||
@ -247,7 +252,9 @@ const ListItem = () => {
|
|||||||
<Box color={'myGray.500'}>{formatTimeToChatTime(app.updateTime)}</Box>
|
<Box color={'myGray.500'}>{formatTimeToChatTime(app.updateTime)}</Box>
|
||||||
</HStack>
|
</HStack>
|
||||||
)}
|
)}
|
||||||
{app.permission.hasWritePer && (
|
{(AppFolderTypeList.includes(app.type)
|
||||||
|
? app.permission.hasManagePer
|
||||||
|
: app.permission.hasWritePer) && (
|
||||||
<Box className="more" display={['', 'none']}>
|
<Box className="more" display={['', 'none']}>
|
||||||
<MyMenu
|
<MyMenu
|
||||||
Button={
|
Button={
|
||||||
@ -315,7 +322,9 @@ const ListItem = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
...(folderDetail?.type === AppTypeEnum.httpPlugin
|
...(folderDetail?.type === AppTypeEnum.httpPlugin &&
|
||||||
|
!(parentApp ? parentApp.permission : app.permission)
|
||||||
|
.hasManagePer
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
{
|
{
|
||||||
@ -412,34 +421,29 @@ const ListItem = () => {
|
|||||||
isInheritPermission={editPerApp.inheritPermission}
|
isInheritPermission={editPerApp.inheritPermission}
|
||||||
avatar={editPerApp.avatar}
|
avatar={editPerApp.avatar}
|
||||||
name={editPerApp.name}
|
name={editPerApp.name}
|
||||||
defaultPer={{
|
|
||||||
value: editPerApp.defaultPermission,
|
|
||||||
defaultValue: AppDefaultPermissionVal,
|
|
||||||
onChange: (e) => {
|
|
||||||
return onUpdateApp(editPerApp._id, { defaultPermission: e });
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
managePer={{
|
managePer={{
|
||||||
|
mode: 'all',
|
||||||
permission: editPerApp.permission,
|
permission: editPerApp.permission,
|
||||||
onGetCollaboratorList: () => getCollaboratorList(editPerApp._id),
|
onGetCollaboratorList: () => getCollaboratorList(editPerApp._id),
|
||||||
permissionList: AppPermissionList,
|
permissionList: AppPermissionList,
|
||||||
onUpdateCollaborators: ({
|
onUpdateCollaborators: (props: {
|
||||||
members = [], // TODO: remove the default value after group is ready
|
|
||||||
permission
|
|
||||||
}: {
|
|
||||||
members?: string[];
|
members?: string[];
|
||||||
|
groups?: string[];
|
||||||
permission: number;
|
permission: number;
|
||||||
}) => {
|
}) =>
|
||||||
return postUpdateAppCollaborators({
|
postUpdateAppCollaborators({
|
||||||
members,
|
...props,
|
||||||
permission,
|
|
||||||
appId: editPerApp._id
|
appId: editPerApp._id
|
||||||
});
|
}),
|
||||||
},
|
onDelOneCollaborator: async (
|
||||||
onDelOneCollaborator: (tmbId: string) =>
|
props: RequireOnlyOne<{
|
||||||
|
tmbId?: string;
|
||||||
|
groupId?: string;
|
||||||
|
}>
|
||||||
|
) =>
|
||||||
deleteAppCollaborators({
|
deleteAppCollaborators({
|
||||||
appId: editPerApp._id,
|
...props,
|
||||||
tmbId
|
appId: editPerApp._id
|
||||||
}),
|
}),
|
||||||
refreshDeps: [editPerApp.inheritPermission]
|
refreshDeps: [editPerApp.inheritPermission]
|
||||||
}}
|
}}
|
||||||
@ -452,6 +456,7 @@ const ListItem = () => {
|
|||||||
onClose={() => setEditHttpPlugin(undefined)}
|
onClose={() => setEditHttpPlugin(undefined)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<MoveConfirmModal />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -12,9 +12,9 @@ import {
|
|||||||
} from '@fastgpt/global/common/parentFolder/type';
|
} from '@fastgpt/global/common/parentFolder/type';
|
||||||
import { AppUpdateParams } from '@/global/core/app/api';
|
import { AppUpdateParams } from '@/global/core/app/api';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { useI18n } from '@/web/context/I18n';
|
|
||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
const MoveModal = dynamic(() => import('@/components/common/folder/MoveModal'));
|
const MoveModal = dynamic(() => import('@/components/common/folder/MoveModal'));
|
||||||
|
|
||||||
type AppListContextType = {
|
type AppListContextType = {
|
||||||
@ -58,7 +58,7 @@ export const AppListContext = createContext<AppListContextType>({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const AppListContextProvider = ({ children }: { children: ReactNode }) => {
|
const AppListContextProvider = ({ children }: { children: ReactNode }) => {
|
||||||
const { appT } = useI18n();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { parentId = null, type = 'ALL' } = router.query as {
|
const { parentId = null, type = 'ALL' } = router.query as {
|
||||||
parentId?: string | null;
|
parentId?: string | null;
|
||||||
@ -129,10 +129,12 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
parentId,
|
parentId,
|
||||||
type: AppTypeEnum.folder
|
type: AppTypeEnum.folder
|
||||||
}).then((res) =>
|
}).then((res) =>
|
||||||
res.map((item) => ({
|
res
|
||||||
id: item._id,
|
.filter((item) => item.permission.hasWritePer)
|
||||||
name: item.name
|
.map((item) => ({
|
||||||
}))
|
id: item._id,
|
||||||
|
name: item.name
|
||||||
|
}))
|
||||||
);
|
);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@ -162,9 +164,10 @@ const AppListContextProvider = ({ children }: { children: ReactNode }) => {
|
|||||||
<MoveModal
|
<MoveModal
|
||||||
moveResourceId={moveAppId}
|
moveResourceId={moveAppId}
|
||||||
server={getAppFolderList}
|
server={getAppFolderList}
|
||||||
title={appT('move_app')}
|
title={t('app:move_app')}
|
||||||
onClose={() => setMoveAppId(undefined)}
|
onClose={() => setMoveAppId(undefined)}
|
||||||
onConfirm={onMoveApp}
|
onConfirm={onMoveApp}
|
||||||
|
moveHint={t('app:move.hint')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</AppListContext.Provider>
|
</AppListContext.Provider>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useMemo, useState } from 'react';
|
import React, { useMemo, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Flex,
|
Flex,
|
||||||
@ -14,8 +14,6 @@ import { useUserStore } from '@/web/support/user/useUserStore';
|
|||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
|
|
||||||
import List from './components/List';
|
|
||||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||||
import { FolderIcon } from '@fastgpt/global/common/file/image/constants';
|
import { FolderIcon } from '@fastgpt/global/common/file/image/constants';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
@ -27,10 +25,7 @@ import FolderPath from '@/components/common/folder/Path';
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import FolderSlideCard from '@/components/common/folder/SlideCard';
|
import FolderSlideCard from '@/components/common/folder/SlideCard';
|
||||||
import { delAppById, resumeInheritPer } from '@/web/core/app/api';
|
import { delAppById, resumeInheritPer } from '@/web/core/app/api';
|
||||||
import {
|
import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant';
|
||||||
AppDefaultPermissionVal,
|
|
||||||
AppPermissionList
|
|
||||||
} from '@fastgpt/global/support/permission/app/constant';
|
|
||||||
import {
|
import {
|
||||||
deleteAppCollaborators,
|
deleteAppCollaborators,
|
||||||
getCollaboratorList,
|
getCollaboratorList,
|
||||||
@ -49,6 +44,7 @@ const EditFolderModal = dynamic(
|
|||||||
() => import('@fastgpt/web/components/common/MyModal/EditFolderModal')
|
() => import('@fastgpt/web/components/common/MyModal/EditFolderModal')
|
||||||
);
|
);
|
||||||
const HttpEditModal = dynamic(() => import('./components/HttpPluginEditModal'));
|
const HttpEditModal = dynamic(() => import('./components/HttpPluginEditModal'));
|
||||||
|
const List = dynamic(() => import('./components/List'));
|
||||||
|
|
||||||
const MyApps = () => {
|
const MyApps = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -273,36 +269,47 @@ const MyApps = () => {
|
|||||||
onMove={() => setMoveAppId(folderDetail._id)}
|
onMove={() => setMoveAppId(folderDetail._id)}
|
||||||
deleteTip={appT('confirm_delete_folder_tip')}
|
deleteTip={appT('confirm_delete_folder_tip')}
|
||||||
onDelete={() => onDeleFolder(folderDetail._id)}
|
onDelete={() => onDeleFolder(folderDetail._id)}
|
||||||
defaultPer={{
|
|
||||||
value: folderDetail.defaultPermission,
|
|
||||||
defaultValue: AppDefaultPermissionVal,
|
|
||||||
onChange: (e) => {
|
|
||||||
return onUpdateApp(folderDetail._id, { defaultPermission: e });
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
managePer={{
|
managePer={{
|
||||||
|
mode: 'all',
|
||||||
permission: folderDetail.permission,
|
permission: folderDetail.permission,
|
||||||
onGetCollaboratorList: () => getCollaboratorList(folderDetail._id),
|
onGetCollaboratorList: () => getCollaboratorList(folderDetail._id),
|
||||||
permissionList: AppPermissionList,
|
permissionList: AppPermissionList,
|
||||||
onUpdateCollaborators: ({
|
onUpdateCollaborators: ({
|
||||||
members = [], // TODO: remove the default value after group is ready
|
members,
|
||||||
|
groups,
|
||||||
permission
|
permission
|
||||||
}: {
|
}: {
|
||||||
members?: string[];
|
members?: string[];
|
||||||
|
groups?: string[];
|
||||||
permission: number;
|
permission: number;
|
||||||
}) => {
|
}) => {
|
||||||
return postUpdateAppCollaborators({
|
return postUpdateAppCollaborators({
|
||||||
members,
|
members,
|
||||||
|
groups,
|
||||||
permission,
|
permission,
|
||||||
appId: folderDetail._id
|
appId: folderDetail._id
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
refreshDeps: [folderDetail._id, folderDetail.inheritPermission],
|
refreshDeps: [folderDetail._id, folderDetail.inheritPermission],
|
||||||
onDelOneCollaborator: (tmbId: string) =>
|
onDelOneCollaborator: async ({
|
||||||
deleteAppCollaborators({
|
tmbId,
|
||||||
appId: folderDetail._id,
|
groupId
|
||||||
tmbId
|
}: {
|
||||||
})
|
tmbId?: string;
|
||||||
|
groupId?: string;
|
||||||
|
}) => {
|
||||||
|
if (tmbId) {
|
||||||
|
return deleteAppCollaborators({
|
||||||
|
appId: folderDetail._id,
|
||||||
|
tmbId
|
||||||
|
});
|
||||||
|
} else if (groupId) {
|
||||||
|
return deleteAppCollaborators({
|
||||||
|
appId: folderDetail._id,
|
||||||
|
groupId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { useRouter } from 'next/router';
|
|
||||||
import { Box, Flex, Input } from '@chakra-ui/react';
|
import { Box, Flex, Input } from '@chakra-ui/react';
|
||||||
import { delDatasetById } from '@/web/core/dataset/api';
|
|
||||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
@ -10,7 +8,7 @@ import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d';
|
|||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { useRequest, useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||||
import AIModelSelector from '@/components/Select/AIModelSelector';
|
import AIModelSelector from '@/components/Select/AIModelSelector';
|
||||||
import { postRebuildEmbedding } from '@/web/core/dataset/api';
|
import { postRebuildEmbedding } from '@/web/core/dataset/api';
|
||||||
@ -21,12 +19,8 @@ import MyDivider from '@fastgpt/web/components/common/MyDivider/index';
|
|||||||
import { DatasetTypeEnum, DatasetTypeMap } from '@fastgpt/global/core/dataset/constants';
|
import { DatasetTypeEnum, DatasetTypeMap } from '@fastgpt/global/core/dataset/constants';
|
||||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||||
import DefaultPermissionList from '@/components/support/permission/DefaultPerList';
|
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import {
|
import { DatasetPermissionList } from '@fastgpt/global/support/permission/dataset/constant';
|
||||||
DatasetDefaultPermissionVal,
|
|
||||||
DatasetPermissionList
|
|
||||||
} from '@fastgpt/global/support/permission/dataset/constant';
|
|
||||||
import MemberManager from '../../component/MemberManager';
|
import MemberManager from '../../component/MemberManager';
|
||||||
import {
|
import {
|
||||||
getCollaboratorList,
|
getCollaboratorList,
|
||||||
@ -39,7 +33,6 @@ import { EditResourceInfoFormType } from '@/components/common/Modal/EditResource
|
|||||||
const EditResourceModal = dynamic(() => import('@/components/common/Modal/EditResourceModal'));
|
const EditResourceModal = dynamic(() => import('@/components/common/Modal/EditResourceModal'));
|
||||||
|
|
||||||
const Info = ({ datasetId }: { datasetId: string }) => {
|
const Info = ({ datasetId }: { datasetId: string }) => {
|
||||||
const router = useRouter();
|
|
||||||
const [openBaseConfig, setOpenBaseConfig] = useState(true);
|
const [openBaseConfig, setOpenBaseConfig] = useState(true);
|
||||||
const [openPermissionConfig, setOpenPermissionConfig] = useState(true);
|
const [openPermissionConfig, setOpenPermissionConfig] = useState(true);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@ -56,10 +49,9 @@ const Info = ({ datasetId }: { datasetId: string }) => {
|
|||||||
|
|
||||||
const vectorModel = watch('vectorModel');
|
const vectorModel = watch('vectorModel');
|
||||||
const agentModel = watch('agentModel');
|
const agentModel = watch('agentModel');
|
||||||
const defaultPermission = watch('defaultPermission');
|
|
||||||
|
|
||||||
const { datasetModelList, vectorModelList } = useSystemStore();
|
const { datasetModelList, vectorModelList } = useSystemStore();
|
||||||
const { openConfirm: onOpenConfirmDel, ConfirmModal: ConfirmDelModal } = useConfirm({
|
const { ConfirmModal: ConfirmDelModal } = useConfirm({
|
||||||
content: t('common:core.dataset.Delete Confirm'),
|
content: t('common:core.dataset.Delete Confirm'),
|
||||||
type: 'delete'
|
type: 'delete'
|
||||||
});
|
});
|
||||||
@ -69,30 +61,17 @@ const Info = ({ datasetId }: { datasetId: string }) => {
|
|||||||
type: 'delete'
|
type: 'delete'
|
||||||
});
|
});
|
||||||
|
|
||||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
const { File } = useSelectFile({
|
||||||
fileType: '.jpg,.png',
|
fileType: '.jpg,.png',
|
||||||
multiple: false
|
multiple: false
|
||||||
});
|
});
|
||||||
|
|
||||||
/* 点击删除 */
|
const { runAsync: onSave } = useRequest2(
|
||||||
const { mutate: onclickDelete, isLoading: isDeleting } = useRequest({
|
|
||||||
mutationFn: () => {
|
|
||||||
return delDatasetById(datasetId);
|
|
||||||
},
|
|
||||||
onSuccess() {
|
|
||||||
router.replace(`/dataset/list`);
|
|
||||||
},
|
|
||||||
successToast: t('common:common.Delete Success'),
|
|
||||||
errorToast: t('common:common.Delete Failed')
|
|
||||||
});
|
|
||||||
|
|
||||||
const { runAsync: onSave, loading: isSaving } = useRequest2(
|
|
||||||
(data: DatasetItemType) => {
|
(data: DatasetItemType) => {
|
||||||
return updateDataset({
|
return updateDataset({
|
||||||
id: datasetId,
|
id: datasetId,
|
||||||
agentModel: data.agentModel,
|
agentModel: data.agentModel,
|
||||||
externalReadUrl: data.externalReadUrl,
|
externalReadUrl: data.externalReadUrl
|
||||||
defaultPermission: data.defaultPermission
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -101,7 +80,7 @@ const Info = ({ datasetId }: { datasetId: string }) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { runAsync: onSelectFile, loading: isSelecting } = useRequest2(
|
const { runAsync: onSelectFile } = useRequest2(
|
||||||
(e: File[]) => {
|
(e: File[]) => {
|
||||||
const file = e[0];
|
const file = e[0];
|
||||||
if (!file) return Promise.resolve(null);
|
if (!file) return Promise.resolve(null);
|
||||||
@ -122,7 +101,7 @@ const Info = ({ datasetId }: { datasetId: string }) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { runAsync: onRebuilding, loading: isRebuilding } = useRequest2(
|
const { runAsync: onRebuilding } = useRequest2(
|
||||||
(vectorModel: VectorModelItemType) => {
|
(vectorModel: VectorModelItemType) => {
|
||||||
return postRebuildEmbedding({
|
return postRebuildEmbedding({
|
||||||
datasetId,
|
datasetId,
|
||||||
@ -242,10 +221,9 @@ const Info = ({ datasetId }: { datasetId: string }) => {
|
|||||||
onchange={(e) => {
|
onchange={(e) => {
|
||||||
const vectorModel = vectorModelList.find((item) => item.model === e);
|
const vectorModel = vectorModelList.find((item) => item.model === e);
|
||||||
if (!vectorModel) return;
|
if (!vectorModel) return;
|
||||||
return onOpenConfirmRebuild(() => {
|
return onOpenConfirmRebuild(async () => {
|
||||||
return onRebuilding(vectorModel).then(() => {
|
await onRebuilding(vectorModel);
|
||||||
setValue('vectorModel', vectorModel);
|
setValue('vectorModel', vectorModel);
|
||||||
});
|
|
||||||
})();
|
})();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -326,20 +304,12 @@ const Info = ({ datasetId }: { datasetId: string }) => {
|
|||||||
<FormLabel fontWeight={'500'} fontSize={'mini'} pb={3} userSelect={'none'}>
|
<FormLabel fontWeight={'500'} fontSize={'mini'} pb={3} userSelect={'none'}>
|
||||||
{t('common:permission.Default permission')}
|
{t('common:permission.Default permission')}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<DefaultPermissionList
|
|
||||||
fontSize={'mini'}
|
|
||||||
per={defaultPermission}
|
|
||||||
defaultPer={DatasetDefaultPermissionVal}
|
|
||||||
onChange={(v) => {
|
|
||||||
setValue('defaultPermission', v);
|
|
||||||
return handleSubmit((data) => onSave({ ...data, defaultPermission: v }))();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box py={4}>
|
<Box py={4}>
|
||||||
<MemberManager
|
<MemberManager
|
||||||
managePer={{
|
managePer={{
|
||||||
|
mode: 'all',
|
||||||
permission: datasetDetail.permission,
|
permission: datasetDetail.permission,
|
||||||
onGetCollaboratorList: () => getCollaboratorList(datasetId),
|
onGetCollaboratorList: () => getCollaboratorList(datasetId),
|
||||||
permissionList: DatasetPermissionList,
|
permissionList: DatasetPermissionList,
|
||||||
@ -348,11 +318,19 @@ const Info = ({ datasetId }: { datasetId: string }) => {
|
|||||||
...body,
|
...body,
|
||||||
datasetId
|
datasetId
|
||||||
}),
|
}),
|
||||||
onDelOneCollaborator: (tmbId) =>
|
onDelOneCollaborator: async ({ groupId, tmbId }) => {
|
||||||
deleteDatasetCollaborators({
|
if (tmbId) {
|
||||||
datasetId,
|
return deleteDatasetCollaborators({
|
||||||
tmbId
|
datasetId,
|
||||||
})
|
tmbId
|
||||||
|
});
|
||||||
|
} else if (groupId) {
|
||||||
|
return deleteDatasetCollaborators({
|
||||||
|
datasetId,
|
||||||
|
groupId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@ -18,10 +18,7 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
|||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { DatasetsContext } from '../context';
|
import { DatasetsContext } from '../context';
|
||||||
import {
|
import { DatasetPermissionList } from '@fastgpt/global/support/permission/dataset/constant';
|
||||||
DatasetDefaultPermissionVal,
|
|
||||||
DatasetPermissionList
|
|
||||||
} from '@fastgpt/global/support/permission/dataset/constant';
|
|
||||||
import ConfigPerModal from '@/components/support/permission/ConfigPerModal';
|
import ConfigPerModal from '@/components/support/permission/ConfigPerModal';
|
||||||
import {
|
import {
|
||||||
deleteDatasetCollaborators,
|
deleteDatasetCollaborators,
|
||||||
@ -34,7 +31,6 @@ import MyBox from '@fastgpt/web/components/common/MyBox';
|
|||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import { formatTimeToChatTime } from '@fastgpt/global/common/string/time';
|
|
||||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||||
import SideTag from './SideTag';
|
import SideTag from './SideTag';
|
||||||
|
|
||||||
@ -42,7 +38,6 @@ const EditResourceModal = dynamic(() => import('@/components/common/Modal/EditRe
|
|||||||
|
|
||||||
function List() {
|
function List() {
|
||||||
const { setLoading } = useSystemStore();
|
const { setLoading } = useSystemStore();
|
||||||
const { toast } = useToast();
|
|
||||||
const { isPc } = useSystem();
|
const { isPc } = useSystem();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { commonT } = useI18n();
|
const { commonT } = useI18n();
|
||||||
@ -59,21 +54,32 @@ function List() {
|
|||||||
folderDetail
|
folderDetail
|
||||||
} = useContextSelector(DatasetsContext, (v) => v);
|
} = useContextSelector(DatasetsContext, (v) => v);
|
||||||
const [editPerDatasetIndex, setEditPerDatasetIndex] = useState<number>();
|
const [editPerDatasetIndex, setEditPerDatasetIndex] = useState<number>();
|
||||||
const [loadingDatasetId, setLoadingDatasetId] = useState<string>();
|
const router = useRouter();
|
||||||
|
const { parentId = null } = router.query as { parentId?: string | null };
|
||||||
|
const parentDataset = useMemo(
|
||||||
|
() => myDatasets.find((item) => String(item._id) === parentId),
|
||||||
|
[parentId, myDatasets]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { openConfirm: openMoveConfirm, ConfirmModal: MoveConfirmModal } = useConfirm({
|
||||||
|
type: 'common',
|
||||||
|
title: t('common:move.confirm'),
|
||||||
|
content: t('dataset:move.hint')
|
||||||
|
});
|
||||||
|
|
||||||
|
const { runAsync: updateDataset } = useRequest2(onUpdateDataset);
|
||||||
|
|
||||||
const { getBoxProps } = useFolderDrag({
|
const { getBoxProps } = useFolderDrag({
|
||||||
activeStyles: {
|
activeStyles: {
|
||||||
borderColor: 'primary.600'
|
borderColor: 'primary.600'
|
||||||
},
|
},
|
||||||
onDrop: async (dragId: string, targetId: string) => {
|
onDrop: (dragId: string, targetId: string) => {
|
||||||
setLoadingDatasetId(dragId);
|
openMoveConfirm(() =>
|
||||||
try {
|
updateDataset({
|
||||||
await onUpdateDataset({
|
|
||||||
id: dragId,
|
id: dragId,
|
||||||
parentId: targetId
|
parentId: targetId
|
||||||
});
|
})
|
||||||
} catch (error) {}
|
)();
|
||||||
setLoadingDatasetId(undefined);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -86,10 +92,6 @@ function List() {
|
|||||||
[editPerDatasetIndex, myDatasets]
|
[editPerDatasetIndex, myDatasets]
|
||||||
);
|
);
|
||||||
|
|
||||||
const router = useRouter();
|
|
||||||
|
|
||||||
const { parentId = null } = router.query as { parentId?: string | null };
|
|
||||||
|
|
||||||
const { mutate: exportDataset } = useRequest({
|
const { mutate: exportDataset } = useRequest({
|
||||||
mutationFn: async (dataset: DatasetItemType) => {
|
mutationFn: async (dataset: DatasetItemType) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@ -100,15 +102,10 @@ function List() {
|
|||||||
filename: `${dataset.name}.csv`
|
filename: `${dataset.name}.csv`
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSuccess() {
|
|
||||||
toast({
|
|
||||||
status: 'success',
|
|
||||||
title: t('common:core.dataset.Start export')
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onSettled() {
|
onSettled() {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
},
|
},
|
||||||
|
successToast: t('common:core.dataset.Start export'),
|
||||||
errorToast: t('common:dataset.Export Dataset Limit Error')
|
errorToast: t('common:dataset.Export Dataset Limit Error')
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -176,7 +173,6 @@ function List() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<MyBox
|
<MyBox
|
||||||
isLoading={loadingDatasetId === dataset._id}
|
|
||||||
display={'flex'}
|
display={'flex'}
|
||||||
flexDirection={'column'}
|
flexDirection={'column'}
|
||||||
lineHeight={1.5}
|
lineHeight={1.5}
|
||||||
@ -278,8 +274,8 @@ function List() {
|
|||||||
</HStack>
|
</HStack>
|
||||||
)}
|
)}
|
||||||
<PermissionIconText
|
<PermissionIconText
|
||||||
|
private={dataset.private}
|
||||||
iconColor="myGray.400"
|
iconColor="myGray.400"
|
||||||
defaultPermission={dataset.defaultPermission}
|
|
||||||
color={'myGray.500'}
|
color={'myGray.500'}
|
||||||
/>
|
/>
|
||||||
</HStack>
|
</HStack>
|
||||||
@ -293,7 +289,9 @@ function List() {
|
|||||||
</Box>
|
</Box>
|
||||||
</HStack>
|
</HStack>
|
||||||
)}
|
)}
|
||||||
{dataset.permission.hasWritePer && (
|
{(dataset.type === DatasetTypeEnum.folder
|
||||||
|
? dataset.permission.hasManagePer
|
||||||
|
: dataset.permission.hasWritePer) && (
|
||||||
<Box
|
<Box
|
||||||
className="more"
|
className="more"
|
||||||
display={['', 'none']}
|
display={['', 'none']}
|
||||||
@ -336,11 +334,18 @@ function List() {
|
|||||||
avatar: dataset.avatar
|
avatar: dataset.avatar
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
{
|
...((parentDataset ? parentDataset : dataset)?.permission
|
||||||
icon: 'common/file/move',
|
.hasManagePer
|
||||||
label: t('common:Move'),
|
? [
|
||||||
onClick: () => setMoveDatasetId(dataset._id)
|
{
|
||||||
},
|
icon: 'common/file/move',
|
||||||
|
label: t('common:Move'),
|
||||||
|
onClick: () => {
|
||||||
|
setMoveDatasetId(dataset._id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
: []),
|
||||||
...(dataset.permission.hasManagePer
|
...(dataset.permission.hasManagePer
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
@ -427,36 +432,20 @@ function List() {
|
|||||||
}
|
}
|
||||||
avatar={editPerDataset.avatar}
|
avatar={editPerDataset.avatar}
|
||||||
name={editPerDataset.name}
|
name={editPerDataset.name}
|
||||||
defaultPer={{
|
|
||||||
value: editPerDataset.defaultPermission,
|
|
||||||
defaultValue: DatasetDefaultPermissionVal,
|
|
||||||
onChange: (e) =>
|
|
||||||
onUpdateDataset({
|
|
||||||
id: editPerDataset._id,
|
|
||||||
defaultPermission: e
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
managePer={{
|
managePer={{
|
||||||
|
mode: 'all',
|
||||||
permission: editPerDataset.permission,
|
permission: editPerDataset.permission,
|
||||||
onGetCollaboratorList: () => getCollaboratorList(editPerDataset._id),
|
onGetCollaboratorList: () => getCollaboratorList(editPerDataset._id),
|
||||||
permissionList: DatasetPermissionList,
|
permissionList: DatasetPermissionList,
|
||||||
onUpdateCollaborators: ({
|
onUpdateCollaborators: (props) =>
|
||||||
members = [], // TODO: remove default value after group is ready
|
postUpdateDatasetCollaborators({
|
||||||
permission
|
...props,
|
||||||
}: {
|
|
||||||
members?: string[];
|
|
||||||
permission: number;
|
|
||||||
}) => {
|
|
||||||
return postUpdateDatasetCollaborators({
|
|
||||||
members,
|
|
||||||
permission,
|
|
||||||
datasetId: editPerDataset._id
|
datasetId: editPerDataset._id
|
||||||
});
|
}),
|
||||||
},
|
onDelOneCollaborator: async (props) =>
|
||||||
onDelOneCollaborator: (tmbId: string) =>
|
|
||||||
deleteDatasetCollaborators({
|
deleteDatasetCollaborators({
|
||||||
datasetId: editPerDataset._id,
|
...props,
|
||||||
tmbId
|
datasetId: editPerDataset._id
|
||||||
}),
|
}),
|
||||||
refreshDeps: [editPerDataset._id, editPerDataset.inheritPermission]
|
refreshDeps: [editPerDataset._id, editPerDataset.inheritPermission]
|
||||||
}}
|
}}
|
||||||
@ -464,6 +453,7 @@ function List() {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<ConfirmModal />
|
<ConfirmModal />
|
||||||
|
<MoveConfirmModal />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,186 +0,0 @@
|
|||||||
import React, { useMemo, useState } from 'react';
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
Flex,
|
|
||||||
Box,
|
|
||||||
Button,
|
|
||||||
ModalBody,
|
|
||||||
ModalHeader,
|
|
||||||
ModalFooter,
|
|
||||||
useTheme,
|
|
||||||
Grid
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
|
||||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
|
||||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
|
||||||
import { useTranslation } from 'next-i18next';
|
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { getDatasets, putDatasetById, getDatasetPaths } from '@/web/core/dataset/api';
|
|
||||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
|
||||||
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
|
|
||||||
|
|
||||||
const MoveModal = ({
|
|
||||||
onClose,
|
|
||||||
onSuccess,
|
|
||||||
moveDataId
|
|
||||||
}: {
|
|
||||||
onClose: () => void;
|
|
||||||
onSuccess: () => void;
|
|
||||||
moveDataId: string;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const theme = useTheme();
|
|
||||||
|
|
||||||
const [parentId, setParentId] = useState<string>('');
|
|
||||||
|
|
||||||
const { data } = useQuery(['getDatasets', parentId], () => {
|
|
||||||
return Promise.all([
|
|
||||||
getDatasets({ parentId, type: DatasetTypeEnum.folder }),
|
|
||||||
getDatasetPaths(parentId)
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
const paths = useMemo(
|
|
||||||
() => [
|
|
||||||
{
|
|
||||||
parentId: '',
|
|
||||||
parentName: t('common:core.dataset.My Dataset')
|
|
||||||
},
|
|
||||||
...(data?.[1] || [])
|
|
||||||
],
|
|
||||||
[data, t]
|
|
||||||
);
|
|
||||||
const folderList = useMemo(
|
|
||||||
() => (data?.[0] || []).filter((item) => item._id !== moveDataId),
|
|
||||||
[moveDataId, data]
|
|
||||||
);
|
|
||||||
|
|
||||||
const { mutate, isLoading } = useRequest({
|
|
||||||
mutationFn: () => putDatasetById({ id: moveDataId, parentId }),
|
|
||||||
onSuccess,
|
|
||||||
errorToast: t('common:dataset.Move Failed')
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MyModal
|
|
||||||
isOpen={true}
|
|
||||||
maxW={['90vw', '800px']}
|
|
||||||
w={'800px'}
|
|
||||||
iconSrc="/imgs/modal/move.svg"
|
|
||||||
title={
|
|
||||||
<>
|
|
||||||
{!!parentId ? (
|
|
||||||
<Flex flex={1} userSelect={'none'} fontSize={['sm', 'md']} fontWeight={'normal'}>
|
|
||||||
{paths.map((item, i) => (
|
|
||||||
<Flex key={item.parentId} mr={2} alignItems={'center'}>
|
|
||||||
<Box
|
|
||||||
borderRadius={'md'}
|
|
||||||
{...(i === paths.length - 1
|
|
||||||
? {
|
|
||||||
cursor: 'default'
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
cursor: 'pointer',
|
|
||||||
_hover: {
|
|
||||||
color: 'primary.500'
|
|
||||||
},
|
|
||||||
onClick: () => {
|
|
||||||
setParentId(item.parentId);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{item.parentName}
|
|
||||||
</Box>
|
|
||||||
{i !== paths.length - 1 && (
|
|
||||||
<MyIcon name={'common/rightArrowLight'} color={'myGray.500'} w={'14px'} />
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
) : (
|
|
||||||
<Box>{t('common:core.dataset.My Dataset')}</Box>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
onClose={onClose}
|
|
||||||
>
|
|
||||||
<Flex flexDirection={'column'} h={['90vh', 'auto']}>
|
|
||||||
<ModalBody
|
|
||||||
flex={['1 0 0', '0 0 auto']}
|
|
||||||
maxH={'80vh'}
|
|
||||||
overflowY={'auto'}
|
|
||||||
display={'grid'}
|
|
||||||
userSelect={'none'}
|
|
||||||
>
|
|
||||||
<Grid
|
|
||||||
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
|
|
||||||
gridGap={3}
|
|
||||||
>
|
|
||||||
{folderList.map((item) =>
|
|
||||||
(() => {
|
|
||||||
return (
|
|
||||||
<MyTooltip
|
|
||||||
key={item._id}
|
|
||||||
label={
|
|
||||||
item.type === DatasetTypeEnum.dataset
|
|
||||||
? t('common:dataset.Select Dataset')
|
|
||||||
: t('common:dataset.Select Folder')
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Card
|
|
||||||
p={3}
|
|
||||||
border={theme.borders.base}
|
|
||||||
boxShadow={'sm'}
|
|
||||||
h={'80px'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
_hover={{
|
|
||||||
boxShadow: 'md'
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
setParentId(item._id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Flex alignItems={'center'} h={'38px'}>
|
|
||||||
<Avatar src={item.avatar} w={['24px', '28px']}></Avatar>
|
|
||||||
<Box
|
|
||||||
className="textEllipsis"
|
|
||||||
ml={3}
|
|
||||||
fontWeight={'bold'}
|
|
||||||
fontSize={['md', 'md']}
|
|
||||||
>
|
|
||||||
{item.name}
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
|
|
||||||
{item.type === DatasetTypeEnum.folder ? (
|
|
||||||
<Box color={'myGray.500'}>{t('common:Folder')}</Box>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<MyIcon mr={1} name="kbTest" w={'12px'} />
|
|
||||||
<Box color={'myGray.500'}>{item.vectorModel.name}</Box>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
</Card>
|
|
||||||
</MyTooltip>
|
|
||||||
);
|
|
||||||
})()
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
{folderList.length === 0 && (
|
|
||||||
<EmptyTip text={t('common:common.folder.No Folder')}></EmptyTip>
|
|
||||||
)}
|
|
||||||
</ModalBody>
|
|
||||||
|
|
||||||
<ModalFooter>
|
|
||||||
<Button isLoading={isLoading} onClick={mutate}>
|
|
||||||
{t('common:dataset.Confirm move the folder')}
|
|
||||||
</Button>
|
|
||||||
</ModalFooter>
|
|
||||||
</Flex>
|
|
||||||
</MyModal>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default MoveModal;
|
|
||||||
@ -13,7 +13,6 @@ import {
|
|||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { createContext } from 'use-context-selector';
|
import { createContext } from 'use-context-selector';
|
||||||
import { useI18n } from '@/web/context/I18n';
|
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { DatasetUpdateBody } from '@fastgpt/global/core/dataset/api';
|
import { DatasetUpdateBody } from '@fastgpt/global/core/dataset/api';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
@ -68,7 +67,6 @@ export const DatasetsContext = createContext<DatasetContextType>({
|
|||||||
|
|
||||||
function DatasetContextProvider({ children }: { children: React.ReactNode }) {
|
function DatasetContextProvider({ children }: { children: React.ReactNode }) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { commonT } = useI18n();
|
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [moveDatasetId, setMoveDatasetId] = useState<string>();
|
const [moveDatasetId, setMoveDatasetId] = useState<string>();
|
||||||
const [searchKey, setSearchKey] = useState('');
|
const [searchKey, setSearchKey] = useState('');
|
||||||
@ -127,10 +125,12 @@ function DatasetContextProvider({ children }: { children: React.ReactNode }) {
|
|||||||
parentId,
|
parentId,
|
||||||
type: DatasetTypeEnum.folder
|
type: DatasetTypeEnum.folder
|
||||||
})
|
})
|
||||||
).map((item) => ({
|
)
|
||||||
id: item._id,
|
.filter((item) => item.permission.hasManagePer)
|
||||||
name: item.name
|
.map((item) => ({
|
||||||
}));
|
id: item._id,
|
||||||
|
name: item.name
|
||||||
|
}));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const [editedDataset, setEditedDataset] = useState<EditResourceInfoFormType>();
|
const [editedDataset, setEditedDataset] = useState<EditResourceInfoFormType>();
|
||||||
@ -164,9 +164,10 @@ function DatasetContextProvider({ children }: { children: React.ReactNode }) {
|
|||||||
<MoveModal
|
<MoveModal
|
||||||
moveResourceId={moveDatasetId}
|
moveResourceId={moveDatasetId}
|
||||||
server={getDatasetFolderList}
|
server={getDatasetFolderList}
|
||||||
title={commonT('Move')}
|
title={t('common:Move')}
|
||||||
onClose={() => setMoveDatasetId(undefined)}
|
onClose={() => setMoveDatasetId(undefined)}
|
||||||
onConfirm={onMoveDataset}
|
onConfirm={(parentId) => onMoveDataset(parentId)}
|
||||||
|
moveHint={t('dataset:move.hint')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</DatasetsContext.Provider>
|
</DatasetsContext.Provider>
|
||||||
|
|||||||
@ -17,10 +17,7 @@ import { EditFolderFormType } from '@fastgpt/web/components/common/MyModal/EditF
|
|||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { postCreateDatasetFolder, resumeInheritPer } from '@/web/core/dataset/api';
|
import { postCreateDatasetFolder, resumeInheritPer } from '@/web/core/dataset/api';
|
||||||
import FolderSlideCard from '@/components/common/folder/SlideCard';
|
import FolderSlideCard from '@/components/common/folder/SlideCard';
|
||||||
import {
|
import { DatasetPermissionList } from '@fastgpt/global/support/permission/dataset/constant';
|
||||||
DatasetDefaultPermissionVal,
|
|
||||||
DatasetPermissionList
|
|
||||||
} from '@fastgpt/global/support/permission/dataset/constant';
|
|
||||||
import {
|
import {
|
||||||
postUpdateDatasetCollaborators,
|
postUpdateDatasetCollaborators,
|
||||||
deleteDatasetCollaborators,
|
deleteDatasetCollaborators,
|
||||||
@ -52,7 +49,6 @@ const Dataset = () => {
|
|||||||
loadMyDatasets,
|
loadMyDatasets,
|
||||||
refetchFolderDetail,
|
refetchFolderDetail,
|
||||||
folderDetail,
|
folderDetail,
|
||||||
setEditedDataset,
|
|
||||||
setMoveDatasetId,
|
setMoveDatasetId,
|
||||||
onDelDataset,
|
onDelDataset,
|
||||||
onUpdateDataset,
|
onUpdateDataset,
|
||||||
@ -228,38 +224,39 @@ const Dataset = () => {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
defaultPer={{
|
|
||||||
value: folderDetail.defaultPermission,
|
|
||||||
defaultValue: DatasetDefaultPermissionVal,
|
|
||||||
onChange: (e) => {
|
|
||||||
return onUpdateDataset({
|
|
||||||
id: folderDetail._id,
|
|
||||||
defaultPermission: e
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
managePer={{
|
managePer={{
|
||||||
|
mode: 'all',
|
||||||
permission: folderDetail.permission,
|
permission: folderDetail.permission,
|
||||||
onGetCollaboratorList: () => getCollaboratorList(folderDetail._id),
|
onGetCollaboratorList: () => getCollaboratorList(folderDetail._id),
|
||||||
permissionList: DatasetPermissionList,
|
permissionList: DatasetPermissionList,
|
||||||
onUpdateCollaborators: ({
|
onUpdateCollaborators: ({
|
||||||
members = [], // TODO: remove the default value after group is ready
|
members,
|
||||||
|
groups,
|
||||||
permission
|
permission
|
||||||
}: {
|
}: {
|
||||||
members?: string[];
|
members?: string[];
|
||||||
|
groups?: string[];
|
||||||
permission: number;
|
permission: number;
|
||||||
}) => {
|
}) =>
|
||||||
return postUpdateDatasetCollaborators({
|
postUpdateDatasetCollaborators({
|
||||||
members,
|
members,
|
||||||
|
groups,
|
||||||
permission,
|
permission,
|
||||||
datasetId: folderDetail._id
|
datasetId: folderDetail._id
|
||||||
});
|
|
||||||
},
|
|
||||||
onDelOneCollaborator: (tmbId: string) =>
|
|
||||||
deleteDatasetCollaborators({
|
|
||||||
datasetId: folderDetail._id,
|
|
||||||
tmbId
|
|
||||||
}),
|
}),
|
||||||
|
onDelOneCollaborator: async ({ tmbId, groupId }) => {
|
||||||
|
if (tmbId) {
|
||||||
|
return deleteDatasetCollaborators({
|
||||||
|
datasetId: folderDetail._id,
|
||||||
|
tmbId
|
||||||
|
});
|
||||||
|
} else if (groupId) {
|
||||||
|
return deleteDatasetCollaborators({
|
||||||
|
datasetId: folderDetail._id,
|
||||||
|
groupId
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
refreshDeps: [folderDetail._id, folderDetail.inheritPermission]
|
refreshDeps: [folderDetail._id, folderDetail.inheritPermission]
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -64,7 +64,7 @@ export function getTestRequest<Q = any, B = any>({
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MockParseHeaderCert = async ({
|
export const parseHeaderCertMock = async ({
|
||||||
req,
|
req,
|
||||||
authToken = true,
|
authToken = true,
|
||||||
authRoot = false,
|
authRoot = false,
|
||||||
|
|||||||
@ -2,8 +2,6 @@ import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
|||||||
import { AppDetailType } from '@fastgpt/global/core/app/type.d';
|
import { AppDetailType } from '@fastgpt/global/core/app/type.d';
|
||||||
import type { FeishuAppType, OutLinkEditType } from '@fastgpt/global/support/outLink/type.d';
|
import type { FeishuAppType, OutLinkEditType } from '@fastgpt/global/support/outLink/type.d';
|
||||||
import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
|
import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
|
||||||
import { NullPermission } from '@fastgpt/global/support/permission/constant';
|
|
||||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
|
||||||
export const defaultApp: AppDetailType = {
|
export const defaultApp: AppDetailType = {
|
||||||
_id: '',
|
_id: '',
|
||||||
name: 'AI',
|
name: 'AI',
|
||||||
@ -18,7 +16,6 @@ export const defaultApp: AppDetailType = {
|
|||||||
teamTags: [],
|
teamTags: [],
|
||||||
edges: [],
|
edges: [],
|
||||||
version: 'v2',
|
version: 'v2',
|
||||||
defaultPermission: NullPermission,
|
|
||||||
permission: new AppPermission(),
|
permission: new AppPermission(),
|
||||||
inheritPermission: false
|
inheritPermission: false
|
||||||
};
|
};
|
||||||
|
|||||||
@ -11,5 +11,5 @@ export const getCollaboratorList = (datasetId: string) =>
|
|||||||
export const postUpdateDatasetCollaborators = (body: UpdateDatasetCollaboratorBody) =>
|
export const postUpdateDatasetCollaborators = (body: UpdateDatasetCollaboratorBody) =>
|
||||||
POST('/proApi/core/dataset/collaborator/update', body);
|
POST('/proApi/core/dataset/collaborator/update', body);
|
||||||
|
|
||||||
export const deleteDatasetCollaborators = ({ ...params }: DatasetCollaboratorDeleteParams) =>
|
export const deleteDatasetCollaborators = (params: DatasetCollaboratorDeleteParams) =>
|
||||||
DELETE('/proApi/core/dataset/collaborator/delete', { ...params });
|
DELETE('/proApi/core/dataset/collaborator/delete', params);
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import type {
|
|||||||
DatasetCollectionItemType,
|
DatasetCollectionItemType,
|
||||||
DatasetItemType
|
DatasetItemType
|
||||||
} from '@fastgpt/global/core/dataset/type.d';
|
} from '@fastgpt/global/core/dataset/type.d';
|
||||||
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
|
||||||
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
|
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
|
||||||
|
|
||||||
export const defaultDatasetDetail: DatasetItemType = {
|
export const defaultDatasetDetail: DatasetItemType = {
|
||||||
@ -26,7 +25,6 @@ export const defaultDatasetDetail: DatasetItemType = {
|
|||||||
permission: new DatasetPermission(),
|
permission: new DatasetPermission(),
|
||||||
vectorModel: defaultVectorModels[0],
|
vectorModel: defaultVectorModels[0],
|
||||||
agentModel: defaultQAModels[0],
|
agentModel: defaultQAModels[0],
|
||||||
defaultPermission: DatasetDefaultPermissionVal,
|
|
||||||
inheritPermission: true
|
inheritPermission: true
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -48,7 +46,6 @@ export const defaultCollectionDetail: DatasetCollectionItemType = {
|
|||||||
status: 'active',
|
status: 'active',
|
||||||
vectorModel: defaultVectorModels[0].model,
|
vectorModel: defaultVectorModels[0].model,
|
||||||
agentModel: defaultQAModels[0].model,
|
agentModel: defaultQAModels[0].model,
|
||||||
defaultPermission: DatasetDefaultPermissionVal,
|
|
||||||
inheritPermission: true
|
inheritPermission: true
|
||||||
},
|
},
|
||||||
tags: [],
|
tags: [],
|
||||||
|
|||||||
@ -28,6 +28,7 @@ type State = {
|
|||||||
loadAndGetTeamMembers: (init?: boolean) => Promise<TeamMemberItemType[]>;
|
loadAndGetTeamMembers: (init?: boolean) => Promise<TeamMemberItemType[]>;
|
||||||
|
|
||||||
teamMemberGroups: MemberGroupListType;
|
teamMemberGroups: MemberGroupListType;
|
||||||
|
myGroups: MemberGroupListType;
|
||||||
loadAndGetGroups: (init?: boolean) => Promise<MemberGroupListType>;
|
loadAndGetGroups: (init?: boolean) => Promise<MemberGroupListType>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -106,6 +107,7 @@ export const useUserStore = create<State>()(
|
|||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
teamMemberGroups: [],
|
teamMemberGroups: [],
|
||||||
|
myGroups: [],
|
||||||
loadAndGetGroups: async (init = false) => {
|
loadAndGetGroups: async (init = false) => {
|
||||||
if (!useSystemStore.getState()?.feConfigs?.isPlus) return [];
|
if (!useSystemStore.getState()?.feConfigs?.isPlus) return [];
|
||||||
|
|
||||||
@ -116,6 +118,9 @@ export const useUserStore = create<State>()(
|
|||||||
const res = await getGroupList();
|
const res = await getGroupList();
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.teamMemberGroups = res;
|
state.teamMemberGroups = res;
|
||||||
|
state.myGroups = res.filter((item) =>
|
||||||
|
item.members.map((i) => String(i.tmbId)).includes(String(state.userInfo?.team?.tmbId))
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user