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:
@@ -1,7 +1,8 @@
|
||||
import { MongoMemoryServer } from 'mongodb-memory-server';
|
||||
import { MongoMemoryReplSet } from 'mongodb-memory-server';
|
||||
import mongoose from 'mongoose';
|
||||
import { MockParseHeaderCert } from '@/test/utils';
|
||||
import { initMockData } from './db/init';
|
||||
import { parseHeaderCertMock } from '@/test/utils';
|
||||
import { initMockData, root } from './db/init';
|
||||
import { faker } from '@faker-js/faker/locale/zh_CN';
|
||||
|
||||
jest.mock('nanoid', () => {
|
||||
return {
|
||||
@@ -13,24 +14,40 @@ jest.mock('@fastgpt/global/common/string/tools', () => {
|
||||
return {
|
||||
hashStr(str: string) {
|
||||
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', () => {
|
||||
return {
|
||||
parseHeaderCert: MockParseHeaderCert,
|
||||
getResourcePermission: jest.requireActual('@fastgpt/service/support/permission/controller')
|
||||
.getResourcePermission,
|
||||
getResourceAllClbs: jest.requireActual('@fastgpt/service/support/permission/controller')
|
||||
.getResourceAllClbs
|
||||
};
|
||||
});
|
||||
jest.setMock(
|
||||
'@fastgpt/service/support/permission/controller',
|
||||
(() => {
|
||||
const origin = jest.requireActual<
|
||||
typeof import('@fastgpt/service/support/permission/controller')
|
||||
>('@fastgpt/service/support/permission/controller');
|
||||
|
||||
const parse = jest.createMockFromModule('@fastgpt/service/support/permission/controller') as any;
|
||||
parse.parseHeaderCert = MockParseHeaderCert;
|
||||
return {
|
||||
...origin,
|
||||
parseHeaderCert: parseHeaderCertMock
|
||||
};
|
||||
})()
|
||||
);
|
||||
|
||||
jest.mock('@/service/middleware/entry', () => {
|
||||
return {
|
||||
@@ -59,11 +76,30 @@ jest.mock('@/service/middleware/entry', () => {
|
||||
beforeAll(async () => {
|
||||
// 新建一个内存数据库,然后让 mongoose 连接这个数据库
|
||||
if (!global.mongod || !global.mongodb) {
|
||||
const mongod = await MongoMemoryServer.create();
|
||||
global.mongod = mongod;
|
||||
const replSet = new MongoMemoryReplSet({
|
||||
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;
|
||||
|
||||
await global.mongodb.connect(mongod.getUri(), {
|
||||
await global.mongodb.connect(uri, {
|
||||
dbName: 'fastgpt_test',
|
||||
bufferCommands: true,
|
||||
maxConnecting: 50,
|
||||
maxPoolSize: 50,
|
||||
@@ -77,6 +113,7 @@ beforeAll(async () => {
|
||||
});
|
||||
|
||||
await initMockData();
|
||||
console.log(root);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -84,6 +121,9 @@ afterAll(async () => {
|
||||
if (global.mongodb) {
|
||||
await global.mongodb.disconnect();
|
||||
}
|
||||
if (global.replSet) {
|
||||
await global.replSet.stop();
|
||||
}
|
||||
if (global.mongod) {
|
||||
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 { MongoMemberGroupModel } from '@fastgpt/service/support/permission/memberGroup/memberGroupSchema';
|
||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
||||
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
||||
import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
|
||||
@@ -13,37 +14,43 @@ export const root = {
|
||||
};
|
||||
|
||||
export const initMockData = async () => {
|
||||
const initRootUser = async () => {
|
||||
// init root user
|
||||
const rootUser = await MongoUser.create({
|
||||
const [rootUser] = await MongoUser.create([
|
||||
{
|
||||
username: 'root',
|
||||
password: '123456'
|
||||
});
|
||||
|
||||
const rootTeam = await MongoTeam.create({
|
||||
name: 'root-default-team',
|
||||
ownerId: rootUser._id
|
||||
});
|
||||
|
||||
const rootTeamMember = await MongoTeamMember.create({
|
||||
}
|
||||
]);
|
||||
root.uid = String(rootUser._id);
|
||||
const [rootTeam] = await MongoTeam.create([
|
||||
{
|
||||
name: 'root Team'
|
||||
}
|
||||
]);
|
||||
root.teamId = String(rootTeam._id);
|
||||
const [rootTmb] = await MongoTeamMember.create([
|
||||
{
|
||||
teamId: rootTeam._id,
|
||||
name: 'owner',
|
||||
role: 'owner',
|
||||
userId: rootUser._id,
|
||||
name: 'root-default-team-member',
|
||||
status: 'active',
|
||||
role: TeamMemberRoleEnum.owner
|
||||
});
|
||||
const rootApp = await MongoApp.create({
|
||||
name: 'root-default-app',
|
||||
status: 'active'
|
||||
}
|
||||
]);
|
||||
root.tmbId = String(rootTmb._id);
|
||||
await MongoMemberGroupModel.create([
|
||||
{
|
||||
name: DefaultGroupName,
|
||||
teamId: rootTeam._id
|
||||
}
|
||||
]);
|
||||
|
||||
const [rootApp] = await MongoApp.create([
|
||||
{
|
||||
name: 'root Test App',
|
||||
teamId: rootTeam._id,
|
||||
tmbId: rootTeam._id,
|
||||
type: 'advanced'
|
||||
});
|
||||
tmbId: rootTmb._id
|
||||
}
|
||||
]);
|
||||
|
||||
root.uid = rootUser._id;
|
||||
root.tmbId = rootTeamMember._id;
|
||||
root.teamId = rootTeam._id;
|
||||
root.appId = rootApp._id;
|
||||
};
|
||||
|
||||
await initRootUser();
|
||||
root.appId = String(rootApp._id);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
import { MongoMemoryServer } from 'mongodb-memory-server';
|
||||
import type { MongoMemoryReplSet, MongoMemoryServer } from 'mongodb-memory-server';
|
||||
declare global {
|
||||
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 { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||
import {
|
||||
OwnerPermissionVal,
|
||||
PerResourceTypeEnum,
|
||||
WritePermissionVal
|
||||
} 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 { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
||||
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 { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
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 = {
|
||||
parentId?: ParentIdType;
|
||||
@@ -31,20 +33,21 @@ async function handler(req: ApiRequestProps<CreateAppFolderBody>) {
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { teamId, tmbId } = await authUserPer({ req, authToken: true, per: WritePermissionVal });
|
||||
const parentApp = await (async () => {
|
||||
if (parentId) {
|
||||
// if it is not a root folder
|
||||
return (
|
||||
await authApp({
|
||||
req,
|
||||
appId: parentId,
|
||||
per: WritePermissionVal,
|
||||
authToken: true
|
||||
})
|
||||
).app; // check the parent folder permission
|
||||
}
|
||||
})();
|
||||
const { teamId, tmbId } = await authUserPer({
|
||||
req,
|
||||
authToken: true,
|
||||
per: TeamWritePermissionVal
|
||||
});
|
||||
|
||||
if (parentId) {
|
||||
// if it is not a root folder
|
||||
await authApp({
|
||||
req,
|
||||
appId: parentId,
|
||||
per: WritePermissionVal,
|
||||
authToken: true
|
||||
});
|
||||
}
|
||||
|
||||
// Create app
|
||||
await mongoSessionRun(async (session) => {
|
||||
@@ -55,13 +58,11 @@ async function handler(req: ApiRequestProps<CreateAppFolderBody>) {
|
||||
intro,
|
||||
teamId,
|
||||
tmbId,
|
||||
type: AppTypeEnum.folder,
|
||||
// inheritPermission: !!parentApp ? true : false,
|
||||
defaultPermission: !!parentApp ? parentApp.defaultPermission : AppDefaultPermissionVal
|
||||
type: AppTypeEnum.folder
|
||||
});
|
||||
|
||||
if (parentId) {
|
||||
const parentClbs = await getResourceAllClbs({
|
||||
const parentClbsAndGroups = await getResourceClbsAndGroups({
|
||||
teamId,
|
||||
resourceId: parentId,
|
||||
resourceType: PerResourceTypeEnum.app,
|
||||
@@ -72,9 +73,25 @@ async function handler(req: ApiRequestProps<CreateAppFolderBody>) {
|
||||
resourceType: PerResourceTypeEnum.app,
|
||||
teamId,
|
||||
resourceId: app._id,
|
||||
collaborators: parentClbs,
|
||||
collaborators: parentClbsAndGroups,
|
||||
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 { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||
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 = {
|
||||
parentId?: ParentIdType;
|
||||
@@ -31,7 +33,7 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
|
||||
app: ParentApp,
|
||||
tmbId,
|
||||
teamId,
|
||||
permission: tmbPer
|
||||
permission: myPer
|
||||
} = await (async () => {
|
||||
if (parentId) {
|
||||
return await authApp({
|
||||
@@ -87,10 +89,17 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
|
||||
})();
|
||||
|
||||
/* 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(
|
||||
findAppsQuery,
|
||||
'_id parentId avatar type name intro tmbId updateTime pluginData defaultPermission inheritPermission'
|
||||
'_id parentId avatar type name intro tmbId updateTime pluginData inheritPermission'
|
||||
)
|
||||
.sort({
|
||||
updateTime: -1
|
||||
@@ -98,41 +107,67 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
|
||||
.limit(searchKey ? 20 : 1000)
|
||||
.lean(),
|
||||
MongoResourcePermission.find({
|
||||
resourceType: PerResourceTypeEnum.app,
|
||||
teamId,
|
||||
tmbId
|
||||
$and: [
|
||||
{
|
||||
resourceType: PerResourceTypeEnum.app,
|
||||
teamId,
|
||||
resourceId: {
|
||||
$exists: true
|
||||
}
|
||||
},
|
||||
{ $or: [{ tmbId }, { groupId: { $in: myGroupIds } }] }
|
||||
]
|
||||
}).lean()
|
||||
]);
|
||||
|
||||
const filterApps = myApps
|
||||
.map((app) => {
|
||||
const Per = (() => {
|
||||
const { Per, privateApp } = (() => {
|
||||
// Inherit app
|
||||
if (app.inheritPermission && ParentApp && !AppFolderTypeList.includes(app.type)) {
|
||||
// get its parent's permission as its permission
|
||||
app.defaultPermission = ParentApp.defaultPermission;
|
||||
const perVal = rpList.find(
|
||||
(item) => String(item.resourceId) === String(ParentApp._id)
|
||||
const tmbPer = perList.find(
|
||||
(item) => String(item.resourceId) === String(ParentApp._id) && !!item.tmbId
|
||||
)?.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({
|
||||
per: perVal ?? app.defaultPermission,
|
||||
isOwner: String(app.tmbId) === String(tmbId) || tmbPer.isOwner
|
||||
});
|
||||
return {
|
||||
Per: new AppPermission({
|
||||
per: tmbPer ?? groupPer ?? AppDefaultPermissionVal,
|
||||
isOwner: String(app.tmbId) === String(tmbId) || myPer.isOwner
|
||||
}),
|
||||
privateApp: !tmbPer && !groupPer
|
||||
};
|
||||
} else {
|
||||
const perVal = rpList.find(
|
||||
(item) => String(item.resourceId) === String(app._id)
|
||||
const tmbPer = perList.find(
|
||||
(item) => String(item.resourceId) === String(app._id) && !!item.tmbId
|
||||
)?.permission;
|
||||
return new AppPermission({
|
||||
per: perVal ?? app.defaultPermission,
|
||||
isOwner: String(app.tmbId) === String(tmbId) || tmbPer.isOwner
|
||||
});
|
||||
const group = perList.filter(
|
||||
(item) =>
|
||||
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 {
|
||||
...app,
|
||||
permission: Per
|
||||
permission: Per,
|
||||
privateApp: privateApp
|
||||
};
|
||||
})
|
||||
.filter((app) => app.permission.hasReadPer);
|
||||
@@ -148,9 +183,9 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
|
||||
intro: app.intro,
|
||||
updateTime: app.updateTime,
|
||||
permission: app.permission,
|
||||
defaultPermission: app.defaultPermission || AppDefaultPermissionVal,
|
||||
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 {
|
||||
ManagePermissionVal,
|
||||
PerResourceTypeEnum,
|
||||
ReadPermissionVal,
|
||||
WritePermissionVal
|
||||
} from '@fastgpt/global/support/permission/constant';
|
||||
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
||||
@@ -18,62 +19,78 @@ import {
|
||||
import { AppFolderTypeList } from '@fastgpt/global/core/app/constants';
|
||||
import { ClientSession } from 'mongoose';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import { getResourceAllClbs } from '@fastgpt/service/support/permission/controller';
|
||||
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
|
||||
import { getResourceClbsAndGroups } from '@fastgpt/service/support/permission/controller';
|
||||
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||
import { TeamWritePermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||
|
||||
/*
|
||||
修改默认权限
|
||||
1. 继承态目录:关闭继承态,修改权限,同步子目录默认权限
|
||||
2. 继承态资源:关闭继承态,修改权限, 复制父级协作者。
|
||||
3. 非继承目录:修改权限,同步子目录默认权限
|
||||
4. 非继承资源:修改权限
|
||||
export type AppUpdateQuery = {
|
||||
appId: string;
|
||||
};
|
||||
|
||||
移动
|
||||
1. 继承态目录:改 parentId, 修改成父的默认权限,同步子目录默认权限和协作者
|
||||
2. 继承态资源:改 parentId
|
||||
3. 非继承:改 parentId
|
||||
*/
|
||||
export type AppUpdateBody = AppUpdateParams;
|
||||
|
||||
async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>) {
|
||||
const {
|
||||
parentId,
|
||||
name,
|
||||
avatar,
|
||||
type,
|
||||
intro,
|
||||
nodes,
|
||||
edges,
|
||||
chatConfig,
|
||||
teamTags,
|
||||
defaultPermission
|
||||
} = req.body as AppUpdateParams;
|
||||
// 更新应用接口
|
||||
// 包括如下功能:
|
||||
// 1. 更新应用的信息(包括名称,类型,头像,介绍等)
|
||||
// 2. 更新应用的编排信息
|
||||
// 3. 移动应用
|
||||
// 操作权限:
|
||||
// 1. 更新信息和工作流编排需要有应用的写权限
|
||||
// 2. 移动应用需要有
|
||||
// (1) 父目录的管理权限
|
||||
// (2) 目标目录的管理权限
|
||||
// (3) 如果从根目录移动或移动到根目录,需要有团队的应用创建权限
|
||||
async function handler(req: ApiRequestProps<AppUpdateBody, AppUpdateQuery>) {
|
||||
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) {
|
||||
Promise.reject(CommonErrEnum.missingParams);
|
||||
}
|
||||
const isMove = parentId !== undefined;
|
||||
|
||||
const { app } = await (async () => {
|
||||
if (defaultPermission !== undefined) {
|
||||
// if defaultPermission or inheritPermission is set, then need manage permission
|
||||
return authApp({ req, authToken: true, appId, per: ManagePermissionVal });
|
||||
} else {
|
||||
return authApp({ req, authToken: true, appId, per: WritePermissionVal });
|
||||
// this step is to get the app and its permission, and we will check the permission manually for
|
||||
// different cases
|
||||
const { app, permission } = await authApp({
|
||||
req,
|
||||
authToken: true,
|
||||
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
|
||||
// 1. dataset search limit, less than model quoteMaxToken
|
||||
const isDefaultPermissionChanged =
|
||||
defaultPermission !== undefined && defaultPermission !== app.defaultPermission;
|
||||
const isFolder = AppFolderTypeList.includes(app.type);
|
||||
|
||||
const onUpdate = async (
|
||||
session?: ClientSession,
|
||||
updatedDefaultPermission?: PermissionValueType
|
||||
) => {
|
||||
const onUpdate = async (session?: ClientSession) => {
|
||||
// format nodes data
|
||||
// 1. dataset search limit, less than model quoteMaxToken
|
||||
const { nodes: formatNodes } = beforeUpdateAppFormat({ nodes });
|
||||
|
||||
return MongoApp.findByIdAndUpdate(
|
||||
@@ -84,12 +101,6 @@ async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>)
|
||||
...(type && { type }),
|
||||
...(avatar && { avatar }),
|
||||
...(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 }),
|
||||
...(formatNodes && {
|
||||
modules: formatNodes
|
||||
@@ -97,34 +108,19 @@ async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>)
|
||||
...(edges && {
|
||||
edges
|
||||
}),
|
||||
...(chatConfig && { chatConfig })
|
||||
...(chatConfig && { chatConfig }),
|
||||
...(isMove && { inheritPermission: true })
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
};
|
||||
|
||||
// Move
|
||||
if (parentId !== undefined) {
|
||||
if (isMove) {
|
||||
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
|
||||
if (isFolder && app.inheritPermission) {
|
||||
const parentClbs = await getResourceAllClbs({
|
||||
if (AppFolderTypeList.includes(app.type)) {
|
||||
const parentClbsAndGroups = await getResourceClbsAndGroups({
|
||||
teamId: app.teamId,
|
||||
resourceId: parentId,
|
||||
resourceType: PerResourceTypeEnum.app,
|
||||
@@ -134,7 +130,7 @@ async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>)
|
||||
await syncCollaborators({
|
||||
resourceId: app._id,
|
||||
resourceType: PerResourceTypeEnum.app,
|
||||
collaborators: parentClbs,
|
||||
collaborators: parentClbsAndGroups,
|
||||
session,
|
||||
teamId: app.teamId
|
||||
});
|
||||
@@ -144,53 +140,12 @@ async function handler(req: ApiRequestProps<AppUpdateParams, { appId: string }>)
|
||||
resourceType: PerResourceTypeEnum.app,
|
||||
resourceModel: MongoApp,
|
||||
folderTypeList: AppFolderTypeList,
|
||||
defaultPermission: parentDefaultPermission,
|
||||
collaborators: parentClbs,
|
||||
collaborators: parentClbsAndGroups,
|
||||
session
|
||||
});
|
||||
|
||||
return onUpdate(session, parentDefaultPermission);
|
||||
}
|
||||
|
||||
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 {
|
||||
return onUpdate();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { NextApiRequest } from 'next';
|
||||
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
|
||||
import { getVectorModel } from '@fastgpt/service/core/ai/model';
|
||||
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 {
|
||||
PerResourceTypeEnum,
|
||||
@@ -11,6 +10,9 @@ import {
|
||||
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
|
||||
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
|
||||
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 */
|
||||
async function handler(req: NextApiRequest): Promise<DatasetSimpleItemType[]> {
|
||||
@@ -25,7 +27,14 @@ async function handler(req: NextApiRequest): Promise<DatasetSimpleItemType[]> {
|
||||
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({
|
||||
teamId
|
||||
})
|
||||
@@ -34,39 +43,59 @@ async function handler(req: NextApiRequest): Promise<DatasetSimpleItemType[]> {
|
||||
})
|
||||
.lean(),
|
||||
MongoResourcePermission.find({
|
||||
resourceType: PerResourceTypeEnum.dataset,
|
||||
teamId,
|
||||
tmbId
|
||||
$and: [
|
||||
{
|
||||
resourceType: PerResourceTypeEnum.dataset,
|
||||
teamId,
|
||||
resourceId: {
|
||||
$exists: true
|
||||
}
|
||||
},
|
||||
{ $or: [{ tmbId }, { groupId: { $in: myGroupIds } }] }
|
||||
]
|
||||
}).lean()
|
||||
]);
|
||||
|
||||
const filterDatasets = myDatasets
|
||||
.map((dataset) => {
|
||||
const perVal = (() => {
|
||||
const perVal = rpList.find(
|
||||
(item) => String(item.resourceId) === String(dataset._id)
|
||||
)?.permission;
|
||||
if (perVal) {
|
||||
return perVal;
|
||||
}
|
||||
const parentDataset = myDatasets.find(
|
||||
(item) => String(item._id) === String(dataset.parentId)
|
||||
);
|
||||
|
||||
if (dataset.inheritPermission && dataset.parentId) {
|
||||
const parentDataset = myDatasets.find(
|
||||
(item) => String(item._id) === String(dataset.parentId)
|
||||
if (dataset.inheritPermission && dataset.parentId && parentDataset) {
|
||||
const tmbPer = perList.find(
|
||||
(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) {
|
||||
const parentPerVal =
|
||||
rpList.find((item) => String(item.resourceId) === String(parentDataset._id))
|
||||
?.permission ?? parentDataset.defaultPermission;
|
||||
if (parentPerVal) {
|
||||
return parentPerVal;
|
||||
}
|
||||
}
|
||||
return tmbPer ?? groupPer ?? DatasetDefaultPermissionVal;
|
||||
} else {
|
||||
const tmbPer = perList.find(
|
||||
(item) => String(item.resourceId) === String(dataset._id) && !!item.tmbId
|
||||
)?.permission;
|
||||
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({
|
||||
per: perVal ?? dataset.defaultPermission,
|
||||
per: perVal ?? DatasetDefaultPermissionVal,
|
||||
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 { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||
import {
|
||||
OwnerPermissionVal,
|
||||
PerResourceTypeEnum,
|
||||
WritePermissionVal
|
||||
} 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 { FolderImgUrl } from '@fastgpt/global/common/file/image/constants';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
||||
import { getResourceAllClbs } from '@fastgpt/service/support/permission/controller';
|
||||
import { getResourceClbsAndGroups } from '@fastgpt/service/support/permission/controller';
|
||||
import { syncCollaborators } from '@fastgpt/service/support/permission/inheritPermission';
|
||||
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
|
||||
export type DatasetFolderCreateQuery = {};
|
||||
export type DatasetFolderCreateBody = {
|
||||
parentId?: string;
|
||||
@@ -38,35 +39,28 @@ async function handler(
|
||||
authToken: true
|
||||
});
|
||||
|
||||
const parentFolder = await (async () => {
|
||||
if (parentId) {
|
||||
return (
|
||||
await authDataset({
|
||||
datasetId: parentId,
|
||||
per: WritePermissionVal,
|
||||
req,
|
||||
authToken: true
|
||||
})
|
||||
).dataset;
|
||||
}
|
||||
})();
|
||||
if (parentId) {
|
||||
await authDataset({
|
||||
datasetId: parentId,
|
||||
per: WritePermissionVal,
|
||||
req,
|
||||
authToken: true
|
||||
});
|
||||
}
|
||||
|
||||
await mongoSessionRun(async (session) => {
|
||||
const app = await MongoDataset.create({
|
||||
const dataset = await MongoDataset.create({
|
||||
...parseParentIdInMongo(parentId),
|
||||
avatar: FolderImgUrl,
|
||||
name,
|
||||
intro,
|
||||
teamId,
|
||||
tmbId,
|
||||
type: DatasetTypeEnum.folder,
|
||||
defaultPermission: !!parentFolder
|
||||
? parentFolder.defaultPermission
|
||||
: DatasetDefaultPermissionVal
|
||||
type: DatasetTypeEnum.folder
|
||||
});
|
||||
|
||||
if (parentId) {
|
||||
const parentClbs = await getResourceAllClbs({
|
||||
const parentClbsAndGroups = await getResourceClbsAndGroups({
|
||||
teamId,
|
||||
resourceId: parentId,
|
||||
resourceType: PerResourceTypeEnum.dataset,
|
||||
@@ -76,11 +70,26 @@ async function handler(
|
||||
await syncCollaborators({
|
||||
resourceType: PerResourceTypeEnum.dataset,
|
||||
teamId,
|
||||
resourceId: app._id,
|
||||
collaborators: parentClbs,
|
||||
resourceId: dataset._id,
|
||||
collaborators: parentClbsAndGroups,
|
||||
session
|
||||
});
|
||||
}
|
||||
|
||||
if (!parentId) {
|
||||
await MongoResourcePermission.create(
|
||||
[
|
||||
{
|
||||
resourceType: PerResourceTypeEnum.dataset,
|
||||
teamId,
|
||||
resourceId: dataset._id,
|
||||
tmbId,
|
||||
permission: OwnerPermissionVal
|
||||
}
|
||||
],
|
||||
{ session }
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return {};
|
||||
|
||||
@@ -16,6 +16,8 @@ import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils'
|
||||
import { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
|
||||
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 = {
|
||||
parentId: ParentIdType;
|
||||
@@ -30,7 +32,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
|
||||
dataset: parentDataset,
|
||||
teamId,
|
||||
tmbId,
|
||||
permission: tmbPer
|
||||
permission: myPer
|
||||
} = await (async () => {
|
||||
if (parentId) {
|
||||
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)
|
||||
.sort({
|
||||
updateTime: -1
|
||||
})
|
||||
.lean(),
|
||||
MongoResourcePermission.find({
|
||||
resourceType: PerResourceTypeEnum.dataset,
|
||||
teamId,
|
||||
tmbId
|
||||
$and: [
|
||||
{
|
||||
resourceType: PerResourceTypeEnum.dataset,
|
||||
teamId,
|
||||
resourceId: {
|
||||
$exists: true
|
||||
}
|
||||
},
|
||||
{ $or: [{ tmbId }, { groupId: { $in: myGroupIds } }] }
|
||||
]
|
||||
}).lean()
|
||||
]);
|
||||
|
||||
const filterDatasets = myDatasets
|
||||
.map((dataset) => {
|
||||
const Per = (() => {
|
||||
const { Per, privateDataset } = (() => {
|
||||
// inherit
|
||||
if (dataset.inheritPermission && parentDataset && dataset.type !== DatasetTypeEnum.folder) {
|
||||
dataset.defaultPermission = parentDataset.defaultPermission;
|
||||
const perVal = rpList.find(
|
||||
(item) => String(item.resourceId) === String(parentDataset._id)
|
||||
const tmbPer = perList.find(
|
||||
(item) => String(item.resourceId) === String(parentDataset._id) && !!item.tmbId
|
||||
)?.permission;
|
||||
return new DatasetPermission({
|
||||
per: perVal ?? parentDataset.defaultPermission,
|
||||
isOwner: String(parentDataset.tmbId) === tmbId || tmbPer.isOwner
|
||||
});
|
||||
const groupPer = getGroupPer(
|
||||
perList
|
||||
.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 {
|
||||
const perVal = rpList.find(
|
||||
(item) => String(item.resourceId) === String(dataset._id)
|
||||
const tmbPer = perList.find(
|
||||
(item) =>
|
||||
String(item.resourceId) === String(dataset._id) && !!item.tmbId && !!item.permission
|
||||
)?.permission;
|
||||
return new DatasetPermission({
|
||||
per: perVal ?? dataset.defaultPermission,
|
||||
isOwner: String(dataset.tmbId) === tmbId || tmbPer.isOwner
|
||||
});
|
||||
const groupPer = getGroupPer(
|
||||
perList
|
||||
.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 {
|
||||
...dataset,
|
||||
permission: Per
|
||||
permission: Per,
|
||||
privateDataset
|
||||
};
|
||||
})
|
||||
.filter((app) => app.permission.hasReadPer);
|
||||
@@ -127,10 +169,10 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
|
||||
type: item.type,
|
||||
permission: item.permission,
|
||||
vectorModel: getVectorModel(item.vectorModel),
|
||||
defaultPermission: item.defaultPermission ?? DatasetDefaultPermissionVal,
|
||||
inheritPermission: item.inheritPermission,
|
||||
tmbId: item.tmbId,
|
||||
updateTime: item.updateTime
|
||||
updateTime: item.updateTime,
|
||||
private: item.privateDataset
|
||||
}))
|
||||
);
|
||||
|
||||
|
||||
@@ -5,63 +5,86 @@ import { NextAPI } from '@/service/middleware/entry';
|
||||
import {
|
||||
ManagePermissionVal,
|
||||
PerResourceTypeEnum,
|
||||
WritePermissionVal
|
||||
ReadPermissionVal
|
||||
} from '@fastgpt/global/support/permission/constant';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { ClientSession } from 'mongoose';
|
||||
import { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
||||
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
|
||||
import { DatasetSchemaType } from '@fastgpt/global/core/dataset/type';
|
||||
import { getResourceAllClbs } from '@fastgpt/service/support/permission/controller';
|
||||
import { getResourceClbsAndGroups } from '@fastgpt/service/support/permission/controller';
|
||||
import {
|
||||
syncChildrenPermission,
|
||||
syncCollaborators
|
||||
} 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 DatasetUpdateResponse = any;
|
||||
|
||||
// 更新知识库接口
|
||||
// 包括如下功能:
|
||||
// 1. 更新应用的信息(包括名称,类型,头像,介绍等)
|
||||
// 2. 更新数据库的配置信息
|
||||
// 3. 移动知识库
|
||||
// 操作权限:
|
||||
// 1. 更新信息和配置编排需要有知识库的写权限
|
||||
// 2. 移动应用需要有
|
||||
// (1) 父目录的管理权限
|
||||
// (2) 目标目录的管理权限
|
||||
// (3) 如果从根目录移动或移动到根目录,需要有团队的应用创建权限
|
||||
async function handler(
|
||||
req: ApiRequestProps<DatasetUpdateBody, DatasetUpdateQuery>,
|
||||
_res: ApiResponseType<any>
|
||||
): Promise<DatasetUpdateResponse> {
|
||||
const {
|
||||
id,
|
||||
parentId,
|
||||
name,
|
||||
avatar,
|
||||
intro,
|
||||
agentModel,
|
||||
websiteConfig,
|
||||
externalReadUrl,
|
||||
defaultPermission,
|
||||
status
|
||||
} = req.body;
|
||||
const { id, parentId, name, avatar, intro, agentModel, websiteConfig, externalReadUrl, status } =
|
||||
req.body;
|
||||
|
||||
if (!id) {
|
||||
return Promise.reject(CommonErrEnum.missingParams);
|
||||
}
|
||||
|
||||
const { dataset } = (await (async () => {
|
||||
if (defaultPermission !== undefined) {
|
||||
return await authDataset({ req, authToken: true, datasetId: id, per: ManagePermissionVal });
|
||||
} else {
|
||||
return await authDataset({ req, authToken: true, datasetId: id, per: WritePermissionVal });
|
||||
}
|
||||
})()) as { dataset: DatasetSchemaType };
|
||||
const isMove = parentId !== undefined;
|
||||
|
||||
const { dataset, permission } = await authDataset({
|
||||
req,
|
||||
authToken: true,
|
||||
datasetId: id,
|
||||
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 onUpdate = async (
|
||||
session?: ClientSession,
|
||||
updatedDefaultPermission?: PermissionValueType
|
||||
) => {
|
||||
const onUpdate = async (session?: ClientSession) => {
|
||||
await MongoDataset.findByIdAndUpdate(
|
||||
id,
|
||||
{
|
||||
@@ -73,35 +96,16 @@ async function handler(
|
||||
...(status && { status }),
|
||||
...(intro !== undefined && { intro }),
|
||||
...(externalReadUrl !== undefined && { externalReadUrl }),
|
||||
// move
|
||||
...(updatedDefaultPermission !== undefined && {
|
||||
defaultPermission: updatedDefaultPermission
|
||||
}),
|
||||
// update the defaultPermission
|
||||
...(dataset.parentId && isDefaultPermissionChanged && { inheritPermission: false })
|
||||
...(isMove && { inheritPermission: true })
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
};
|
||||
|
||||
// move
|
||||
if (parentId !== undefined) {
|
||||
if (isMove) {
|
||||
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) {
|
||||
const parentClbs = await getResourceAllClbs({
|
||||
const parentClbsAndGroups = await getResourceClbsAndGroups({
|
||||
teamId: dataset.teamId,
|
||||
resourceId: parentId,
|
||||
resourceType: PerResourceTypeEnum.dataset,
|
||||
@@ -112,7 +116,7 @@ async function handler(
|
||||
teamId: dataset.teamId,
|
||||
resourceId: id,
|
||||
resourceType: PerResourceTypeEnum.dataset,
|
||||
collaborators: parentClbs,
|
||||
collaborators: parentClbsAndGroups,
|
||||
session
|
||||
});
|
||||
|
||||
@@ -121,48 +125,13 @@ async function handler(
|
||||
resourceType: PerResourceTypeEnum.dataset,
|
||||
resourceModel: MongoDataset,
|
||||
folderTypeList: [DatasetTypeEnum.folder],
|
||||
collaborators: parentClbs,
|
||||
defaultPermission: parentDefaultPermission,
|
||||
collaborators: parentClbsAndGroups,
|
||||
session
|
||||
});
|
||||
return onUpdate(session, parentDefaultPermission);
|
||||
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 {
|
||||
return onUpdate();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { getTestRequest } from '@/test/utils';
|
||||
import '../../__mocks__/base';
|
||||
import { getTestRequest } from '@/test/utils';
|
||||
import handler, { OutLinkUpdateBody, OutLinkUpdateQuery } from './update';
|
||||
import { root } from '../../__mocks__/db/init';
|
||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import { root } from '../../__mocks__/db/init';
|
||||
|
||||
test('Update Outlink', async () => {
|
||||
const outlink = await MongoOutLink.create({
|
||||
beforeAll(async () => {
|
||||
await MongoOutLink.create({
|
||||
shareId: 'aaa',
|
||||
appId: root.appId,
|
||||
tmbId: root.tmbId,
|
||||
@@ -14,8 +14,13 @@ test('Update Outlink', async () => {
|
||||
type: 'share',
|
||||
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(
|
||||
...getTestRequest<OutLinkUpdateQuery, OutLinkUpdateBody>({
|
||||
@@ -27,6 +32,7 @@ test('Update Outlink', async () => {
|
||||
})
|
||||
)) as any;
|
||||
|
||||
console.log(res);
|
||||
expect(res.code).toBe(200);
|
||||
|
||||
const link = await MongoOutLink.findById(outlink._id).lean();
|
||||
|
||||
Reference in New Issue
Block a user