Skip to content

Commit

Permalink
feat(member-group): Team (#2616)
Browse files Browse the repository at this point in the history
* feat: member-group schema define

* feat(fe): create group

* feat: add group edit modal

* feat(fe): add avatar group component

* feat: edit group
fix: permission select menu style

* feat: bio-mode support for select-member component

* fix: avatar group key unique

* feat: group manage

* feat: divide member into group and clbs

* feat: finish team permission

* chore: adjust

* fix: get clbs
  • Loading branch information
FinleyGe authored and c121914yu committed Sep 13, 2024
1 parent 092bb9a commit e194eea
Show file tree
Hide file tree
Showing 48 changed files with 1,483 additions and 441 deletions.
17 changes: 16 additions & 1 deletion packages/global/common/error/code/team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ export enum TeamErrEnum {
appAmountNotEnough = 'appAmountNotEnough',
pluginAmountNotEnough = 'pluginAmountNotEnough',
websiteSyncNotEnough = 'websiteSyncNotEnough',
reRankNotEnough = 'reRankNotEnough'
reRankNotEnough = 'reRankNotEnough',
groupNameEmpty = 'groupNameEmpty',
groupNotExist = 'groupNotExist',
cannotDeleteDefaultGroup = 'cannotDeleteDefaultGroup'
}

const teamErr = [
Expand Down Expand Up @@ -46,6 +49,18 @@ const teamErr = [
{
statusText: TeamErrEnum.reRankNotEnough,
message: i18nT('common:code_error.team_error.re_rank_not_enough')
},
{
statusText: TeamErrEnum.groupNameEmpty,
message: i18nT('common:code_error.team_error.group_name_empty')
},
{
statusText: TeamErrEnum.groupNotExist,
message: i18nT('common:code_error.team_error.group_not_exist')
},
{
statusText: TeamErrEnum.cannotDeleteDefaultGroup,
message: i18nT('common:code_error.team_error.cannot_delete_default_group')
}
];

Expand Down
5 changes: 5 additions & 0 deletions packages/global/common/file/image/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export enum MongoImageTypeEnum {
datasetAvatar = 'datasetAvatar',
userAvatar = 'userAvatar',
teamAvatar = 'teamAvatar',
groupAvatar = 'groupAvatar',

chatImage = 'chatImage',
collectionImage = 'collectionImage'
Expand Down Expand Up @@ -36,6 +37,10 @@ export const mongoImageTypeMap = {
label: 'teamAvatar',
unique: true
},
[MongoImageTypeEnum.groupAvatar]: {
label: 'groupAvatar',
unique: true
},

[MongoImageTypeEnum.chatImage]: {
label: 'chatImage',
Expand Down
9 changes: 8 additions & 1 deletion packages/global/support/permission/collaborator.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { RequireAtLeastOne, RequireOnlyOne } from '../../common/type/utils';
import { Permission } from './controller';
import { PermissionValueType } from './type';

Expand All @@ -10,6 +11,12 @@ export type CollaboratorItemType = {
};

export type UpdateClbPermissionProps = {
tmbIds: string[];
members?: string[];
groups?: string[];
permission: PermissionValueType;
};

export type DeleteClbPermissionProps = RequireOnlyOne<{
tmbId: string;
groupId: string;
}>;
5 changes: 5 additions & 0 deletions packages/global/support/permission/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ export enum PerResourceTypeEnum {
dataset = 'dataset'
}

export enum SubjectTypeEnum {
tmb = 'tmb',
group = 'group'
}

/* new permission */
export enum PermissionKeyEnum {
read = 'read',
Expand Down
21 changes: 21 additions & 0 deletions packages/global/support/permission/memberGroup/type.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { TeamMemberItemType } from 'support/user/team/type';
import { TeamPermission } from '../user/controller';

type MemberGroupSchemaType = {
_id: string;
teamId: string;
name: string;
avatar: string;
};

type GroupMemberSchemaType = {
groupId: string;
tmbId: string;
};

type MemberGroupType = MemberGroupSchemaType & {
members: string[]; // we can get tmb's info from other api. there is no need but only need to get tmb's id
permission: TeamPermission;
};

type MemberGroupListType = MemberGroupType[];
15 changes: 12 additions & 3 deletions packages/global/support/permission/type.d.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import { RequireOnlyOne } from '../../common/type/utils';
import { TeamMemberWithUserSchema } from '../user/team/type';
import { AuthUserTypeEnum, PermissionKeyEnum, PerResourceTypeEnum } from './constant';
import {
AuthUserTypeEnum,
PermissionKeyEnum,
PerResourceTypeEnum,
SubjectTypeEnum
} from './constant';

// 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.
// The lowest 3 bits present the permission of reading, writing and managing.
// The higher bits are advanced permissions or extended permissions, which could be customized.
export type PermissionValueType = number;
export type ResourceType = `${PerResourceTypeEnum}`;
export type SubjectType = `${SubjectTypeEnum}`;

export type PermissionListType<T = {}> = Record<
T | PermissionKeyEnum,
Expand All @@ -20,11 +27,13 @@ export type PermissionListType<T = {}> = Record<

export type ResourcePermissionType = {
teamId: string;
tmbId: string;
resourceType: ResourceType;
permission: PermissionValueType;
resourceId: string;
};
} & RequireOnlyOne<{
tmbId: string;
groupId: string;
}>;

export type ResourcePerWithTmbWithUser = Omit<ResourcePermissionType, 'tmbId'> & {
tmbId: TeamMemberWithUserSchema;
Expand Down
1 change: 0 additions & 1 deletion packages/global/support/user/team/controller.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ export type UpdateTeamMemberProps = {
export type InviteMemberProps = {
teamId: string;
usernames: string[];
permission: PermissionValueType;
};
export type UpdateInviteProps = {
tmbId: string;
Expand Down
12 changes: 12 additions & 0 deletions packages/global/support/user/team/group/api.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export type postCreateGroupData = {
name: string;
avatar?: string;
memberIdList?: string[];
};

export type putUpdateGroupData = {
groupId: string;
name?: string;
avatar?: string;
memberIdList?: string[];
};
4 changes: 3 additions & 1 deletion packages/global/support/user/team/type.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ export type TeamSchema = {
lastWebsiteSyncTime: Date;
};
lafAccount: LafAccountType;
defaultPermission: PermissionValueType;
notificationAccount?: string;
};

export type tagsType = {
label: string;
key: string;
};

export type TeamTagSchema = TeamTagItemType & {
_id: string;
teamId: string;
Expand All @@ -45,9 +45,11 @@ export type TeamMemberSchema = {
export type TeamMemberWithUserSchema = Omit<TeamMemberSchema, 'userId'> & {
userId: UserModelSchema;
};

export type TeamMemberWithTeamSchema = Omit<TeamMemberSchema, 'teamId'> & {
teamId: TeamSchema;
};

export type TeamMemberWithTeamAndUserSchema = Omit<TeamMemberWithTeamSchema, 'userId'> & {
userId: UserModelSchema;
};
Expand Down
2 changes: 1 addition & 1 deletion packages/service/support/permission/app/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export const authAppByTmbId = async ({
resourceId: appId,
resourceType: PerResourceTypeEnum.app
});
const Per = new AppPermission({ per: rp?.permission ?? app.defaultPermission, isOwner });
const Per = new AppPermission({ per: rp ?? app.defaultPermission, isOwner });
return {
Per,
defaultPermission: app.defaultPermission
Expand Down
114 changes: 95 additions & 19 deletions packages/service/support/permission/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,58 +8,121 @@ import { authOpenApiKey } from '../openapi/auth';
import { FileTokenQuery } from '@fastgpt/global/common/file/type';
import { MongoResourcePermission } from './schema';
import { ClientSession } from 'mongoose';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
import {
PermissionValueType,
ResourcePermissionType
} from '@fastgpt/global/support/permission/type';
import { bucketNameMap } from '@fastgpt/global/common/file/constants';
import { addMinutes } from 'date-fns';
import { getGroupsByTmbId } from './memberGroup/controllers';

/** get resource permission for a team member
* If there is no permission for the team member, it will return undefined
* @param resourceType: PerResourceTypeEnum
* @param teamId
* @param tmbId
* @param resourceId
* @returns PermissionValueType | undefined
*/
export const getResourcePermission = async ({
resourceType,
teamId,
tmbId,
resourceId
}: {
resourceType: PerResourceTypeEnum;
teamId: string;
tmbId: string;
resourceId?: string;
}) => {
const per = await MongoResourcePermission.findOne({
tmbId,
teamId,
resourceType,
resourceId
});
} & (
| {
resourceType: 'team';
resourceId?: undefined;
}
| {
resourceType: Omit<PerResourceTypeEnum, 'team'>;
resourceId: string;
}
)): Promise<PermissionValueType | undefined> => {
const tmbPer = (
await MongoResourcePermission.findOne(
{
tmbId,
teamId,
resourceType,
resourceId
},
'permission'
).lean()
)?.permission;

if (tmbPer !== undefined) {
// could be 0
return tmbPer;
}

if (!per) {
return null;
const groupIdList = await getGroupsByTmbId(tmbId);
if (!groupIdList || !groupIdList.length) {
return tmbPer; // could be undefined
}
return per;

const groupPer = await (async () => {
// get the maximum permission of the group
if (!groupIdList || !groupIdList.length) {
return undefined;
}
const pers = (
await MongoResourcePermission.find(
{
teamId,
resourceType,
groupId: {
$in: groupIdList
},
resourceId
},
'permission'
)
).map((item) => item.permission);

return getMaxGroupPer(pers);
})();

return groupPer ?? undefined;
};

export async function getResourceAllClbs({
resourceId,
teamId,
resourceType,
session
}: {
resourceId: ParentIdType;
teamId: string;
resourceType: PerResourceTypeEnum;
session?: ClientSession;
}): Promise<ResourcePermissionType[]> {
if (!resourceId) return [];
} & (
| {
resourceType: 'team';
resourceId?: undefined;
}
| {
resourceType: Omit<PerResourceTypeEnum, 'team'>;
resourceId?: string | null;
}
)): Promise<ResourcePermissionType[]> {
return MongoResourcePermission.find(
{
resourceId,
resourceType: resourceType,
teamId: teamId
teamId: teamId,
groupId: {
$exists: false
}
},
null,
{
session
}
).lean();
}

export const delResourcePermissionById = (id: string) => {
return MongoResourcePermission.findByIdAndRemove(id);
};
Expand Down Expand Up @@ -301,3 +364,16 @@ export const authFileToken = (token?: string) =>
});
});
});

export const getMaxGroupPer = (groups?: PermissionValueType[]) => {
if (!groups || !groups.length) {
return undefined;
}
return groups.reduce((prev, cur) => {
if (cur) {
return Math.max(prev, cur);
} else {
return prev;
}
});
};
2 changes: 1 addition & 1 deletion packages/service/support/permission/dataset/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const authDatasetByTmbId = async ({
resourceType: PerResourceTypeEnum.dataset
});
const Per = new DatasetPermission({
per: rp?.permission ?? dataset.defaultPermission,
per: rp ?? dataset.defaultPermission,
isOwner
});
return {
Expand Down
7 changes: 5 additions & 2 deletions packages/service/support/permission/inheritPermission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { MongoResourcePermission } from './schema';
import { ClientSession, Model } from 'mongoose';
import { NullPermission, PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { getResourceAllClbs } from './controller';
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';

export type SyncChildrenPermissionResourceType = {
_id: string;
Expand All @@ -14,8 +15,10 @@ export type SyncChildrenPermissionResourceType = {
};
export type UpdateCollaboratorItem = {
permission: PermissionValueType;
} & RequireOnlyOne<{
tmbId: string;
};
groupId: string;
}>;

// sync the permission to all children folders.
export async function syncChildrenPermission({
Expand Down
Loading

0 comments on commit e194eea

Please sign in to comment.