Skip to content

Commit

Permalink
feat: add org schema
Browse files Browse the repository at this point in the history
  • Loading branch information
I-Info committed Dec 18, 2024
1 parent b279242 commit 44d0894
Show file tree
Hide file tree
Showing 8 changed files with 361 additions and 0 deletions.
7 changes: 7 additions & 0 deletions packages/global/common/error/code/team.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ export enum TeamErrEnum {
groupNameEmpty = 'groupNameEmpty',
groupNameDuplicate = 'groupNameDuplicate',
groupNotExist = 'groupNotExist',
orgNameEmpty = 'orgNameEmpty',
orgOwnerNotExist = 'orgOwnerNotExist',
orgNotExist = 'orgNotExist',
orgParentNotExist = 'orgParentNotExist',
deletingOrgWithChildren = 'deletingOrgWithChildren',
deletingRootOrg = 'deletingRootOrg',
orgNameDuplicate = 'orgNameDuplicate',
cannotDeleteDefaultGroup = 'cannotDeleteDefaultGroup',
userNotActive = 'userNotActive'
}
Expand Down
28 changes: 28 additions & 0 deletions packages/global/support/user/team/org/api.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { OrgMemberRole } from './constant';

type postCreateOrgData = {
name: string;
parentId: string;
avatar?: string;
};

type putUpdateOrgMembersData = {
orgId: string;
members: {
tmbId: string;
role: `${OrgMemberRole}`;
}[];
};

type putChnageOrgOwnerData = {
orgId: string;
tmbId: string;
toAdmin?: boolean;
};

type putUpdateOrgData = {
orgId: string;
name?: string;
avatar?: string;
parentId?: string;
};
10 changes: 10 additions & 0 deletions packages/global/support/user/team/org/constant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const OrgCollectionName = 'team_orgs';
export const OrgMemberCollectionName = 'team_org_members';

export enum OrgMemberRole {
owner = 'owner',
admin = 'admin',
member = 'member'
}

export const RootOrgName = 'ROOT';
19 changes: 19 additions & 0 deletions packages/global/support/user/team/org/type.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { OrgMemberRole } from './constant';
import { ResourcePermissionType } from '../type';

type OrgSchemaType = {
_id: string;
teamId: string;
path: string;
name: string;
avatar: string;
updateTime: Date;
members: OrgMemberSchemaType[] | undefined;
permissions: ResourcePermissionType[] | undefined;
};

type OrgMemberSchemaType = {
orgId: string | undefined;
tmbId: string;
role: `${OrgMemberRole}`;
};
173 changes: 173 additions & 0 deletions packages/service/support/permission/org/controllers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { MongoOrgMemberModel } from './orgMemberSchema';
import { MongoOrgModel } from './orgSchema';
import { OrgMemberRole } from '@fastgpt/global/support/user/team/org/constant';
import { AuthModeType, AuthResponseType } from '../type';
import { parseHeaderCert } from '../controller';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { getTmbInfoByTmbId } from '../../user/team/controller';
import { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { ClientSession } from 'mongoose';

// if role1 > role2, return 1
// if role1 < role2, return -1
// else return 0
export const compareRole = (role1: OrgMemberRole, role2: OrgMemberRole) => {
if (role1 === OrgMemberRole.owner) {
if (role2 === OrgMemberRole.owner) {
return 0;
}
return 1;
}
if (role2 === OrgMemberRole.owner) {
return -1;
}
if (role1 === OrgMemberRole.admin) {
if (role2 === OrgMemberRole.admin) {
return 0;
}
return 1;
}
if (role2 === OrgMemberRole.admin) {
return -1;
}
return 0;
};

export const checkOrgRole = (role: OrgMemberRole, targetRole: OrgMemberRole) => {
return compareRole(role, targetRole) >= 0;
};

export const getOrgsByTeamId = async (teamId: string) => {
const orgs = await MongoOrgModel.find({
teamId
})
.populate('members')
.lean();

return orgs;
};

export const getOrgAndChildren = async ({
orgId,
session
}: {
orgId: string;
session?: ClientSession;
}) => {
const org = await MongoOrgModel.findById(orgId, undefined, { session }).lean();
if (!org) {
return Promise.reject(TeamErrEnum.orgNotExist);
}
const children = await MongoOrgModel.find(
{
path: {
$regex: `^${org.path}/${org._id}`
}
},
undefined,
{ session }
).lean();
return { org, children };
};

export const getOrgMemberRole = async ({
orgId,
tmbId
}: {
orgId: string;
tmbId: string;
}): Promise<OrgMemberRole | undefined> => {
let role: OrgMemberRole | undefined;
const orgMember = await MongoOrgMemberModel.findOne({
orgId,
tmbId
})
.populate('orgId')
.lean();
if (orgMember) {
role = OrgMemberRole[orgMember.role];
} else {
return role;
}
if (role == OrgMemberRole.owner) {
return role;
}
// Check the parent orgs
const org = orgMember.orgId as unknown as OrgSchemaType;
if (!org) {
return Promise.reject(TeamErrEnum.orgNotExist);
}
const parentIds = org.path.split('/').filter((id) => id);
if (parentIds.length === 0) {
return role;
}
const parentOrgMembers = await MongoOrgMemberModel.find({
orgId: {
$in: parentIds
},
tmbId
}).lean();
// Update the role to the highest role
for (const parentOrgMember of parentOrgMembers) {
const parentRole = OrgMemberRole[parentOrgMember.role];
if (parentRole === OrgMemberRole.owner) {
role = parentRole;
break;
} else if (parentRole === OrgMemberRole.admin && role === OrgMemberRole.member) {
role = parentRole;
}
}
return role;
};

export const authOrgMember = async ({
orgIds,
role,
req,
authToken = false,
authRoot = false,
authApiKey = false
}: {
orgIds: string | string[];
role: `${OrgMemberRole}` | OrgMemberRole;
} & AuthModeType): Promise<AuthResponseType> => {
const result = await parseHeaderCert({ req, authToken, authApiKey, authRoot });
const { teamId, tmbId, isRoot } = result;
if (isRoot) {
return {
teamId,
tmbId,
appId: result.appId,
apikey: result.apikey,
isRoot,
authType: result.authType,
permission: new TeamPermission({ isOwner: true })
};
}

if (!Array.isArray(orgIds)) {
orgIds = [orgIds];
}
const promises = orgIds.map((orgId) => getOrgMemberRole({ orgId, tmbId }));

const [tmb, ...orgRoles] = await Promise.all([getTmbInfoByTmbId({ tmbId }), ...promises]);
if (tmb.permission.hasManagePer) {
return {
...result,
permission: tmb.permission
};
}

let targetRole = OrgMemberRole[role];
for (const orgRole of orgRoles) {
if (!orgRole || checkOrgRole(orgRole, targetRole)) {
return Promise.reject(TeamErrEnum.unAuthTeam);
}
}

return {
...result,
permission: tmb.permission
};
};
48 changes: 48 additions & 0 deletions packages/service/support/permission/org/orgMemberSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { OrgCollectionName, OrgMemberRole } from '@fastgpt/global/support/user/team/org/constant';
import { connectionMongo, getMongoModel } from '../../../common/mongo';
import { TeamMemberCollectionName } from '@fastgpt/global/support/user/team/constant';
import { OrgMemberSchemaType } from '@fastgpt/global/support/user/team/org/type';
const { Schema } = connectionMongo;

export const OrgMemberCollectionName = 'team_org_members';

export const OrgMemberSchema = new Schema({
orgId: {
type: Schema.Types.ObjectId,
ref: OrgCollectionName,
required: true
},
tmbId: {
type: Schema.Types.ObjectId,
ref: TeamMemberCollectionName,
required: true
},
role: {
type: String,
enum: Object.values(OrgMemberRole),
required: true,
default: OrgMemberRole.member
}
});

try {
OrgMemberSchema.index(
{
orgId: 1,
tmbId: 1
},
{
unique: true
}
);
OrgMemberSchema.index({
tmbId: 1
});
} catch (error) {
console.log(error);
}

export const MongoOrgMemberModel = getMongoModel<OrgMemberSchemaType>(
OrgMemberCollectionName,
OrgMemberSchema
);
71 changes: 71 additions & 0 deletions packages/service/support/permission/org/orgSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
import { connectionMongo, getMongoModel } from '../../../common/mongo';
import { OrgMemberCollectionName } from './orgMemberSchema';
import { ResourcePermissionCollectionName } from '../schema';
import { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
import { OrgCollectionName } from '@fastgpt/global/support/user/team/org/constant';
const { Schema } = connectionMongo;

function requiredStringPath(this: OrgSchemaType) {
return typeof this.path === 'string' ? false : true;
}

export const OrgSchema = new Schema(
{
teamId: {
type: Schema.Types.ObjectId,
ref: TeamCollectionName,
required: true
},
path: {
type: String,
required: requiredStringPath
},
name: {
type: String,
required: true
},
avatar: {
type: String
},
updateTime: {
type: Date,
default: () => new Date()
}
},
{
// Auto update updateTime
timestamps: {
updatedAt: 'updateTime'
}
}
);

OrgSchema.virtual('members', {
ref: OrgMemberCollectionName,
localField: '_id',
foreignField: 'orgId'
});
OrgSchema.virtual('permissions', {
ref: ResourcePermissionCollectionName,
localField: '_id',
foreignField: 'orgId'
});

try {
OrgSchema.index(
{
teamId: 1,
path: 1,
name: 1
},
{
unique: true
}
);
OrgSchema.index({ path: 1 });
} catch (error) {
console.log(error);
}

export const MongoOrgModel = getMongoModel<OrgSchemaType>(OrgCollectionName, OrgSchema);
5 changes: 5 additions & 0 deletions packages/service/support/permission/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { connectionMongo, getMongoModel } from '../../common/mongo';
import type { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { MemberGroupCollectionName } from './memberGroup/memberGroupSchema';
import { OrgCollectionName } from '@fastgpt/global/support/user/team/org/constant';
const { Schema } = connectionMongo;

export const ResourcePermissionCollectionName = 'resource_permissions';
Expand All @@ -23,6 +24,10 @@ export const ResourcePermissionSchema = new Schema({
type: Schema.Types.ObjectId,
ref: MemberGroupCollectionName
},
orgId: {
type: Schema.Types.ObjectId,
ref: OrgCollectionName
},
resourceType: {
type: String,
enum: Object.values(PerResourceTypeEnum),
Expand Down

0 comments on commit 44d0894

Please sign in to comment.