mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-15 15:41:05 +00:00
V4.12.4 features (#5626)
* fix: push again, user select option button and form input radio content overflow (#5601) * fix: push again, user select option button and form input radio content overflow * fix: use useCallback instead of useMemo, fix unnecessary delete * fix: Move the variable inside the component * fix: do not pass valueLabel to MySelect * ui * del collection api adapt * refactor: inherit permission (#5529) * refactor: permission update conflict check function * refactor(permission): app collaborator update api * refactor(permission): support app update collaborator * feat: support fe permission conflict check * refactor(permission): app permission * refactor(permission): dataset permission * refactor(permission): team permission * chore: fe adjust * fix: type error * fix: audit pagiation * fix: tc * chore: initv4130 * fix: app/dataset auth logic * chore: move code * refactor(permission): remove selfPermission * fix: mock * fix: test * fix: app & dataset auth * fix: inherit * test(inheritPermission): test syncChildrenPermission * prompt editor add list plugin (#5620) * perf: search result (#5608) * fix: table size (#5598) * temp: list value * backspace * optimize code --------- Co-authored-by: Archer <545436317@qq.com> Co-authored-by: 伍闲犬 <whoeverimf5@gmail.com> * fix: fe & member list (#5619) * chore: initv4130 * fix: MemberItemCard * fix: MemberItemCard * chore: fe adjust & init script * perf: test code * doc * fix debug variables (#5617) * perf: search result (#5608) * fix: table size (#5598) * fix debug variables * fix --------- Co-authored-by: Archer <545436317@qq.com> Co-authored-by: 伍闲犬 <whoeverimf5@gmail.com> * perf: member ui * fix: inherit bug (#5624) * refactor(permission): remove getClbsWithInfo, which is useless * fix: app list privateApp * fix: get infos * perf(fe): remove delete icon when it is disable in MemberItemCard * fix: dataset private dataset * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Archer <545436317@qq.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * perf: auto coupon * chore: upgrade script & get infos avatar (#5625) * fix: get infos * chore: initv4130 * feat: support WecomRobot publish, and fix AesKey can not save bug (#5526) * feat: resolve conflicts * fix: add param 'show_publish_wecom' * feat: abstract out WecomCrypto type * doc: wecom robot document * fix: solve instability in AI output * doc: update some pictures * feat: remove functions from request.ts to chat.ts and toolCall.ts * doc: wecom robot doc update * fix * delete unused code * doc: update version and prompt * feat: remove wecom crypto, delete wecom code in workflow * feat: delete unused codes --------- Co-authored-by: heheer <zhiyu44@qq.com> * remove test * rename init shell * feat: collection page store * reload sandbox * pysandbox * remove log * chore: remove useless code (#5629) * chore: remove useless code * fix: checkConflict * perf: support hidden type for RoleList * fix: copy node * update doc * fix(permission): some bug (#5632) * fix: app/dataset list * fix: inherit bug * perf: del app;i18n;save chat * fix: test * i18n * fix: sumper overflow return OwnerRoleVal (#5633) * remove invalid code * fix: scroll * fix: objectId * update next * update package * object id * mock redis * feat: add redis append to resolve wecom stream response (#5643) * feat: resolve conflicts * fix: add param 'show_publish_wecom' * feat: abstract out WecomCrypto type * doc: wecom robot document * fix: solve instability in AI output * doc: update some pictures * feat: remove functions from request.ts to chat.ts and toolCall.ts * doc: wecom robot doc update * fix * delete unused code * doc: update version and prompt * feat: remove wecom crypto, delete wecom code in workflow * feat: delete unused codes * feat: add redis append method --------- Co-authored-by: heheer <zhiyu44@qq.com> * cache per * fix(test): init team sub when creating mocked user (#5646) * fix: button is not vertically centered (#5647) * doc * fix: gridFs objectId (#5649) --------- Co-authored-by: Zeng Qingwen <143274079+fishwww-ww@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: 伍闲犬 <whoeverimf5@gmail.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: heheer <zhiyu44@qq.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { retryFn } from '@fastgpt/global/common/system/utils';
|
||||
import { connectionMongo } from '../../mongo';
|
||||
import { connectionMongo, Types } from '../../mongo';
|
||||
import { MongoRawTextBufferSchema, bucketName } from './schema';
|
||||
import { addLog } from '../../system/log';
|
||||
import { setCron } from '../../system/cron';
|
||||
@@ -86,7 +86,7 @@ export const getRawTextBuffer = async (sourceId: string) => {
|
||||
}
|
||||
|
||||
// Read file content
|
||||
const downloadStream = gridBucket.openDownloadStream(bufferData._id);
|
||||
const downloadStream = gridBucket.openDownloadStream(new Types.ObjectId(bufferData._id));
|
||||
|
||||
const fileBuffers = await gridFsStream2Buffer(downloadStream);
|
||||
|
||||
@@ -120,7 +120,7 @@ export const deleteRawTextBuffer = async (sourceId: string): Promise<boolean> =>
|
||||
return false;
|
||||
}
|
||||
|
||||
await gridBucket.delete(buffer._id);
|
||||
await gridBucket.delete(new Types.ObjectId(buffer._id));
|
||||
return true;
|
||||
});
|
||||
};
|
||||
@@ -155,7 +155,7 @@ export const clearExpiredRawTextBufferCron = async () => {
|
||||
|
||||
for (const item of data) {
|
||||
try {
|
||||
await gridBucket.delete(item._id);
|
||||
await gridBucket.delete(new Types.ObjectId(item._id));
|
||||
} catch (error) {
|
||||
addLog.error('Delete expired raw text buffer error', error);
|
||||
}
|
||||
|
@@ -64,6 +64,33 @@ const addCommonMiddleware = (schema: mongoose.Schema) => {
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// Convert _id to string
|
||||
schema.post(/^find/, function (docs) {
|
||||
if (!docs) return;
|
||||
|
||||
const convertObjectIds = (obj: any) => {
|
||||
if (!obj) return;
|
||||
|
||||
// Convert _id
|
||||
if (obj._id && obj._id.toString) {
|
||||
obj._id = obj._id.toString();
|
||||
}
|
||||
|
||||
// Convert other ObjectId fields
|
||||
Object.keys(obj).forEach((key) => {
|
||||
if (obj[key] && obj[key]._bsontype === 'ObjectId') {
|
||||
obj[key] = obj[key].toString();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (Array.isArray(docs)) {
|
||||
docs.forEach((doc) => convertObjectIds(doc));
|
||||
} else {
|
||||
convertObjectIds(docs);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return schema;
|
||||
|
@@ -4,3 +4,10 @@ export const readFromSecondary = {
|
||||
readPreference: ReadPreference.SECONDARY_PREFERRED, // primary | primaryPreferred | secondary | secondaryPreferred | nearest
|
||||
readConcern: 'local' as any // local | majority | linearizable | available
|
||||
};
|
||||
|
||||
export const writePrimary = {
|
||||
writeConcern: {
|
||||
w: 1,
|
||||
journal: false
|
||||
}
|
||||
};
|
||||
|
@@ -56,3 +56,20 @@ export const delRedisCache = async (key: string) => {
|
||||
const redis = getGlobalRedisConnection();
|
||||
await retryFn(() => redis.del(getCacheKey(key)));
|
||||
};
|
||||
|
||||
export const appendRedisCache = async (
|
||||
key: string,
|
||||
value: string | Buffer | number,
|
||||
expireSeconds?: number
|
||||
) => {
|
||||
try {
|
||||
const redis = getGlobalRedisConnection();
|
||||
await retryFn(() => redis.append(getCacheKey(key), value));
|
||||
if (expireSeconds) {
|
||||
await redis.expire(getCacheKey(key), expireSeconds);
|
||||
}
|
||||
} catch (error) {
|
||||
addLog.error('Append cache error:', error);
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
|
@@ -2,9 +2,9 @@ import type { NextApiResponse } from 'next';
|
||||
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { proxyError, ERROR_RESPONSE, ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||
import { addLog } from '../system/log';
|
||||
import { clearCookie } from '../../support/permission/controller';
|
||||
import { replaceSensitiveText } from '@fastgpt/global/common/string/tools';
|
||||
import { UserError } from '@fastgpt/global/common/error/utils';
|
||||
import { clearCookie } from '../../support/permission/auth/common';
|
||||
|
||||
export interface ResponseType<T = any> {
|
||||
code: number;
|
||||
|
@@ -645,4 +645,4 @@ const createChatCompletion = async ({
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
};
|
||||
};
|
@@ -157,23 +157,18 @@ export const onDelOneApp = async ({
|
||||
).lean();
|
||||
await Promise.all(evalJobs.map((evalJob) => removeEvaluationJob(evalJob._id)));
|
||||
|
||||
// Delete chats
|
||||
await deleteChatFiles({ appId });
|
||||
await MongoChatItem.deleteMany({
|
||||
appId
|
||||
});
|
||||
await MongoChat.deleteMany({
|
||||
appId
|
||||
});
|
||||
|
||||
const del = async (session: ClientSession) => {
|
||||
for await (const app of apps) {
|
||||
const appId = app._id;
|
||||
// Chats
|
||||
await deleteChatFiles({ appId });
|
||||
await MongoChatItem.deleteMany(
|
||||
{
|
||||
appId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
await MongoChat.deleteMany(
|
||||
{
|
||||
appId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
|
||||
// 删除分享链接
|
||||
await MongoOutLink.deleteMany({
|
||||
@@ -205,6 +200,7 @@ export const onDelOneApp = async ({
|
||||
{ $pull: { quickAppIds: { id: String(appId) } } }
|
||||
).session(session);
|
||||
|
||||
// Del permission
|
||||
await MongoResourcePermission.deleteMany({
|
||||
resourceType: PerResourceTypeEnum.app,
|
||||
teamId,
|
||||
|
@@ -15,6 +15,7 @@ import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { extractDeepestInteractive } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { MongoAppChatLog } from '../app/logs/chatLogsSchema';
|
||||
import { writePrimary } from '../../common/mongo/utils';
|
||||
|
||||
type Props = {
|
||||
chatId: string;
|
||||
@@ -115,7 +116,7 @@ export async function saveChat({
|
||||
});
|
||||
|
||||
await mongoSessionRun(async (session) => {
|
||||
const [{ _id: chatItemIdHuman }, { _id: chatItemIdAi }] = await MongoChatItem.insertMany(
|
||||
const [{ _id: chatItemIdHuman }, { _id: chatItemIdAi }] = await MongoChatItem.create(
|
||||
processedContent.map((item) => ({
|
||||
chatId,
|
||||
teamId,
|
||||
@@ -123,7 +124,7 @@ export async function saveChat({
|
||||
appId,
|
||||
...item
|
||||
})),
|
||||
{ session }
|
||||
{ session, ordered: true, ...writePrimary }
|
||||
);
|
||||
|
||||
await MongoChat.updateOne(
|
||||
@@ -152,7 +153,8 @@ export async function saveChat({
|
||||
},
|
||||
{
|
||||
session,
|
||||
upsert: true
|
||||
upsert: true,
|
||||
...writePrimary
|
||||
}
|
||||
);
|
||||
|
||||
@@ -215,7 +217,8 @@ export async function saveChat({
|
||||
}
|
||||
},
|
||||
{
|
||||
upsert: true
|
||||
upsert: true,
|
||||
...writePrimary
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
@@ -223,9 +226,15 @@ export async function saveChat({
|
||||
}
|
||||
|
||||
if (isUpdateUseTime) {
|
||||
await MongoApp.findByIdAndUpdate(appId, {
|
||||
updateTime: new Date()
|
||||
}).catch();
|
||||
await MongoApp.updateOne(
|
||||
{ _id: appId },
|
||||
{
|
||||
updateTime: new Date()
|
||||
},
|
||||
{
|
||||
...writePrimary
|
||||
}
|
||||
).catch();
|
||||
}
|
||||
} catch (error) {
|
||||
addLog.error(`update chat history error`, error);
|
||||
|
@@ -142,7 +142,7 @@ export const clearExpiredDatasetImageCron = async () => {
|
||||
|
||||
for (const item of data) {
|
||||
try {
|
||||
await gridBucket.delete(item._id);
|
||||
await gridBucket.delete(new Types.ObjectId(item._id));
|
||||
} catch (error) {
|
||||
addLog.error('Delete expired dataset image error', error);
|
||||
}
|
||||
|
@@ -15,7 +15,7 @@
|
||||
"@vercel/otel": "^1.13.0",
|
||||
"@xmldom/xmldom": "^0.8.10",
|
||||
"@zilliz/milvus2-sdk-node": "2.4.10",
|
||||
"axios": "^1.8.2",
|
||||
"axios": "^1.12.1",
|
||||
"bullmq": "^5.52.2",
|
||||
"chalk": "^5.3.0",
|
||||
"cheerio": "1.0.0-rc.12",
|
||||
@@ -38,7 +38,7 @@
|
||||
"mongoose": "^8.10.1",
|
||||
"multer": "2.0.2",
|
||||
"mysql2": "^3.11.3",
|
||||
"next": "14.2.28",
|
||||
"next": "14.2.32",
|
||||
"nextjs-cors": "^2.2.0",
|
||||
"node-cron": "^3.0.3",
|
||||
"node-xlsx": "^0.24.0",
|
||||
|
@@ -1,15 +1,15 @@
|
||||
/* Auth app permission */
|
||||
import { MongoApp } from '../../../core/app/schema';
|
||||
import { type AppDetailType } from '@fastgpt/global/core/app/type.d';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import {
|
||||
NullRoleVal,
|
||||
PerResourceTypeEnum,
|
||||
ReadPermissionVal,
|
||||
ReadRoleVal
|
||||
} from '@fastgpt/global/support/permission/constant';
|
||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||
import { getTmbInfoByTmbId } from '../../user/team/controller';
|
||||
import { getResourcePermission } from '../controller';
|
||||
import { getTmbPermission } from '../controller';
|
||||
import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
|
||||
import { type PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
@@ -18,6 +18,8 @@ import { PluginSourceEnum } from '@fastgpt/global/core/app/plugin/constants';
|
||||
import { type AuthModeType, type AuthResponseType } from '../type';
|
||||
import { splitCombinePluginId } from '@fastgpt/global/core/app/plugin/utils';
|
||||
import { AppReadChatLogPerVal } from '@fastgpt/global/support/permission/app/constant';
|
||||
import { parseHeaderCert } from '../auth/common';
|
||||
import { sumPer } from '@fastgpt/global/support/permission/utils';
|
||||
|
||||
export const authPluginByTmbId = async ({
|
||||
tmbId,
|
||||
@@ -90,53 +92,27 @@ export const authAppByTmbId = async ({
|
||||
|
||||
const isOwner = tmbPer.isOwner || String(app.tmbId) === String(tmbId);
|
||||
|
||||
const { Per } = await (async () => {
|
||||
if (isOwner) {
|
||||
return {
|
||||
Per: new AppPermission({ isOwner: true })
|
||||
};
|
||||
}
|
||||
const isGetParentClb =
|
||||
app.inheritPermission && !AppFolderTypeList.includes(app.type) && !!app.parentId;
|
||||
|
||||
if (
|
||||
AppFolderTypeList.includes(app.type) ||
|
||||
app.inheritPermission === false ||
|
||||
!app.parentId
|
||||
) {
|
||||
// 1. is a folder. (Folders have completely permission)
|
||||
// 2. inheritPermission is false.
|
||||
// 3. is root folder/app.
|
||||
const role = await getResourcePermission({
|
||||
teamId,
|
||||
tmbId,
|
||||
resourceId: appId,
|
||||
resourceType: PerResourceTypeEnum.app
|
||||
});
|
||||
const Per = new AppPermission({ role, isOwner });
|
||||
const [folderPer = NullRoleVal, myPer = NullRoleVal] = await Promise.all([
|
||||
isGetParentClb
|
||||
? getTmbPermission({
|
||||
teamId,
|
||||
tmbId,
|
||||
resourceId: app.parentId!,
|
||||
resourceType: PerResourceTypeEnum.app
|
||||
})
|
||||
: NullRoleVal,
|
||||
getTmbPermission({
|
||||
teamId,
|
||||
tmbId,
|
||||
resourceId: appId,
|
||||
resourceType: PerResourceTypeEnum.app
|
||||
})
|
||||
]);
|
||||
|
||||
if (app.favourite || app.quick) {
|
||||
Per.addRole(ReadRoleVal);
|
||||
}
|
||||
|
||||
return {
|
||||
Per
|
||||
};
|
||||
} else {
|
||||
// is not folder and inheritPermission is true and is not root folder.
|
||||
const { app: parent } = await authAppByTmbId({
|
||||
tmbId,
|
||||
appId: app.parentId,
|
||||
per
|
||||
});
|
||||
|
||||
const Per = new AppPermission({
|
||||
role: parent.permission.role,
|
||||
isOwner
|
||||
});
|
||||
return {
|
||||
Per
|
||||
};
|
||||
}
|
||||
})();
|
||||
const Per = new AppPermission({ role: sumPer(folderPer, myPer), isOwner });
|
||||
|
||||
if (!Per.checkPer(per)) {
|
||||
return Promise.reject(AppErrEnum.unAuthApp);
|
||||
|
@@ -1,7 +1,13 @@
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import type { ReqHeaderAuthType } from '../type';
|
||||
import { type AuthModeType } from '../type';
|
||||
import { SERVICE_LOCAL_HOST } from '../../../common/system/tools';
|
||||
import { type ApiRequestProps } from '../../../type/next';
|
||||
import type { NextApiResponse } from 'next';
|
||||
import Cookie from 'cookie';
|
||||
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||
import { authUserSession } from '../../../support/user/session';
|
||||
import { authOpenApiKey } from '../../../support/openapi/auth';
|
||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
export const authCert = async (props: AuthModeType) => {
|
||||
const result = await parseHeaderCert(props);
|
||||
@@ -19,3 +25,149 @@ export const authRequestFromLocal = ({ req }: { req: ApiRequestProps }) => {
|
||||
return Promise.reject('Invalid request');
|
||||
}
|
||||
};
|
||||
|
||||
export async function parseHeaderCert({
|
||||
req,
|
||||
authToken = false,
|
||||
authRoot = false,
|
||||
authApiKey = false
|
||||
}: AuthModeType) {
|
||||
// parse jwt
|
||||
async function authCookieToken(cookie?: string, token?: string) {
|
||||
// 获取 cookie
|
||||
const cookies = Cookie.parse(cookie || '');
|
||||
const cookieToken = token || cookies[TokenName];
|
||||
|
||||
if (!cookieToken) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
return { ...(await authUserSession(cookieToken)), sessionId: cookieToken };
|
||||
}
|
||||
// from authorization get apikey
|
||||
async function parseAuthorization(authorization?: string) {
|
||||
if (!authorization) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
// Bearer fastgpt-xxxx-appId
|
||||
const auth = authorization.split(' ')[1];
|
||||
if (!auth) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
const { apikey, appId: authorizationAppid = '' } = await (async () => {
|
||||
const arr = auth.split('-');
|
||||
// abandon
|
||||
if (arr.length === 3) {
|
||||
return {
|
||||
apikey: `${arr[0]}-${arr[1]}`,
|
||||
appId: arr[2]
|
||||
};
|
||||
}
|
||||
if (arr.length === 2) {
|
||||
return {
|
||||
apikey: auth
|
||||
};
|
||||
}
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
})();
|
||||
|
||||
// auth apikey
|
||||
const { teamId, tmbId, appId: apiKeyAppId = '', sourceName } = await authOpenApiKey({ apikey });
|
||||
|
||||
return {
|
||||
uid: '',
|
||||
teamId,
|
||||
tmbId,
|
||||
apikey,
|
||||
appId: apiKeyAppId || authorizationAppid,
|
||||
sourceName
|
||||
};
|
||||
}
|
||||
// root user
|
||||
async function parseRootKey(rootKey?: string) {
|
||||
if (!rootKey || !process.env.ROOT_KEY || rootKey !== process.env.ROOT_KEY) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
}
|
||||
|
||||
const { cookie, token, rootkey, authorization } = (req.headers || {}) as ReqHeaderAuthType;
|
||||
|
||||
const { uid, teamId, tmbId, appId, openApiKey, authType, isRoot, sourceName, sessionId } =
|
||||
await (async () => {
|
||||
if (authApiKey && authorization) {
|
||||
// apikey from authorization
|
||||
const authResponse = await parseAuthorization(authorization);
|
||||
return {
|
||||
uid: authResponse.uid,
|
||||
teamId: authResponse.teamId,
|
||||
tmbId: authResponse.tmbId,
|
||||
appId: authResponse.appId,
|
||||
openApiKey: authResponse.apikey,
|
||||
authType: AuthUserTypeEnum.apikey,
|
||||
sourceName: authResponse.sourceName
|
||||
};
|
||||
}
|
||||
if (authToken && (token || cookie)) {
|
||||
// user token(from fastgpt web)
|
||||
const res = await authCookieToken(cookie, token);
|
||||
|
||||
return {
|
||||
uid: res.userId,
|
||||
teamId: res.teamId,
|
||||
tmbId: res.tmbId,
|
||||
appId: '',
|
||||
openApiKey: '',
|
||||
authType: AuthUserTypeEnum.token,
|
||||
isRoot: res.isRoot,
|
||||
sessionId: res.sessionId
|
||||
};
|
||||
}
|
||||
if (authRoot && rootkey) {
|
||||
await parseRootKey(rootkey);
|
||||
// root user
|
||||
return {
|
||||
uid: '',
|
||||
teamId: '',
|
||||
tmbId: '',
|
||||
appId: '',
|
||||
openApiKey: '',
|
||||
authType: AuthUserTypeEnum.root,
|
||||
isRoot: true
|
||||
};
|
||||
}
|
||||
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
})();
|
||||
|
||||
if (!authRoot && (!teamId || !tmbId)) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
return {
|
||||
userId: String(uid),
|
||||
teamId: String(teamId),
|
||||
tmbId: String(tmbId),
|
||||
appId,
|
||||
authType,
|
||||
sourceName,
|
||||
apikey: openApiKey,
|
||||
isRoot: !!isRoot,
|
||||
sessionId
|
||||
};
|
||||
}
|
||||
|
||||
/* set cookie */
|
||||
export const TokenName = 'fastgpt_token';
|
||||
export const setCookie = (res: NextApiResponse, token: string) => {
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
`${TokenName}=${token}; Path=/; HttpOnly; Max-Age=604800; Samesite=Strict;`
|
||||
);
|
||||
};
|
||||
|
||||
/* clear cookie */
|
||||
export const clearCookie = (res: NextApiResponse) => {
|
||||
res.setHeader('Set-Cookie', `${TokenName}=; Path=/; Max-Age=0`);
|
||||
};
|
||||
|
@@ -1,11 +1,15 @@
|
||||
import { type AuthModeType, type AuthResponseType } from '../type';
|
||||
import { type DatasetFileSchema } from '@fastgpt/global/core/dataset/type';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { getFileById } from '../../../common/file/gridfs/controller';
|
||||
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
|
||||
import { BucketNameEnum, bucketNameMap } from '@fastgpt/global/common/file/constants';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import { OwnerPermissionVal, ReadRoleVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { Permission } from '@fastgpt/global/support/permission/controller';
|
||||
import type { FileTokenQuery } from '@fastgpt/global/common/file/type';
|
||||
import { addMinutes } from 'date-fns';
|
||||
import { parseHeaderCert } from './common';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||
|
||||
export const authCollectionFile = async ({
|
||||
fileId,
|
||||
@@ -46,3 +50,45 @@ export const authCollectionFile = async ({
|
||||
file
|
||||
};
|
||||
};
|
||||
|
||||
/* file permission */
|
||||
export const createFileToken = (data: FileTokenQuery) => {
|
||||
if (!process.env.FILE_TOKEN_KEY) {
|
||||
return Promise.reject('System unset FILE_TOKEN_KEY');
|
||||
}
|
||||
|
||||
const expireMinutes =
|
||||
data.customExpireMinutes ?? bucketNameMap[data.bucketName].previewExpireMinutes;
|
||||
const expiredTime = Math.floor(addMinutes(new Date(), expireMinutes).getTime() / 1000);
|
||||
|
||||
const key = (process.env.FILE_TOKEN_KEY as string) ?? 'filetoken';
|
||||
const token = jwt.sign(
|
||||
{
|
||||
...data,
|
||||
exp: expiredTime
|
||||
},
|
||||
key
|
||||
);
|
||||
return Promise.resolve(token);
|
||||
};
|
||||
|
||||
export const authFileToken = (token?: string) =>
|
||||
new Promise<FileTokenQuery>((resolve, reject) => {
|
||||
if (!token) {
|
||||
return reject(ERROR_ENUM.unAuthFile);
|
||||
}
|
||||
const key = (process.env.FILE_TOKEN_KEY as string) ?? 'filetoken';
|
||||
|
||||
jwt.verify(token, key, (err, decoded: any) => {
|
||||
if (err || !decoded.bucketName || !decoded?.teamId || !decoded?.fileId) {
|
||||
reject(ERROR_ENUM.unAuthFile);
|
||||
return;
|
||||
}
|
||||
resolve({
|
||||
bucketName: decoded.bucketName,
|
||||
teamId: decoded.teamId,
|
||||
uid: decoded.uid,
|
||||
fileId: decoded.fileId
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { type AuthModeType, type AuthResponseType } from '../type';
|
||||
import { type OpenApiSchema } from '@fastgpt/global/support/openapi/type';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { getTmbInfoByTmbId } from '../../user/team/controller';
|
||||
import { MongoOpenApi } from '../../openapi/schema';
|
||||
import { OpenApiErrEnum } from '@fastgpt/global/common/error/code/openapi';
|
||||
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { authAppByTmbId } from '../app/auth';
|
||||
import { parseHeaderCert } from './common';
|
||||
|
||||
export async function authOpenApiKeyCrud({
|
||||
id,
|
||||
|
@@ -1,27 +1,24 @@
|
||||
import Cookie from 'cookie';
|
||||
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { type NextApiResponse, type NextApiRequest } from 'next';
|
||||
import type { AuthModeType, ReqHeaderAuthType } from './type.d';
|
||||
import type { ClientSession, AnyBulkWriteOperation } from '../../common/mongo';
|
||||
import type { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { authOpenApiKey } from '../openapi/auth';
|
||||
import { type FileTokenQuery } from '@fastgpt/global/common/file/type';
|
||||
import { ManageRoleVal, OwnerRoleVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { MongoResourcePermission } from './schema';
|
||||
import { type ClientSession } from 'mongoose';
|
||||
import type { ResourcePermissionType, ResourceType } from '@fastgpt/global/support/permission/type';
|
||||
import { type 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';
|
||||
import { Permission } from '@fastgpt/global/support/permission/controller';
|
||||
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import { type MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type';
|
||||
import { type TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
|
||||
import { type OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
|
||||
import { getOrgIdSetWithParentByTmbId } from './org/controllers';
|
||||
import { authUserSession } from '../user/session';
|
||||
import { sumPer } from '@fastgpt/global/support/permission/utils';
|
||||
import { getCollaboratorId, sumPer } from '@fastgpt/global/support/permission/utils';
|
||||
import { type SyncChildrenPermissionResourceType } from './inheritPermission';
|
||||
import { pickCollaboratorIdFields } from './utils';
|
||||
import type {
|
||||
CollaboratorItemDetailType,
|
||||
CollaboratorItemType
|
||||
} from '@fastgpt/global/support/permission/collaborator';
|
||||
import { MongoTeamMember } from '../../support/user/team/teamMemberSchema';
|
||||
import { MongoOrgModel } from './org/orgSchema';
|
||||
import { MongoMemberGroupModel } from './memberGroup/memberGroupSchema';
|
||||
import { DEFAULT_ORG_AVATAR, DEFAULT_TEAM_AVATAR } from '@fastgpt/global/common/system/constants';
|
||||
|
||||
/** get resource permission for a team member
|
||||
* If there is no permission for the team member, it will return undefined
|
||||
@@ -31,7 +28,7 @@ import { sumPer } from '@fastgpt/global/support/permission/utils';
|
||||
* @param resourceId
|
||||
* @returns PermissionValueType | undefined
|
||||
*/
|
||||
export const getResourcePermission = async ({
|
||||
export const getTmbPermission = async ({
|
||||
resourceType,
|
||||
teamId,
|
||||
tmbId,
|
||||
@@ -106,17 +103,27 @@ export const getResourcePermission = async ({
|
||||
return sumPer(...groupPers, ...orgPers);
|
||||
};
|
||||
|
||||
export async function getResourceClbsAndGroups({
|
||||
resourceId,
|
||||
/**
|
||||
* Only get resource's owned clbs, not including parents'.
|
||||
*/
|
||||
export async function getResourceOwnedClbs({
|
||||
resourceType,
|
||||
teamId,
|
||||
resourceId,
|
||||
session
|
||||
}: {
|
||||
resourceId: ParentIdType;
|
||||
resourceType: Omit<`${PerResourceTypeEnum}`, 'team'>;
|
||||
teamId: string;
|
||||
session: ClientSession;
|
||||
}) {
|
||||
session?: ClientSession;
|
||||
} & (
|
||||
| {
|
||||
resourceType: 'team';
|
||||
resourceId?: undefined;
|
||||
}
|
||||
| {
|
||||
resourceType: Omit<PerResourceTypeEnum, 'team'>;
|
||||
resourceId: ParentIdType;
|
||||
}
|
||||
)) {
|
||||
return MongoResourcePermission.find(
|
||||
{
|
||||
resourceId,
|
||||
@@ -124,282 +131,110 @@ export async function getResourceClbsAndGroups({
|
||||
teamId
|
||||
},
|
||||
undefined,
|
||||
{ session }
|
||||
{ ...(session ? { session } : {}) }
|
||||
).lean();
|
||||
}
|
||||
|
||||
export const getClbsAndGroupsWithInfo = async ({
|
||||
resourceId,
|
||||
resourceType,
|
||||
teamId
|
||||
export const getClbsInfo = async ({
|
||||
clbs,
|
||||
teamId,
|
||||
ownerTmbId
|
||||
}: {
|
||||
clbs: CollaboratorItemType[];
|
||||
teamId: string;
|
||||
} & (
|
||||
| {
|
||||
resourceId: ParentIdType;
|
||||
resourceType: Omit<`${PerResourceTypeEnum}`, 'team'>;
|
||||
}
|
||||
| {
|
||||
resourceType: 'team';
|
||||
resourceId?: undefined;
|
||||
}
|
||||
)) =>
|
||||
Promise.all([
|
||||
MongoResourcePermission.find({
|
||||
teamId,
|
||||
resourceId,
|
||||
resourceType,
|
||||
tmbId: {
|
||||
$exists: true
|
||||
}
|
||||
})
|
||||
.populate<{ tmb: TeamMemberSchema }>({
|
||||
path: 'tmb',
|
||||
select: 'name userId avatar'
|
||||
})
|
||||
.lean(),
|
||||
MongoResourcePermission.find({
|
||||
teamId,
|
||||
resourceId,
|
||||
resourceType,
|
||||
groupId: {
|
||||
$exists: true
|
||||
}
|
||||
})
|
||||
.populate<{ group: MemberGroupSchemaType }>('group', 'name avatar')
|
||||
.lean(),
|
||||
MongoResourcePermission.find({
|
||||
teamId,
|
||||
resourceId,
|
||||
resourceType,
|
||||
orgId: {
|
||||
$exists: true
|
||||
}
|
||||
})
|
||||
.populate<{ org: OrgSchemaType }>({ path: 'org', select: 'name avatar' })
|
||||
.lean()
|
||||
]);
|
||||
ownerTmbId?: string;
|
||||
}): Promise<CollaboratorItemDetailType[]> => {
|
||||
const tmbIds = [];
|
||||
const orgIds = [];
|
||||
const groupIds = [];
|
||||
|
||||
export const delResourcePermissionById = (id: string) => {
|
||||
return MongoResourcePermission.findByIdAndDelete(id);
|
||||
};
|
||||
export const delResourcePermission = ({
|
||||
session,
|
||||
tmbId,
|
||||
groupId,
|
||||
orgId,
|
||||
...props
|
||||
}: {
|
||||
resourceType: PerResourceTypeEnum;
|
||||
teamId: string;
|
||||
resourceId: string;
|
||||
session?: ClientSession;
|
||||
tmbId?: string;
|
||||
groupId?: string;
|
||||
orgId?: string;
|
||||
}) => {
|
||||
// either tmbId or groupId or orgId must be provided
|
||||
if (!tmbId && !groupId && !orgId) {
|
||||
return Promise.reject(CommonErrEnum.missingParams);
|
||||
for (const clb of clbs) {
|
||||
if (clb.tmbId) tmbIds.push(clb.tmbId);
|
||||
if (clb.orgId) orgIds.push(clb.orgId);
|
||||
if (clb.groupId) groupIds.push(clb.groupId);
|
||||
}
|
||||
|
||||
return MongoResourcePermission.deleteOne(
|
||||
{
|
||||
...(tmbId ? { tmbId } : {}),
|
||||
...(groupId ? { groupId } : {}),
|
||||
...(orgId ? { orgId } : {}),
|
||||
...props
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
};
|
||||
const infos = (
|
||||
await Promise.all([
|
||||
MongoTeamMember.find({ _id: { $in: tmbIds }, teamId }, '_id name avatar').lean(),
|
||||
MongoOrgModel.find({ _id: { $in: orgIds }, teamId }, '_id name avatar').lean(),
|
||||
MongoMemberGroupModel.find({ _id: { $in: groupIds }, teamId }, '_id name avatar').lean()
|
||||
])
|
||||
).flat();
|
||||
|
||||
/* 下面代码等迁移 */
|
||||
|
||||
export async function parseHeaderCert({
|
||||
req,
|
||||
authToken = false,
|
||||
authRoot = false,
|
||||
authApiKey = false
|
||||
}: AuthModeType) {
|
||||
// parse jwt
|
||||
async function authCookieToken(cookie?: string, token?: string) {
|
||||
// 获取 cookie
|
||||
const cookies = Cookie.parse(cookie || '');
|
||||
const cookieToken = token || cookies[TokenName];
|
||||
|
||||
if (!cookieToken) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
return { ...(await authUserSession(cookieToken)), sessionId: cookieToken };
|
||||
}
|
||||
// from authorization get apikey
|
||||
async function parseAuthorization(authorization?: string) {
|
||||
if (!authorization) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
// Bearer fastgpt-xxxx-appId
|
||||
const auth = authorization.split(' ')[1];
|
||||
if (!auth) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
const { apikey, appId: authorizationAppid = '' } = await (async () => {
|
||||
const arr = auth.split('-');
|
||||
// abandon
|
||||
if (arr.length === 3) {
|
||||
return {
|
||||
apikey: `${arr[0]}-${arr[1]}`,
|
||||
appId: arr[2]
|
||||
};
|
||||
}
|
||||
if (arr.length === 2) {
|
||||
return {
|
||||
apikey: auth
|
||||
};
|
||||
}
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
})();
|
||||
|
||||
// auth apikey
|
||||
const { teamId, tmbId, appId: apiKeyAppId = '', sourceName } = await authOpenApiKey({ apikey });
|
||||
return clbs.map((clb) => {
|
||||
const info = infos.find((info) => info._id === getCollaboratorId(clb));
|
||||
|
||||
return {
|
||||
uid: '',
|
||||
...clb,
|
||||
teamId,
|
||||
tmbId,
|
||||
apikey,
|
||||
appId: apiKeyAppId || authorizationAppid,
|
||||
sourceName
|
||||
permission: new Permission({
|
||||
role: clb.permission,
|
||||
isOwner: Boolean(ownerTmbId && clb.tmbId && ownerTmbId === clb.tmbId)
|
||||
}),
|
||||
name: info?.name ?? 'Unknown name',
|
||||
avatar: info?.avatar || (clb.orgId ? DEFAULT_ORG_AVATAR : DEFAULT_TEAM_AVATAR)
|
||||
};
|
||||
}
|
||||
// root user
|
||||
async function parseRootKey(rootKey?: string) {
|
||||
if (!rootKey || !process.env.ROOT_KEY || rootKey !== process.env.ROOT_KEY) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
}
|
||||
|
||||
const { cookie, token, rootkey, authorization } = (req.headers || {}) as ReqHeaderAuthType;
|
||||
|
||||
const { uid, teamId, tmbId, appId, openApiKey, authType, isRoot, sourceName, sessionId } =
|
||||
await (async () => {
|
||||
if (authApiKey && authorization) {
|
||||
// apikey from authorization
|
||||
const authResponse = await parseAuthorization(authorization);
|
||||
return {
|
||||
uid: authResponse.uid,
|
||||
teamId: authResponse.teamId,
|
||||
tmbId: authResponse.tmbId,
|
||||
appId: authResponse.appId,
|
||||
openApiKey: authResponse.apikey,
|
||||
authType: AuthUserTypeEnum.apikey,
|
||||
sourceName: authResponse.sourceName
|
||||
};
|
||||
}
|
||||
if (authToken && (token || cookie)) {
|
||||
// user token(from fastgpt web)
|
||||
const res = await authCookieToken(cookie, token);
|
||||
|
||||
return {
|
||||
uid: res.userId,
|
||||
teamId: res.teamId,
|
||||
tmbId: res.tmbId,
|
||||
appId: '',
|
||||
openApiKey: '',
|
||||
authType: AuthUserTypeEnum.token,
|
||||
isRoot: res.isRoot,
|
||||
sessionId: res.sessionId
|
||||
};
|
||||
}
|
||||
if (authRoot && rootkey) {
|
||||
await parseRootKey(rootkey);
|
||||
// root user
|
||||
return {
|
||||
uid: '',
|
||||
teamId: '',
|
||||
tmbId: '',
|
||||
appId: '',
|
||||
openApiKey: '',
|
||||
authType: AuthUserTypeEnum.root,
|
||||
isRoot: true
|
||||
};
|
||||
}
|
||||
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
})();
|
||||
|
||||
if (!authRoot && (!teamId || !tmbId)) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
return {
|
||||
userId: String(uid),
|
||||
teamId: String(teamId),
|
||||
tmbId: String(tmbId),
|
||||
appId,
|
||||
authType,
|
||||
sourceName,
|
||||
apikey: openApiKey,
|
||||
isRoot: !!isRoot,
|
||||
sessionId
|
||||
};
|
||||
}
|
||||
|
||||
/* set cookie */
|
||||
export const TokenName = 'fastgpt_token';
|
||||
export const setCookie = (res: NextApiResponse, token: string) => {
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
`${TokenName}=${token}; Path=/; HttpOnly; Max-Age=604800; Samesite=Strict;`
|
||||
);
|
||||
};
|
||||
|
||||
/* clear cookie */
|
||||
export const clearCookie = (res: NextApiResponse) => {
|
||||
res.setHeader('Set-Cookie', `${TokenName}=; Path=/; Max-Age=0`);
|
||||
};
|
||||
|
||||
/* file permission */
|
||||
export const createFileToken = (data: FileTokenQuery) => {
|
||||
if (!process.env.FILE_TOKEN_KEY) {
|
||||
return Promise.reject('System unset FILE_TOKEN_KEY');
|
||||
}
|
||||
|
||||
const expireMinutes =
|
||||
data.customExpireMinutes ?? bucketNameMap[data.bucketName].previewExpireMinutes;
|
||||
const expiredTime = Math.floor(addMinutes(new Date(), expireMinutes).getTime() / 1000);
|
||||
|
||||
const key = (process.env.FILE_TOKEN_KEY as string) ?? 'filetoken';
|
||||
const token = jwt.sign(
|
||||
{
|
||||
...data,
|
||||
exp: expiredTime
|
||||
},
|
||||
key
|
||||
);
|
||||
return Promise.resolve(token);
|
||||
};
|
||||
|
||||
export const authFileToken = (token?: string) =>
|
||||
new Promise<FileTokenQuery>((resolve, reject) => {
|
||||
if (!token) {
|
||||
return reject(ERROR_ENUM.unAuthFile);
|
||||
}
|
||||
const key = (process.env.FILE_TOKEN_KEY as string) ?? 'filetoken';
|
||||
|
||||
jwt.verify(token, key, (err, decoded: any) => {
|
||||
if (err || !decoded.bucketName || !decoded?.teamId || !decoded?.fileId) {
|
||||
reject(ERROR_ENUM.unAuthFile);
|
||||
return;
|
||||
}
|
||||
resolve({
|
||||
bucketName: decoded.bucketName,
|
||||
teamId: decoded.teamId,
|
||||
uid: decoded.uid,
|
||||
fileId: decoded.fileId
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const createResourceDefaultCollaborators = async ({
|
||||
resource,
|
||||
resourceType,
|
||||
session,
|
||||
tmbId
|
||||
}: {
|
||||
resource: SyncChildrenPermissionResourceType;
|
||||
resourceType: PerResourceTypeEnum;
|
||||
|
||||
// should be provided when inheritPermission is true
|
||||
session: ClientSession;
|
||||
tmbId: string;
|
||||
}) => {
|
||||
const parentClbs = await getResourceOwnedClbs({
|
||||
resourceId: resource.parentId,
|
||||
resourceType,
|
||||
teamId: resource.teamId,
|
||||
session
|
||||
});
|
||||
// 1. add owner into the permission list with owner per
|
||||
// 2. remove parent's owner permission, instead of manager
|
||||
|
||||
const collaborators: CollaboratorItemType[] = [
|
||||
...parentClbs
|
||||
.filter((item) => item.tmbId !== tmbId)
|
||||
.map((clb) => {
|
||||
if (clb.permission === OwnerRoleVal) {
|
||||
clb.permission = ManageRoleVal;
|
||||
}
|
||||
return clb;
|
||||
}),
|
||||
{
|
||||
tmbId,
|
||||
permission: OwnerRoleVal
|
||||
}
|
||||
];
|
||||
|
||||
const ops: AnyBulkWriteOperation<ResourcePermissionType>[] = [];
|
||||
|
||||
for (const clb of collaborators) {
|
||||
ops.push({
|
||||
updateOne: {
|
||||
filter: {
|
||||
...pickCollaboratorIdFields(clb),
|
||||
teamId: resource.teamId,
|
||||
resourceId: resource._id,
|
||||
resourceType
|
||||
},
|
||||
update: {
|
||||
$set: {
|
||||
permission: clb.permission
|
||||
}
|
||||
},
|
||||
upsert: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await MongoResourcePermission.bulkWrite(ops, { session });
|
||||
};
|
||||
|
@@ -1,5 +1,5 @@
|
||||
import { type PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import { getResourcePermission, parseHeaderCert } from '../controller';
|
||||
import { getTmbPermission } from '../controller';
|
||||
import {
|
||||
type CollectionWithDatasetType,
|
||||
type DatasetDataItemType,
|
||||
@@ -9,6 +9,7 @@ import { getTmbInfoByTmbId } from '../../user/team/controller';
|
||||
import { MongoDataset } from '../../../core/dataset/schema';
|
||||
import {
|
||||
NullPermissionVal,
|
||||
NullRoleVal,
|
||||
PerResourceTypeEnum
|
||||
} from '@fastgpt/global/support/permission/constant';
|
||||
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
|
||||
@@ -21,6 +22,8 @@ import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import { DataSetDefaultRoleVal } from '@fastgpt/global/support/permission/dataset/constant';
|
||||
import { getDatasetImagePreviewUrl } from '../../../core/dataset/image/utils';
|
||||
import { i18nT } from '../../../../web/i18n/utils';
|
||||
import { parseHeaderCert } from '../auth/common';
|
||||
import { sumPer } from '@fastgpt/global/support/permission/utils';
|
||||
|
||||
export const authDatasetByTmbId = async ({
|
||||
tmbId,
|
||||
@@ -61,54 +64,27 @@ export const authDatasetByTmbId = async ({
|
||||
}
|
||||
|
||||
const isOwner = tmbPer.isOwner || String(dataset.tmbId) === String(tmbId);
|
||||
const isGetParentClb =
|
||||
dataset.inheritPermission && dataset.type !== DatasetTypeEnum.folder && !!dataset.parentId;
|
||||
|
||||
// get dataset permission or inherit permission from parent folder.
|
||||
const { Per } = await (async () => {
|
||||
if (isOwner) {
|
||||
return {
|
||||
Per: new DatasetPermission({ isOwner: true })
|
||||
};
|
||||
}
|
||||
if (
|
||||
dataset.type === DatasetTypeEnum.folder ||
|
||||
dataset.inheritPermission === false ||
|
||||
!dataset.parentId
|
||||
) {
|
||||
// 1. is a folder. (Folders have completely permission)
|
||||
// 2. inheritPermission is false.
|
||||
// 3. is root folder/dataset.
|
||||
const rp = await getResourcePermission({
|
||||
teamId,
|
||||
tmbId,
|
||||
resourceId: datasetId,
|
||||
resourceType: PerResourceTypeEnum.dataset
|
||||
});
|
||||
const Per = new DatasetPermission({
|
||||
role: rp,
|
||||
isOwner
|
||||
});
|
||||
return {
|
||||
Per
|
||||
};
|
||||
} else {
|
||||
// is not folder and inheritPermission is true and is not root folder.
|
||||
const { dataset: parent } = await authDatasetByTmbId({
|
||||
tmbId,
|
||||
datasetId: dataset.parentId,
|
||||
per,
|
||||
isRoot
|
||||
});
|
||||
const [folderPer = NullRoleVal, myPer = NullRoleVal] = await Promise.all([
|
||||
isGetParentClb
|
||||
? getTmbPermission({
|
||||
teamId,
|
||||
tmbId,
|
||||
resourceId: dataset.parentId!,
|
||||
resourceType: PerResourceTypeEnum.dataset
|
||||
})
|
||||
: NullRoleVal,
|
||||
getTmbPermission({
|
||||
teamId,
|
||||
tmbId,
|
||||
resourceId: datasetId,
|
||||
resourceType: PerResourceTypeEnum.dataset
|
||||
})
|
||||
]);
|
||||
|
||||
const Per = new DatasetPermission({
|
||||
role: parent.permission.role,
|
||||
isOwner
|
||||
});
|
||||
|
||||
return {
|
||||
Per
|
||||
};
|
||||
}
|
||||
})();
|
||||
const Per = new DatasetPermission({ role: sumPer(folderPer, myPer), isOwner });
|
||||
|
||||
if (!Per.checkPer(per)) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDataset);
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { authAppByTmbId } from '../app/auth';
|
||||
import {
|
||||
ManagePermissionVal,
|
||||
@@ -7,6 +6,7 @@ import {
|
||||
import type { EvaluationSchemaType } from '@fastgpt/global/core/app/evaluation/type';
|
||||
import type { AuthModeType } from '../type';
|
||||
import { MongoEvaluation } from '../../../core/app/evaluation/evalSchema';
|
||||
import { parseHeaderCert } from '../auth/common';
|
||||
|
||||
export const authEval = async ({
|
||||
evalId,
|
||||
|
@@ -1,11 +1,22 @@
|
||||
import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
|
||||
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||
import type { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import type { PermissionValueType } from '@fastgpt/global/support/permission/type';
|
||||
import type { ClientSession, Model } from 'mongoose';
|
||||
import {
|
||||
ManageRoleVal,
|
||||
NullPermissionVal,
|
||||
OwnerRoleVal,
|
||||
type PerResourceTypeEnum
|
||||
} from '@fastgpt/global/support/permission/constant';
|
||||
import type { ResourcePermissionType } from '@fastgpt/global/support/permission/type';
|
||||
import { mongoSessionRun } from '../../common/mongo/sessionRun';
|
||||
import { getResourceClbsAndGroups } from './controller';
|
||||
import { getResourceOwnedClbs } from './controller';
|
||||
import { MongoResourcePermission } from './schema';
|
||||
import type { ClientSession, Model, AnyBulkWriteOperation } from '../../common/mongo';
|
||||
import {
|
||||
getCollaboratorId,
|
||||
mergeCollaboratorList,
|
||||
sumPer
|
||||
} from '@fastgpt/global/support/permission/utils';
|
||||
import type { CollaboratorItemType } from '@fastgpt/global/support/permission/collaborator';
|
||||
import { pickCollaboratorIdFields } from './utils';
|
||||
|
||||
export type SyncChildrenPermissionResourceType = {
|
||||
_id: string;
|
||||
@@ -13,15 +24,10 @@ export type SyncChildrenPermissionResourceType = {
|
||||
teamId: string;
|
||||
parentId?: ParentIdType;
|
||||
};
|
||||
export type UpdateCollaboratorItem = {
|
||||
permission: PermissionValueType;
|
||||
} & RequireOnlyOne<{
|
||||
tmbId: string;
|
||||
groupId: string;
|
||||
orgId: string;
|
||||
}>;
|
||||
|
||||
// sync the permission to all children folders.
|
||||
/**
|
||||
* sync the permission to all children folders.
|
||||
*/
|
||||
export async function syncChildrenPermission({
|
||||
resource,
|
||||
folderTypeList,
|
||||
@@ -29,7 +35,7 @@ export async function syncChildrenPermission({
|
||||
resourceModel,
|
||||
session,
|
||||
|
||||
collaborators
|
||||
collaborators: latestClbList
|
||||
}: {
|
||||
resource: SyncChildrenPermissionResourceType;
|
||||
|
||||
@@ -42,55 +48,155 @@ export async function syncChildrenPermission({
|
||||
// should be provided when inheritPermission is true
|
||||
session: ClientSession;
|
||||
|
||||
collaborators?: UpdateCollaboratorItem[];
|
||||
collaborators: CollaboratorItemType[];
|
||||
}) {
|
||||
// only folder has permission
|
||||
const isFolder = folderTypeList.includes(resource.type);
|
||||
const teamId = resource.teamId;
|
||||
|
||||
// If the 'root' is not a folder, which means the 'root' has no children, no need to sync.
|
||||
if (!isFolder) return;
|
||||
|
||||
// get all folders and the resource permission of the app
|
||||
// get all the resource permission of the app
|
||||
const allFolders = await resourceModel
|
||||
.find(
|
||||
{
|
||||
teamId: resource.teamId,
|
||||
type: { $in: folderTypeList },
|
||||
inheritPermission: true
|
||||
teamId,
|
||||
inheritPermission: true,
|
||||
type: {
|
||||
$in: folderTypeList
|
||||
}
|
||||
},
|
||||
'_id parentId'
|
||||
)
|
||||
.lean<SyncChildrenPermissionResourceType[]>()
|
||||
.session(session);
|
||||
|
||||
// bfs to get all children
|
||||
const queue = [String(resource._id)];
|
||||
const children: string[] = [];
|
||||
while (queue.length) {
|
||||
const parentId = queue.shift();
|
||||
const folderChildren = allFolders.filter(
|
||||
(folder) => String(folder.parentId) === String(parentId)
|
||||
);
|
||||
children.push(...folderChildren.map((folder) => folder._id));
|
||||
queue.push(...folderChildren.map((folder) => folder._id));
|
||||
}
|
||||
if (!children.length) return;
|
||||
const allClbs = await MongoResourcePermission.find({
|
||||
resourceType,
|
||||
teamId,
|
||||
resourceId: {
|
||||
$in: allFolders.map((folder) => folder._id)
|
||||
}
|
||||
})
|
||||
.lean()
|
||||
.session(session);
|
||||
|
||||
// sync the resource permission
|
||||
if (collaborators) {
|
||||
// Update the collaborators of all children
|
||||
for await (const childId of children) {
|
||||
await syncCollaborators({
|
||||
resourceType,
|
||||
session,
|
||||
collaborators,
|
||||
teamId: resource.teamId,
|
||||
resourceId: childId
|
||||
});
|
||||
/** ResourceMap<resourceId, resourceType> */
|
||||
const resourceMap = new Map<string, SyncChildrenPermissionResourceType>();
|
||||
/** parentChildrenMap<parentId, resourceType[]> */
|
||||
const parentChildrenMap = new Map<string, SyncChildrenPermissionResourceType[]>();
|
||||
|
||||
// init the map
|
||||
allFolders.forEach((resource) => {
|
||||
resourceMap.set(resource._id, resource);
|
||||
const parentId = String(resource.parentId);
|
||||
if (!parentChildrenMap.has(parentId)) {
|
||||
parentChildrenMap.set(parentId, []);
|
||||
}
|
||||
parentChildrenMap.get(parentId)!.push(resource);
|
||||
});
|
||||
|
||||
/** resourceIdPermissionMap<resourceId, CollaboratorItemType[]>
|
||||
* save the clb virtual state, not the real state at present in the DB.
|
||||
*/
|
||||
const resourceIdClbMap = new Map<string, ResourcePermissionType[]>();
|
||||
|
||||
// Initialize the resourceIdPermissionMap
|
||||
for (const clb of allClbs) {
|
||||
const resourceId = clb.resourceId;
|
||||
const arr = resourceIdClbMap.get(resourceId);
|
||||
if (!arr) {
|
||||
resourceIdClbMap.set(resourceId, [clb]);
|
||||
} else {
|
||||
arr.push(clb);
|
||||
}
|
||||
}
|
||||
|
||||
// BFS to get all children
|
||||
const queue = [String(resource._id)];
|
||||
const ops: AnyBulkWriteOperation<ResourcePermissionType>[] = [];
|
||||
const latestClbMap = new Map(latestClbList.map((clb) => [getCollaboratorId(clb), { ...clb }]));
|
||||
|
||||
while (queue.length) {
|
||||
const parentId = String(queue.shift());
|
||||
const _children = parentChildrenMap.get(parentId) || [];
|
||||
if (_children.length === 0) continue;
|
||||
for (const child of _children) {
|
||||
// 1. get parent's permission and what permission I have.
|
||||
const parentClbs = resourceIdClbMap.get(String(child.parentId)) || [];
|
||||
const myClbs = resourceIdClbMap.get(child._id) || [];
|
||||
const myClbsIdSet = new Set(myClbs.map((clb) => getCollaboratorId(clb)));
|
||||
|
||||
// add or update
|
||||
for (const latestClb of latestClbList) {
|
||||
if (latestClb.permission === OwnerRoleVal) {
|
||||
continue;
|
||||
}
|
||||
if (!myClbsIdSet.has(getCollaboratorId(latestClb))) {
|
||||
ops.push({
|
||||
insertOne: {
|
||||
document: {
|
||||
resourceId: child._id,
|
||||
resourceType,
|
||||
teamId,
|
||||
permission: latestClb.permission,
|
||||
...pickCollaboratorIdFields(latestClb)
|
||||
} as ResourcePermissionType
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const myclb = myClbs.find(
|
||||
(clb) => getCollaboratorId(latestClb) === getCollaboratorId(clb)
|
||||
)!;
|
||||
ops.push({
|
||||
updateOne: {
|
||||
filter: {
|
||||
resourceId: child._id,
|
||||
teamId,
|
||||
...pickCollaboratorIdFields(latestClb),
|
||||
resourceType
|
||||
},
|
||||
update: {
|
||||
permission: sumPer(myclb.permission, latestClb.permission)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// delele
|
||||
for (const myClb of myClbs) {
|
||||
const parentClb = parentClbs.find(
|
||||
(clb) => getCollaboratorId(clb) === getCollaboratorId(myClb)
|
||||
);
|
||||
// the new collaborators doesnt have it, and the permission is same.
|
||||
// remove it
|
||||
if (
|
||||
!latestClbMap.get(getCollaboratorId(myClb)) &&
|
||||
parentClb &&
|
||||
myClb.permission === parentClb.permission
|
||||
) {
|
||||
ops.push({
|
||||
deleteOne: {
|
||||
filter: {
|
||||
resourceId: child._id,
|
||||
teamId,
|
||||
...pickCollaboratorIdFields(myClb),
|
||||
resourceType
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
queue.push(child._id);
|
||||
}
|
||||
}
|
||||
await MongoResourcePermission.bulkWrite(ops, { session });
|
||||
return;
|
||||
}
|
||||
|
||||
/* Resume the inherit permission of the resource.
|
||||
/** Resume the inherit permission of the resource.
|
||||
1. Folder: Sync parent's defaultPermission and clbs, and sync its children.
|
||||
2. Resource: Sync parent's defaultPermission, and delete all its clbs.
|
||||
*/
|
||||
@@ -108,9 +214,54 @@ export async function resumeInheritPermission({
|
||||
session?: ClientSession;
|
||||
}) {
|
||||
const isFolder = folderTypeList.includes(resource.type);
|
||||
// Folder resource, need to sync children
|
||||
const [parentClbs, oldMyClbs] = await Promise.all([
|
||||
getResourceOwnedClbs({
|
||||
resourceId: resource.parentId,
|
||||
teamId: resource.teamId,
|
||||
resourceType
|
||||
}),
|
||||
getResourceOwnedClbs({
|
||||
resourceId: resource._id,
|
||||
teamId: resource.teamId,
|
||||
resourceType
|
||||
})
|
||||
]);
|
||||
|
||||
const parentOwner = parentClbs.find((clb) => clb.permission === OwnerRoleVal);
|
||||
|
||||
const collaborators = mergeCollaboratorList({
|
||||
parentClbs,
|
||||
childClbs: oldMyClbs
|
||||
});
|
||||
const parentManage = collaborators.find(
|
||||
(clb) => parentOwner?.tmbId && clb.tmbId && parentOwner?.tmbId === clb.tmbId
|
||||
);
|
||||
if (parentManage) parentManage.permission = ManageRoleVal;
|
||||
|
||||
console.log(collaborators);
|
||||
|
||||
const fn = async (session: ClientSession) => {
|
||||
// update the resource permission
|
||||
if (isFolder) {
|
||||
// sync self
|
||||
await syncCollaborators({
|
||||
resourceType,
|
||||
collaborators,
|
||||
teamId: resource.teamId,
|
||||
resourceId: resource._id,
|
||||
session
|
||||
});
|
||||
// sync children
|
||||
await syncChildrenPermission({
|
||||
resource,
|
||||
resourceModel,
|
||||
folderTypeList,
|
||||
resourceType,
|
||||
session,
|
||||
collaborators
|
||||
});
|
||||
}
|
||||
|
||||
await resourceModel.updateOne(
|
||||
{
|
||||
_id: resource._id
|
||||
@@ -120,39 +271,6 @@ export async function resumeInheritPermission({
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
|
||||
// Folder resource, need to sync children
|
||||
if (isFolder) {
|
||||
const parentClbsAndGroups = await getResourceClbsAndGroups({
|
||||
resourceId: resource.parentId,
|
||||
teamId: resource.teamId,
|
||||
resourceType,
|
||||
session
|
||||
});
|
||||
|
||||
// sync self
|
||||
await syncCollaborators({
|
||||
resourceType,
|
||||
collaborators: parentClbsAndGroups,
|
||||
teamId: resource.teamId,
|
||||
resourceId: resource._id,
|
||||
session
|
||||
});
|
||||
// sync children
|
||||
await syncChildrenPermission({
|
||||
resource: {
|
||||
...resource
|
||||
},
|
||||
resourceModel,
|
||||
folderTypeList,
|
||||
resourceType,
|
||||
session,
|
||||
collaborators: parentClbsAndGroups
|
||||
});
|
||||
} else {
|
||||
// Not folder, delete all clb
|
||||
await MongoResourcePermission.deleteMany({ resourceId: resource._id }, { session });
|
||||
}
|
||||
};
|
||||
|
||||
if (session) {
|
||||
@@ -162,9 +280,9 @@ export async function resumeInheritPermission({
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Delete all the collaborators and then insert the new collaborators.
|
||||
*/
|
||||
/**
|
||||
* sync parent collaborators to children.
|
||||
*/
|
||||
export async function syncCollaborators({
|
||||
resourceType,
|
||||
teamId,
|
||||
@@ -175,30 +293,59 @@ export async function syncCollaborators({
|
||||
resourceType: PerResourceTypeEnum;
|
||||
teamId: string;
|
||||
resourceId: string;
|
||||
collaborators: UpdateCollaboratorItem[];
|
||||
collaborators: CollaboratorItemType[];
|
||||
session: ClientSession;
|
||||
}) {
|
||||
await MongoResourcePermission.deleteMany(
|
||||
{
|
||||
resourceType,
|
||||
teamId,
|
||||
resourceId
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
await MongoResourcePermission.insertMany(
|
||||
collaborators.map((item) => ({
|
||||
teamId: teamId,
|
||||
resourceId,
|
||||
resourceType: resourceType,
|
||||
tmbId: item.tmbId,
|
||||
groupId: item.groupId,
|
||||
orgId: item.orgId,
|
||||
permission: item.permission
|
||||
})),
|
||||
{
|
||||
session,
|
||||
ordered: true
|
||||
// should change parent owner permission into manage
|
||||
collaborators.forEach((clb) => {
|
||||
if (clb.permission === OwnerRoleVal) {
|
||||
clb.permission = ManageRoleVal;
|
||||
}
|
||||
});
|
||||
const parentClbMap = new Map(collaborators.map((clb) => [getCollaboratorId(clb), clb]));
|
||||
const clbsNow = await MongoResourcePermission.find({
|
||||
resourceType,
|
||||
teamId,
|
||||
resourceId
|
||||
})
|
||||
.lean()
|
||||
.session(session);
|
||||
const ops: AnyBulkWriteOperation<ResourcePermissionType>[] = [];
|
||||
for (const clb of clbsNow) {
|
||||
const parentClb = parentClbMap.get(getCollaboratorId(clb));
|
||||
const permission = sumPer(parentClb?.permission ?? NullPermissionVal, clb.permission);
|
||||
ops.push({
|
||||
updateOne: {
|
||||
filter: {
|
||||
teamId,
|
||||
resourceId,
|
||||
resourceType,
|
||||
...pickCollaboratorIdFields(clb)
|
||||
},
|
||||
update: {
|
||||
permission
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const parentHasAndIHaveNot = collaborators.filter(
|
||||
(clb) => !clbsNow.some((myClb) => getCollaboratorId(clb) === getCollaboratorId(myClb))
|
||||
);
|
||||
|
||||
for (const clb of parentHasAndIHaveNot) {
|
||||
ops.push({
|
||||
insertOne: {
|
||||
document: {
|
||||
teamId,
|
||||
resourceId,
|
||||
resourceType,
|
||||
...pickCollaboratorIdFields(clb),
|
||||
permission: clb.permission
|
||||
} as ResourcePermissionType
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await MongoResourcePermission.bulkWrite(ops, { session });
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { type MemberGroupSchemaType } from '@fastgpt/global/support/permission/memberGroup/type';
|
||||
import { MongoGroupMemberModel } from './groupMemberSchema';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { MongoMemberGroupModel } from './memberGroupSchema';
|
||||
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||
import { type ClientSession } from 'mongoose';
|
||||
@@ -9,6 +8,7 @@ import { type AuthModeType, type AuthResponseType } from '../type';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
|
||||
import { getTmbInfoByTmbId } from '../../user/team/controller';
|
||||
import { parseHeaderCert } from '../auth/common';
|
||||
|
||||
/**
|
||||
* Get the default group of a team
|
||||
|
@@ -4,6 +4,7 @@ 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';
|
||||
import { DEFAULT_ORG_AVATAR } from '@fastgpt/global/common/system/constants';
|
||||
const { Schema } = connectionMongo;
|
||||
|
||||
export const OrgSchema = new Schema(
|
||||
@@ -29,7 +30,9 @@ export const OrgSchema = new Schema(
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
avatar: String,
|
||||
avatar: {
|
||||
type: String
|
||||
},
|
||||
description: String,
|
||||
updateTime: {
|
||||
type: Date,
|
||||
|
@@ -1,11 +1,11 @@
|
||||
import { type AppDetailType } from '@fastgpt/global/core/app/type';
|
||||
import { type OutlinkAppType, type OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { MongoOutLink } from '../../outLink/schema';
|
||||
import { OutLinkErrEnum } from '@fastgpt/global/common/error/code/outLink';
|
||||
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { authAppByTmbId } from '../app/auth';
|
||||
import { type AuthModeType, type AuthResponseType } from '../type';
|
||||
import { parseHeaderCert } from '../auth/common';
|
||||
|
||||
/* crud outlink permission */
|
||||
export async function authOutLinkCrud({
|
||||
|
@@ -34,11 +34,18 @@ export const ResourcePermissionSchema = new Schema({
|
||||
enum: Object.values(PerResourceTypeEnum),
|
||||
required: true
|
||||
},
|
||||
/**
|
||||
* The **Role** of the object to the resource.
|
||||
*/
|
||||
permission: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
// Resrouce ID: App or DataSet or any other resource type.
|
||||
/**
|
||||
* Optional. Only be set when the resource is *inherited* from the parent resource.
|
||||
* For recording the self permission. When cancel the inheritance, it will overwrite the permission property and set to `unset`.
|
||||
*/
|
||||
// Resource ID: App or DataSet or any other resource type.
|
||||
// It is null if the resourceType is team.
|
||||
resourceId: {
|
||||
type: Schema.Types.ObjectId
|
||||
|
@@ -1,11 +1,10 @@
|
||||
import { type TeamTmbItemType } from '@fastgpt/global/support/user/team/type';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { getTmbInfoByTmbId } from '../../user/team/controller';
|
||||
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
|
||||
import { type AuthModeType, type AuthResponseType } from '../type';
|
||||
import { NullPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
|
||||
import { authCert } from '../auth/common';
|
||||
import { authCert, parseHeaderCert } from '../auth/common';
|
||||
import { MongoUser } from '../../user/schema';
|
||||
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||
import { type ApiRequestProps } from '../../../type/next';
|
||||
|
9
packages/service/support/permission/utils.ts
Normal file
9
packages/service/support/permission/utils.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { CollaboratorIdType } from '@fastgpt/global/support/permission/collaborator';
|
||||
|
||||
export const pickCollaboratorIdFields = (clb: CollaboratorIdType) => {
|
||||
return {
|
||||
...(clb.tmbId && { tmbId: clb.tmbId }),
|
||||
...(clb.groupId && { groupId: clb.groupId }),
|
||||
...(clb.orgId && { orgId: clb.orgId })
|
||||
};
|
||||
};
|
@@ -8,7 +8,7 @@ import {
|
||||
import { MongoTeamMember } from './teamMemberSchema';
|
||||
import { MongoTeam } from './teamSchema';
|
||||
import { type UpdateTeamProps } from '@fastgpt/global/support/user/team/controller';
|
||||
import { getResourcePermission } from '../../permission/controller';
|
||||
import { getTmbPermission } from '../../permission/controller';
|
||||
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
|
||||
import { TeamDefaultRoleVal } from '@fastgpt/global/support/permission/user/constant';
|
||||
@@ -26,7 +26,7 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
|
||||
}
|
||||
|
||||
const role =
|
||||
(await getResourcePermission({
|
||||
(await getTmbPermission({
|
||||
resourceType: PerResourceTypeEnum.team,
|
||||
teamId: tmb.teamId,
|
||||
tmbId: tmb._id
|
||||
|
Reference in New Issue
Block a user