mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 21:13:50 +00:00
V4.8.18 feature (#3565)
* feat: org CRUD (#3380) * feat: add org schema * feat: org manage UI * feat: OrgInfoModal * feat: org tree view * feat: org management * fix: init root org * feat: org permission for app * feat: org support for dataset * fix: disable org role control * styles: opt type signatures * fix: remove unused permission * feat: delete org collaborator * perf: Team org ui (#3499) * perf: org ui * perf: org ui * feat: org auth for app & dataset (#3498) * feat: auth org resource permission * feat: org auth support for app & dataset * perf: org permission check (#3500) * i18n (#3501) * name * i18n * feat: support dataset changeOwner (#3483) * feat: support dataset changeOwner * chore: update dataset change owner api * feat: permission manage UI for org (#3503) * perf: password check;perf: image upload check;perf: sso login check (#3509) * perf: password check * perf: image upload check * perf: sso login check * force show update notification modal & fix login page text (#3512) * fix login page English text * update notification modal * perf: notify account (#3515) * perf(plugin): improve searXNG empty result handling and documentation (#3507) * perf(plugin): improve searXNG empty result handling and documentation * 修改了文档和代码部分无搜索的结果的反馈 * refactor: org pathId (#3516) * optimize payment process (#3517) * feat: support wecom sso (#3518) * feat: support wecom sso * chore: remove unused wecom js-sdk dependency * fix qrcode script (#3520) * fix qrcode script * i18n * perf: full text collection and search code;perf: rename function (#3519) * perf: full text collection and search code * perf: rename function * perf: notify modal * remove invalid code * perf: sso login * perf: pay process * 4.8.18 test (#3524) * perf: remove local token * perf: index * perf: file encoding;perf: leave team code;@c121914yu perf: full text search code (#3528) * perf: text encoding * perf: leave team code * perf: full text search code * fix: http status * perf: embedding search and vector avatar * perf: async read file (#3531) * refactor: team permission manager (#3535) * perf: classify org, group and member * refactor: team per manager * fix: missing functions * 4.8.18 test (#3543) * perf: login check * doc * perf: llm model config * perf: team clb config * fix: MemberModal UI (#3553) * fix: adapt MemberModal title and icon * fix: adapt member modal * fix: search input placeholder * fix: add button text * perf: org permission (#3556) * docs:用户答疑的官方文档补充 (#3540) * docs:用户答疑的官方文档补充 * 问题回答的内容修补 * share link random avatar (#3541) * share link random avatar * fix * delete unused code * share page avatar (#3558) * feat: init 4818 * share page avatar * feat: tmp upgrade code (#3559) * feat: tmp upgrade code * fulltext search test * update action * full text tmp code (#3561) * full text tmp code * fix: init * fix: init * remove tmp code * remove tmp code * 4818-alpha * 4.8.18 test (#3562) * full text tmp code * fix: init * upgrade code * account log * account log * perf: dockerfile * upgrade code * chore: update docs app template submission (#3564) --------- Co-authored-by: a.e. <49438478+I-Info@users.noreply.github.com> Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com> Co-authored-by: heheer <heheer@sealos.io> Co-authored-by: Jiangween <145003935+Jiangween@users.noreply.github.com>
This commit is contained in:
@@ -92,6 +92,7 @@ OutLinkSchema.virtual('associatedApp', {
|
||||
|
||||
try {
|
||||
OutLinkSchema.index({ shareId: -1 });
|
||||
OutLinkSchema.index({ teamId: 1, tmbId: 1, appId: 1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
43
packages/service/support/permission/auth/org.ts
Normal file
43
packages/service/support/permission/auth/org.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
|
||||
import { AuthModeType, AuthResponseType } from '../type';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
import { authUserPer } from '../user/auth';
|
||||
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
/*
|
||||
Team manager can control org
|
||||
*/
|
||||
export const authOrgMember = async ({
|
||||
orgIds,
|
||||
...props
|
||||
}: {
|
||||
orgIds: string | string[];
|
||||
} & AuthModeType): Promise<AuthResponseType> => {
|
||||
const result = await authUserPer({
|
||||
...props,
|
||||
per: ManagePermissionVal
|
||||
});
|
||||
const { teamId, tmbId, isRoot, tmb } = result;
|
||||
|
||||
if (isRoot) {
|
||||
return {
|
||||
teamId,
|
||||
tmbId,
|
||||
userId: result.userId,
|
||||
appId: result.appId,
|
||||
apikey: result.apikey,
|
||||
isRoot,
|
||||
authType: result.authType,
|
||||
permission: new TeamPermission({ isOwner: true })
|
||||
};
|
||||
}
|
||||
|
||||
if (tmb.permission.hasManagePer) {
|
||||
return {
|
||||
...result,
|
||||
permission: tmb.permission
|
||||
};
|
||||
}
|
||||
|
||||
return Promise.reject(TeamErrEnum.unAuthTeam);
|
||||
};
|
@@ -1,8 +1,8 @@
|
||||
import { MongoTeamMember } from '../../user/team/teamMemberSchema';
|
||||
import { checkTeamAIPoints } from '../teamLimit';
|
||||
import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
|
||||
import { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||
import { TeamSchema } from '@fastgpt/global/support/user/team/type';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
|
||||
export async function getUserChatInfoAndAuthTeamPoints(tmbId: string) {
|
||||
const tmb = await MongoTeamMember.findById(tmbId, 'userId teamId')
|
||||
@@ -18,7 +18,7 @@ export async function getUserChatInfoAndAuthTeamPoints(tmbId: string) {
|
||||
])
|
||||
.lean();
|
||||
|
||||
if (!tmb) return Promise.reject(UserErrEnum.unAuthUser);
|
||||
if (!tmb) return Promise.reject(TeamErrEnum.notUser);
|
||||
|
||||
await checkTeamAIPoints(tmb.team._id);
|
||||
|
||||
|
@@ -8,10 +8,7 @@ import { authOpenApiKey } from '../openapi/auth';
|
||||
import { FileTokenQuery } from '@fastgpt/global/common/file/type';
|
||||
import { MongoResourcePermission } from './schema';
|
||||
import { ClientSession } from 'mongoose';
|
||||
import {
|
||||
PermissionValueType,
|
||||
ResourcePermissionType
|
||||
} from '@fastgpt/global/support/permission/type';
|
||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import { bucketNameMap } from '@fastgpt/global/common/file/constants';
|
||||
import { addMinutes } from 'date-fns';
|
||||
import { getGroupsByTmbId } from './memberGroup/controllers';
|
||||
@@ -21,6 +18,8 @@ import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type';
|
||||
import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
|
||||
import { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||
import { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
|
||||
import { getOrgIdSetWithParentByTmbId } from './org/controllers';
|
||||
|
||||
/** get resource permission for a team member
|
||||
* If there is no permission for the team member, it will return undefined
|
||||
@@ -67,67 +66,44 @@ export const getResourcePermission = async ({
|
||||
}
|
||||
|
||||
// If there is no personal permission, get the group permission
|
||||
const groupIdList = (await getGroupsByTmbId({ tmbId, teamId })).map((item) => item._id);
|
||||
const [groupPers, orgPers] = await Promise.all([
|
||||
getGroupsByTmbId({ tmbId, teamId })
|
||||
.then((res) => res.map((item) => item._id))
|
||||
.then((groupIdList) =>
|
||||
MongoResourcePermission.find(
|
||||
{
|
||||
teamId,
|
||||
resourceType,
|
||||
groupId: {
|
||||
$in: groupIdList
|
||||
},
|
||||
resourceId
|
||||
},
|
||||
'permission'
|
||||
).lean()
|
||||
)
|
||||
.then((perList) => perList.map((item) => item.permission)),
|
||||
getOrgIdSetWithParentByTmbId({ tmbId, teamId })
|
||||
.then((item) => Array.from(item))
|
||||
.then((orgIds) =>
|
||||
MongoResourcePermission.find(
|
||||
{
|
||||
teamId,
|
||||
resourceType,
|
||||
orgId: {
|
||||
$in: Array.from(orgIds)
|
||||
},
|
||||
resourceId
|
||||
},
|
||||
'permission'
|
||||
).lean()
|
||||
)
|
||||
.then((perList) => perList.map((item) => item.permission))
|
||||
]);
|
||||
|
||||
if (groupIdList.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// get the maximum permission of the group
|
||||
const pers = (
|
||||
await MongoResourcePermission.find(
|
||||
{
|
||||
teamId,
|
||||
resourceType,
|
||||
groupId: {
|
||||
$in: groupIdList
|
||||
},
|
||||
resourceId
|
||||
},
|
||||
'permission'
|
||||
).lean()
|
||||
).map((item) => item.permission);
|
||||
|
||||
const groupPer = getGroupPer(pers);
|
||||
|
||||
return groupPer;
|
||||
return concatPer([...groupPers, ...orgPers]);
|
||||
};
|
||||
|
||||
/* 仅取 members 不取 groups */
|
||||
export async function getResourceAllClbs({
|
||||
resourceId,
|
||||
teamId,
|
||||
resourceType,
|
||||
session
|
||||
}: {
|
||||
teamId: string;
|
||||
session?: ClientSession;
|
||||
} & (
|
||||
| {
|
||||
resourceType: 'team';
|
||||
resourceId?: undefined;
|
||||
}
|
||||
| {
|
||||
resourceType: Omit<PerResourceTypeEnum, 'team'>;
|
||||
resourceId?: string | null;
|
||||
}
|
||||
)): Promise<ResourcePermissionType[]> {
|
||||
return MongoResourcePermission.find(
|
||||
{
|
||||
resourceType: resourceType,
|
||||
teamId: teamId,
|
||||
resourceId,
|
||||
groupId: {
|
||||
$exists: false
|
||||
}
|
||||
},
|
||||
null,
|
||||
{
|
||||
session
|
||||
}
|
||||
).lean();
|
||||
}
|
||||
|
||||
export async function getResourceClbsAndGroups({
|
||||
resourceId,
|
||||
resourceType,
|
||||
@@ -155,10 +131,17 @@ export const getClbsAndGroupsWithInfo = async ({
|
||||
resourceType,
|
||||
teamId
|
||||
}: {
|
||||
resourceId: ParentIdType;
|
||||
resourceType: Omit<`${PerResourceTypeEnum}`, 'team'>;
|
||||
teamId: string;
|
||||
}) =>
|
||||
} & (
|
||||
| {
|
||||
resourceId: ParentIdType;
|
||||
resourceType: Omit<`${PerResourceTypeEnum}`, 'team'>;
|
||||
}
|
||||
| {
|
||||
resourceType: 'team';
|
||||
resourceId?: undefined;
|
||||
}
|
||||
)) =>
|
||||
Promise.all([
|
||||
MongoResourcePermission.find({
|
||||
teamId,
|
||||
@@ -170,7 +153,7 @@ export const getClbsAndGroupsWithInfo = async ({
|
||||
})
|
||||
.populate<{ tmb: TeamMemberSchema & { user: UserModelSchema } }>({
|
||||
path: 'tmb',
|
||||
select: 'name userId',
|
||||
select: 'name userId role',
|
||||
populate: {
|
||||
path: 'user',
|
||||
select: 'avatar'
|
||||
@@ -186,6 +169,16 @@ export const getClbsAndGroupsWithInfo = async ({
|
||||
}
|
||||
})
|
||||
.populate<{ group: MemberGroupSchemaType }>('group', 'name avatar')
|
||||
.lean(),
|
||||
MongoResourcePermission.find({
|
||||
teamId,
|
||||
resourceId,
|
||||
resourceType,
|
||||
orgId: {
|
||||
$exists: true
|
||||
}
|
||||
})
|
||||
.populate<{ org: OrgSchemaType }>({ path: 'org', select: 'name avatar' })
|
||||
.lean()
|
||||
]);
|
||||
|
||||
@@ -196,6 +189,7 @@ export const delResourcePermission = ({
|
||||
session,
|
||||
tmbId,
|
||||
groupId,
|
||||
orgId,
|
||||
...props
|
||||
}: {
|
||||
resourceType: PerResourceTypeEnum;
|
||||
@@ -204,15 +198,18 @@ export const delResourcePermission = ({
|
||||
session?: ClientSession;
|
||||
tmbId?: string;
|
||||
groupId?: string;
|
||||
orgId?: string;
|
||||
}) => {
|
||||
// tmbId or groupId only one and not both
|
||||
if (!!tmbId === !!groupId) {
|
||||
// either tmbId or groupId or orgId must be provided
|
||||
if (!tmbId && !groupId && !orgId) {
|
||||
return Promise.reject(CommonErrEnum.missingParams);
|
||||
}
|
||||
|
||||
return MongoResourcePermission.deleteOne(
|
||||
{
|
||||
...(tmbId ? { tmbId } : {}),
|
||||
...(groupId ? { groupId } : {}),
|
||||
...(orgId ? { orgId } : {}),
|
||||
...props
|
||||
},
|
||||
{ session }
|
||||
@@ -250,7 +247,7 @@ export function authJWT(token: string) {
|
||||
}>((resolve, reject) => {
|
||||
const key = process.env.TOKEN_KEY as string;
|
||||
|
||||
jwt.verify(token, key, function (err, decoded: any) {
|
||||
jwt.verify(token, key, (err, decoded: any) => {
|
||||
if (err || !decoded?.userId) {
|
||||
reject(ERROR_ENUM.unAuthorization);
|
||||
return;
|
||||
@@ -436,7 +433,7 @@ export const authFileToken = (token?: string) =>
|
||||
}
|
||||
const key = (process.env.FILE_TOKEN_KEY as string) ?? 'filetoken';
|
||||
|
||||
jwt.verify(token, key, function (err, decoded: any) {
|
||||
jwt.verify(token, key, (err, decoded: any) => {
|
||||
if (err || !decoded.bucketName || !decoded?.teamId || !decoded?.fileId) {
|
||||
reject(ERROR_ENUM.unAuthFile);
|
||||
return;
|
||||
@@ -450,10 +447,10 @@ export const authFileToken = (token?: string) =>
|
||||
});
|
||||
});
|
||||
|
||||
export const getGroupPer = (groups: PermissionValueType[] = []) => {
|
||||
if (groups.length === 0) {
|
||||
export const concatPer = (perList: PermissionValueType[] = []) => {
|
||||
if (perList.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return new Permission().addPer(...groups).value;
|
||||
return new Permission().addPer(...perList).value;
|
||||
};
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { mongoSessionRun } from '../../common/mongo/sessionRun';
|
||||
import { MongoResourcePermission } from './schema';
|
||||
import { ClientSession, Model } from 'mongoose';
|
||||
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import type { ClientSession, Model } from 'mongoose';
|
||||
import type { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import type { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import { getResourceClbsAndGroups } from './controller';
|
||||
import { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||
import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
|
||||
export type SyncChildrenPermissionResourceType = {
|
||||
_id: string;
|
||||
@@ -18,6 +18,7 @@ export type UpdateCollaboratorItem = {
|
||||
} & RequireOnlyOne<{
|
||||
tmbId: string;
|
||||
groupId: string;
|
||||
orgId: string;
|
||||
}>;
|
||||
|
||||
// sync the permission to all children folders.
|
||||
@@ -161,7 +162,7 @@ export async function resumeInheritPermission({
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
Delete all the collaborators and then insert the new collaborators.
|
||||
*/
|
||||
export async function syncCollaborators({
|
||||
|
@@ -1,9 +1,6 @@
|
||||
import { MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type';
|
||||
import { MongoGroupMemberModel } from './groupMemberSchema';
|
||||
import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
|
||||
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { MongoResourcePermission } from '../schema';
|
||||
import { getGroupPer, parseHeaderCert } from '../controller';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { MongoMemberGroupModel } from './memberGroupSchema';
|
||||
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||
import { ClientSession } from 'mongoose';
|
||||
@@ -50,26 +47,32 @@ export const getTeamDefaultGroup = async ({
|
||||
export const getGroupsByTmbId = async ({
|
||||
tmbId,
|
||||
teamId,
|
||||
role
|
||||
role,
|
||||
session
|
||||
}: {
|
||||
tmbId: string;
|
||||
teamId: string;
|
||||
role?: `${GroupMemberRole}`[];
|
||||
session?: ClientSession;
|
||||
}) =>
|
||||
(
|
||||
await Promise.all([
|
||||
(
|
||||
await MongoGroupMemberModel.find({
|
||||
tmbId,
|
||||
groupId: {
|
||||
$exists: true
|
||||
await MongoGroupMemberModel.find(
|
||||
{
|
||||
tmbId,
|
||||
groupId: {
|
||||
$exists: true
|
||||
},
|
||||
...(role ? { role: { $in: role } } : {})
|
||||
},
|
||||
...(role ? { role: { $in: role } } : {})
|
||||
})
|
||||
undefined,
|
||||
{ session }
|
||||
)
|
||||
.populate<{ group: MemberGroupSchemaType }>('group')
|
||||
.lean()
|
||||
).map((item) => item.group),
|
||||
role ? [] : getTeamDefaultGroup({ teamId })
|
||||
role ? [] : getTeamDefaultGroup({ teamId, session })
|
||||
])
|
||||
).flat();
|
||||
|
||||
@@ -79,46 +82,6 @@ export const getGroupMembersByGroupId = async (groupId: string) => {
|
||||
}).lean();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get tmb's group permission: the maximum permission of the group
|
||||
* @param tmbId
|
||||
* @param resourceId
|
||||
* @param resourceType
|
||||
* @returns the maximum permission of the group
|
||||
*/
|
||||
export const getGroupPermission = async ({
|
||||
tmbId,
|
||||
resourceId,
|
||||
teamId,
|
||||
resourceType
|
||||
}: {
|
||||
tmbId: string;
|
||||
teamId: string;
|
||||
} & (
|
||||
| {
|
||||
resourceId?: undefined;
|
||||
resourceType: 'team';
|
||||
}
|
||||
| {
|
||||
resourceId: string;
|
||||
resourceType: Omit<PerResourceTypeEnum, 'team'>;
|
||||
}
|
||||
)) => {
|
||||
const groupIds = (await getGroupsByTmbId({ tmbId, teamId })).map((item) => item._id);
|
||||
const groupPermissions = (
|
||||
await MongoResourcePermission.find({
|
||||
groupId: {
|
||||
$in: groupIds
|
||||
},
|
||||
resourceType,
|
||||
resourceId,
|
||||
teamId
|
||||
})
|
||||
).map((item) => item.permission);
|
||||
|
||||
return getGroupPer(groupPermissions);
|
||||
};
|
||||
|
||||
// auth group member role
|
||||
export const authGroupMemberRole = async ({
|
||||
groupId,
|
||||
@@ -140,8 +103,12 @@ export const authGroupMemberRole = async ({
|
||||
tmbId
|
||||
};
|
||||
}
|
||||
const groupMember = await MongoGroupMemberModel.findOne({ groupId, tmbId });
|
||||
const tmb = await getTmbInfoByTmbId({ tmbId });
|
||||
const [groupMember, tmb] = await Promise.all([
|
||||
MongoGroupMemberModel.findOne({ groupId, tmbId }),
|
||||
getTmbInfoByTmbId({ tmbId })
|
||||
]);
|
||||
|
||||
// Team admin or role check
|
||||
if (tmb.permission.hasManagePer || (groupMember && role.includes(groupMember.role))) {
|
||||
return {
|
||||
...result,
|
||||
|
95
packages/service/support/permission/org/controllers.ts
Normal file
95
packages/service/support/permission/org/controllers.ts
Normal file
@@ -0,0 +1,95 @@
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
import type { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
|
||||
import type { ClientSession } from 'mongoose';
|
||||
import { MongoOrgModel } from './orgSchema';
|
||||
import { MongoOrgMemberModel } from './orgMemberSchema';
|
||||
import { getOrgChildrenPath } from '@fastgpt/global/support/user/team/org/constant';
|
||||
|
||||
export const getOrgsByTmbId = async ({ teamId, tmbId }: { teamId: string; tmbId: string }) =>
|
||||
MongoOrgMemberModel.find({ teamId, tmbId }, 'orgId').lean();
|
||||
|
||||
export const getOrgIdSetWithParentByTmbId = async ({
|
||||
teamId,
|
||||
tmbId
|
||||
}: {
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
}) => {
|
||||
const orgMembers = await MongoOrgMemberModel.find({ teamId, tmbId }, 'orgId').lean();
|
||||
|
||||
const orgIds = Array.from(new Set(orgMembers.map((item) => String(item.orgId))));
|
||||
const orgs = await MongoOrgModel.find({ _id: { $in: orgIds } }, 'path').lean();
|
||||
|
||||
const pathIdList = new Set<string>(
|
||||
orgs
|
||||
.map((org) => {
|
||||
const pathIdList = org.path.split('/').filter(Boolean);
|
||||
return pathIdList;
|
||||
})
|
||||
.flat()
|
||||
);
|
||||
const parentOrgs = await MongoOrgModel.find(
|
||||
{
|
||||
teamId,
|
||||
pathId: { $in: Array.from(pathIdList) }
|
||||
},
|
||||
'_id'
|
||||
).lean();
|
||||
const parentOrgIds = parentOrgs.map((item) => String(item._id));
|
||||
|
||||
return new Set([...orgIds, ...parentOrgIds]);
|
||||
};
|
||||
|
||||
export const getChildrenByOrg = async ({
|
||||
org,
|
||||
teamId,
|
||||
session
|
||||
}: {
|
||||
org: OrgSchemaType;
|
||||
teamId: string;
|
||||
session?: ClientSession;
|
||||
}) => {
|
||||
return MongoOrgModel.find(
|
||||
{ teamId, path: { $regex: `^${getOrgChildrenPath(org)}` } },
|
||||
undefined,
|
||||
{
|
||||
session
|
||||
}
|
||||
).lean();
|
||||
};
|
||||
|
||||
export const getOrgAndChildren = async ({
|
||||
orgId,
|
||||
teamId,
|
||||
session
|
||||
}: {
|
||||
orgId: string;
|
||||
teamId: string;
|
||||
session?: ClientSession;
|
||||
}) => {
|
||||
const org = await MongoOrgModel.findOne({ _id: orgId, teamId }, undefined, { session }).lean();
|
||||
if (!org) {
|
||||
return Promise.reject(TeamErrEnum.orgNotExist);
|
||||
}
|
||||
const children = await getChildrenByOrg({ org, teamId, session });
|
||||
return { org, children };
|
||||
};
|
||||
|
||||
export async function createRootOrg({
|
||||
teamId,
|
||||
session
|
||||
}: {
|
||||
teamId: string;
|
||||
session?: ClientSession;
|
||||
}) {
|
||||
return MongoOrgModel.create(
|
||||
[
|
||||
{
|
||||
teamId,
|
||||
name: 'ROOT',
|
||||
path: ''
|
||||
}
|
||||
],
|
||||
{ session }
|
||||
);
|
||||
}
|
65
packages/service/support/permission/org/orgMemberSchema.ts
Normal file
65
packages/service/support/permission/org/orgMemberSchema.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { OrgCollectionName } from '@fastgpt/global/support/user/team/org/constant';
|
||||
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
||||
import {
|
||||
TeamCollectionName,
|
||||
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({
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamCollectionName,
|
||||
required: true
|
||||
},
|
||||
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
|
||||
// }
|
||||
});
|
||||
|
||||
OrgMemberSchema.virtual('org', {
|
||||
ref: OrgCollectionName,
|
||||
localField: 'orgId',
|
||||
foreignField: '_id',
|
||||
justOne: true
|
||||
});
|
||||
|
||||
try {
|
||||
OrgMemberSchema.index(
|
||||
{
|
||||
teamId: 1,
|
||||
orgId: 1,
|
||||
tmbId: 1
|
||||
},
|
||||
{
|
||||
unique: true
|
||||
}
|
||||
);
|
||||
OrgMemberSchema.index({
|
||||
teamId: 1,
|
||||
tmbId: 1
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoOrgMemberModel = getMongoModel<OrgMemberSchemaType>(
|
||||
OrgMemberCollectionName,
|
||||
OrgMemberSchema
|
||||
);
|
77
packages/service/support/permission/org/orgSchema.ts
Normal file
77
packages/service/support/permission/org/orgSchema.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||
import { OrgCollectionName } from '@fastgpt/global/support/user/team/org/constant';
|
||||
import type { OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
|
||||
import { connectionMongo, getMongoModel } from '../../../common/mongo';
|
||||
import { OrgMemberCollectionName } from './orgMemberSchema';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
const { Schema } = connectionMongo;
|
||||
|
||||
export const OrgSchema = new Schema(
|
||||
{
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamCollectionName,
|
||||
required: true
|
||||
},
|
||||
pathId: {
|
||||
// path id, only used for path
|
||||
type: String,
|
||||
required: true,
|
||||
default: () => getNanoid()
|
||||
},
|
||||
path: {
|
||||
type: String,
|
||||
required: function (this: OrgSchemaType) {
|
||||
return typeof this.path !== 'string';
|
||||
} // allow empty string, but not null
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
avatar: String,
|
||||
description: String,
|
||||
updateTime: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
}
|
||||
},
|
||||
{
|
||||
// Auto update updateTime
|
||||
timestamps: {
|
||||
updatedAt: 'updateTime'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
OrgSchema.virtual('members', {
|
||||
ref: OrgMemberCollectionName,
|
||||
localField: '_id',
|
||||
foreignField: 'orgId'
|
||||
});
|
||||
// OrgSchema.virtual('permission', {
|
||||
// ref: ResourcePermissionCollectionName,
|
||||
// localField: '_id',
|
||||
// foreignField: 'orgId',
|
||||
// justOne: true
|
||||
// });
|
||||
|
||||
try {
|
||||
OrgSchema.index({
|
||||
teamId: 1,
|
||||
path: 1
|
||||
});
|
||||
OrgSchema.index(
|
||||
{
|
||||
teamId: 1,
|
||||
pathId: 1
|
||||
},
|
||||
{
|
||||
unique: true
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoOrgModel = getMongoModel<OrgSchemaType>(OrgCollectionName, OrgSchema);
|
@@ -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';
|
||||
@@ -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),
|
||||
@@ -51,6 +56,12 @@ ResourcePermissionSchema.virtual('group', {
|
||||
foreignField: '_id',
|
||||
justOne: true
|
||||
});
|
||||
ResourcePermissionSchema.virtual('org', {
|
||||
ref: OrgCollectionName,
|
||||
localField: 'orgId',
|
||||
foreignField: '_id',
|
||||
justOne: true
|
||||
});
|
||||
|
||||
try {
|
||||
ResourcePermissionSchema.index(
|
||||
@@ -70,6 +81,23 @@ try {
|
||||
}
|
||||
);
|
||||
|
||||
ResourcePermissionSchema.index(
|
||||
{
|
||||
resourceType: 1,
|
||||
teamId: 1,
|
||||
resourceId: 1,
|
||||
orgId: 1
|
||||
},
|
||||
{
|
||||
unique: true,
|
||||
partialFilterExpression: {
|
||||
orgId: {
|
||||
$exists: true
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
ResourcePermissionSchema.index(
|
||||
{
|
||||
resourceType: 1,
|
||||
@@ -87,6 +115,7 @@ try {
|
||||
}
|
||||
);
|
||||
|
||||
// Delete tmb permission
|
||||
ResourcePermissionSchema.index({
|
||||
resourceType: 1,
|
||||
teamId: 1,
|
||||
|
@@ -19,9 +19,7 @@ export const checkDatasetLimit = async ({
|
||||
if (!standardConstants) return;
|
||||
|
||||
if (usedDatasetSize + insertLen >= datasetMaxSize) {
|
||||
return Promise.reject(
|
||||
`您的知识库容量为: ${datasetMaxSize}组,已使用: ${usedDatasetSize}组,导入当前文件需要: ${insertLen}组,请增加知识库容量后导入。`
|
||||
);
|
||||
return Promise.reject(TeamErrEnum.datasetSizeNotEnough);
|
||||
}
|
||||
|
||||
if (usedPoints >= totalPoints) {
|
||||
|
@@ -3,22 +3,10 @@ const { Schema } = connectionMongo;
|
||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
||||
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||
import { UserStatusEnum, userStatusMap } from '@fastgpt/global/support/user/constant';
|
||||
import { getRandomUserAvatar } from '@fastgpt/global/support/user/utils';
|
||||
|
||||
export const userCollectionName = 'users';
|
||||
|
||||
const defaultAvatars = [
|
||||
'/imgs/avatar/RoyalBlueAvatar.svg',
|
||||
'/imgs/avatar/PurpleAvatar.svg',
|
||||
'/imgs/avatar/AdoraAvatar.svg',
|
||||
'/imgs/avatar/OrangeAvatar.svg',
|
||||
'/imgs/avatar/RedAvatar.svg',
|
||||
'/imgs/avatar/GrayModernAvatar.svg',
|
||||
'/imgs/avatar/TealAvatar.svg',
|
||||
'/imgs/avatar/GreenAvatar.svg',
|
||||
'/imgs/avatar/BrightBlueAvatar.svg',
|
||||
'/imgs/avatar/BlueAvatar.svg'
|
||||
];
|
||||
|
||||
const UserSchema = new Schema({
|
||||
status: {
|
||||
type: String,
|
||||
@@ -47,7 +35,7 @@ const UserSchema = new Schema({
|
||||
},
|
||||
avatar: {
|
||||
type: String,
|
||||
default: defaultAvatars[Math.floor(Math.random() * defaultAvatars.length)]
|
||||
default: () => getRandomUserAvatar()
|
||||
},
|
||||
|
||||
promotionRate: {
|
||||
@@ -78,11 +66,8 @@ const UserSchema = new Schema({
|
||||
});
|
||||
|
||||
try {
|
||||
// login
|
||||
UserSchema.index({ username: 1, password: 1 }, { background: true });
|
||||
|
||||
// Admin charts
|
||||
UserSchema.index({ createTime: -1 }, { background: true });
|
||||
UserSchema.index({ createTime: -1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
@@ -16,6 +16,8 @@ import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupS
|
||||
import { mongoSessionRun } from '../../../common/mongo/sessionRun';
|
||||
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||
import { getAIApi, openaiBaseUrl } from '../../../core/ai/config';
|
||||
import { createRootOrg } from '../../permission/org/controllers';
|
||||
import { refreshSourceAvatar } from '../../../common/file/image/controller';
|
||||
|
||||
async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemType> {
|
||||
const tmb = await MongoTeamMember.findOne(match).populate<{ team: TeamSchema }>('team').lean();
|
||||
@@ -32,6 +34,7 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
|
||||
return {
|
||||
userId: String(tmb.userId),
|
||||
teamId: String(tmb.teamId),
|
||||
teamAvatar: tmb.team.avatar,
|
||||
teamName: tmb.team.name,
|
||||
memberName: tmb.name,
|
||||
avatar: tmb.team.avatar,
|
||||
@@ -77,13 +80,11 @@ export async function createDefaultTeam({
|
||||
userId,
|
||||
teamName = 'My Team',
|
||||
avatar = '/icon/logo.svg',
|
||||
balance,
|
||||
session
|
||||
}: {
|
||||
userId: string;
|
||||
teamName?: string;
|
||||
avatar?: string;
|
||||
balance?: number;
|
||||
session: ClientSession;
|
||||
}) {
|
||||
// auth default team
|
||||
@@ -100,7 +101,6 @@ export async function createDefaultTeam({
|
||||
ownerId: userId,
|
||||
name: teamName,
|
||||
avatar,
|
||||
balance,
|
||||
createTime: new Date()
|
||||
}
|
||||
],
|
||||
@@ -132,15 +132,11 @@ export async function createDefaultTeam({
|
||||
],
|
||||
{ session }
|
||||
);
|
||||
console.log('create default team and group', userId);
|
||||
await createRootOrg({ teamId: tmb.teamId, session });
|
||||
console.log('create default team, group and root org', userId);
|
||||
return tmb;
|
||||
} else {
|
||||
console.log('default team exist', userId);
|
||||
await MongoTeam.findByIdAndUpdate(tmb.teamId, {
|
||||
$set: {
|
||||
...(balance !== undefined && { balance })
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +211,8 @@ export async function updateTeam({
|
||||
return obj;
|
||||
})();
|
||||
|
||||
await MongoTeam.findByIdAndUpdate(
|
||||
// This is where we get the old team
|
||||
const team = await MongoTeam.findByIdAndUpdate(
|
||||
teamId,
|
||||
{
|
||||
$set: {
|
||||
@@ -241,6 +238,8 @@ export async function updateTeam({
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
|
||||
await refreshSourceAvatar(avatar, team?.avatar, session);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -23,10 +23,6 @@ const TeamMemberSchema = new Schema({
|
||||
type: String,
|
||||
default: 'Member'
|
||||
},
|
||||
role: {
|
||||
type: String
|
||||
// enum: Object.keys(TeamMemberRoleMap) // disable enum validation for old data
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
enum: Object.keys(TeamMemberStatusMap)
|
||||
@@ -38,6 +34,12 @@ const TeamMemberSchema = new Schema({
|
||||
defaultTeam: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// Abandoned
|
||||
role: {
|
||||
type: String
|
||||
// enum: Object.keys(TeamMemberRoleMap) // disable enum validation for old data
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -21,10 +21,7 @@ const TeamSchema = new Schema({
|
||||
type: Date,
|
||||
default: () => Date.now()
|
||||
},
|
||||
balance: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
balance: Number,
|
||||
teamDomain: {
|
||||
type: String
|
||||
},
|
||||
|
@@ -61,11 +61,11 @@ const UsageSchema = new Schema({
|
||||
});
|
||||
|
||||
try {
|
||||
UsageSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 }, { background: true });
|
||||
UsageSchema.index({ teamId: 1, tmbId: 1, source: 1, time: -1 });
|
||||
// timer task. clear dead team
|
||||
// UsageSchema.index({ teamId: 1, time: -1 }, { background: true });
|
||||
// UsageSchema.index({ teamId: 1, time: -1 });
|
||||
|
||||
UsageSchema.index({ time: 1 }, { background: true, expireAfterSeconds: 360 * 24 * 60 * 60 });
|
||||
UsageSchema.index({ time: 1 }, { expireAfterSeconds: 360 * 24 * 60 * 60 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
Reference in New Issue
Block a user