feat: gate permission
This commit is contained in:
parent
e74ab643fe
commit
81202c53a8
@ -1,9 +1,18 @@
|
|||||||
import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant';
|
import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant';
|
||||||
import { type PermissionListType } from '../type';
|
import { type PermissionListType } from '../type';
|
||||||
import { i18nT } from '../../../../web/i18n/utils';
|
import { i18nT } from '../../../../web/i18n/utils';
|
||||||
|
export enum AppPermissionKeyEnum {}
|
||||||
|
|
||||||
export enum AppPermissionKeyEnum {
|
export enum AppPermissionKeyEnum {
|
||||||
log = 'log'
|
log = 'log',
|
||||||
|
quickGate = 'quickGate',
|
||||||
|
featuredGate = 'featuredGate'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const AppLogPermission = 0b100000;
|
||||||
|
export const GateQuickAppPermission = 0b001100;
|
||||||
|
export const GateFeaturedAppPermission = 0b010100;
|
||||||
|
|
||||||
export const AppPermissionList: PermissionListType<AppPermissionKeyEnum> = {
|
export const AppPermissionList: PermissionListType<AppPermissionKeyEnum> = {
|
||||||
[PermissionKeyEnum.read]: {
|
[PermissionKeyEnum.read]: {
|
||||||
...PermissionList[PermissionKeyEnum.read],
|
...PermissionList[PermissionKeyEnum.read],
|
||||||
@ -15,14 +24,26 @@ export const AppPermissionList: PermissionListType<AppPermissionKeyEnum> = {
|
|||||||
},
|
},
|
||||||
[PermissionKeyEnum.manage]: {
|
[PermissionKeyEnum.manage]: {
|
||||||
...PermissionList[PermissionKeyEnum.manage],
|
...PermissionList[PermissionKeyEnum.manage],
|
||||||
value: 0b1111,
|
value: 0b111111,
|
||||||
description: i18nT('app:permission.des.manage')
|
description: i18nT('app:permission.des.manage')
|
||||||
},
|
},
|
||||||
[AppPermissionKeyEnum.log]: {
|
[AppPermissionKeyEnum.log]: {
|
||||||
name: i18nT('app:permission.name.log'),
|
name: i18nT('app:permission.name.log'),
|
||||||
value: 0b1000,
|
value: AppLogPermission,
|
||||||
checkBoxType: 'multiple',
|
checkBoxType: 'multiple',
|
||||||
description: i18nT('app:permission.des.log')
|
description: i18nT('app:permission.des.log')
|
||||||
|
},
|
||||||
|
[AppPermissionKeyEnum.quickGate]: {
|
||||||
|
name: '门户快捷应用权限',
|
||||||
|
description: '',
|
||||||
|
value: GateQuickAppPermission,
|
||||||
|
checkBoxType: 'multiple' // TODO: 加个隐藏选项
|
||||||
|
},
|
||||||
|
[AppPermissionKeyEnum.featuredGate]: {
|
||||||
|
name: '门户推荐应用权限',
|
||||||
|
description: '',
|
||||||
|
value: GateFeaturedAppPermission,
|
||||||
|
checkBoxType: 'multiple' // TODO: 加个隐藏选项
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,86 @@
|
|||||||
import { MongoTeamGate, gateCollectionName } from './schema';
|
import { MongoTeamGate } from './schema';
|
||||||
import { Types } from '../../../../common/mongo';
|
import { Types } from '../../../../common/mongo';
|
||||||
|
import type { ClientSession } from '../../../../common/mongo';
|
||||||
|
import { mongoSessionRun } from '../../../../common/mongo/sessionRun';
|
||||||
|
import {
|
||||||
|
GateFeaturedAppPermission,
|
||||||
|
GateQuickAppPermission
|
||||||
|
} from '@fastgpt/global/support/permission/app/constant';
|
||||||
|
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||||
|
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||||
|
import { MongoMemberGroupModel } from '../../../permission/memberGroup/memberGroupSchema';
|
||||||
|
import { MongoResourcePermission } from '../../../permission/schema';
|
||||||
|
|
||||||
|
export const addGatePermission = async ({
|
||||||
|
teamId,
|
||||||
|
appId,
|
||||||
|
per,
|
||||||
|
session
|
||||||
|
}: {
|
||||||
|
teamId: string;
|
||||||
|
appId: string;
|
||||||
|
per: number;
|
||||||
|
session?: ClientSession;
|
||||||
|
}) => {
|
||||||
|
// 1. 先找全员组
|
||||||
|
const teamGroup = await MongoMemberGroupModel.findOne({
|
||||||
|
teamId,
|
||||||
|
name: DefaultGroupName
|
||||||
|
});
|
||||||
|
if (!teamGroup) {
|
||||||
|
return Promise.reject('找不到全员组');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 加权限
|
||||||
|
await MongoResourcePermission.updateOne(
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
groupId: teamGroup?._id,
|
||||||
|
resourceType: PerResourceTypeEnum.app,
|
||||||
|
resourceId: appId
|
||||||
|
},
|
||||||
|
{
|
||||||
|
permission: per
|
||||||
|
},
|
||||||
|
{
|
||||||
|
session,
|
||||||
|
upsert: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export const removeGatePermission = async ({
|
||||||
|
teamId,
|
||||||
|
appId,
|
||||||
|
per,
|
||||||
|
session
|
||||||
|
}: {
|
||||||
|
teamId: string;
|
||||||
|
appId: string;
|
||||||
|
per: number;
|
||||||
|
session?: ClientSession;
|
||||||
|
}) => {
|
||||||
|
// 1. 先找全员组
|
||||||
|
const teamGroup = await MongoMemberGroupModel.findOne({
|
||||||
|
teamId,
|
||||||
|
name: DefaultGroupName
|
||||||
|
});
|
||||||
|
if (!teamGroup) {
|
||||||
|
return Promise.reject('找不到全员组');
|
||||||
|
}
|
||||||
|
|
||||||
|
await MongoResourcePermission.deleteOne(
|
||||||
|
{
|
||||||
|
teamId,
|
||||||
|
groupId: teamGroup?._id,
|
||||||
|
resourceType: PerResourceTypeEnum.app,
|
||||||
|
resourceId: appId,
|
||||||
|
permission: per
|
||||||
|
},
|
||||||
|
{
|
||||||
|
session
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建团队门户配置
|
* 创建团队门户配置
|
||||||
@ -133,8 +214,20 @@ export const addFeaturedApp = async ({ teamId, appId }: { teamId: string; appId:
|
|||||||
/**
|
/**
|
||||||
* 删除特色应用
|
* 删除特色应用
|
||||||
*/
|
*/
|
||||||
export const removeFeaturedApp = async ({ teamId, appId }: { teamId: string; appId: string }) => {
|
export const removeFeaturedApp = async ({
|
||||||
await MongoTeamGate.updateOne({ teamId }, { $pull: { featuredApps: new Types.ObjectId(appId) } });
|
teamId,
|
||||||
|
appId,
|
||||||
|
session
|
||||||
|
}: {
|
||||||
|
teamId: string;
|
||||||
|
appId: string;
|
||||||
|
session?: ClientSession;
|
||||||
|
}) => {
|
||||||
|
await MongoTeamGate.updateOne(
|
||||||
|
{ teamId },
|
||||||
|
{ $pull: { featuredApps: new Types.ObjectId(appId) } },
|
||||||
|
{ session }
|
||||||
|
);
|
||||||
return MongoTeamGate.findOne({ teamId }).lean();
|
return MongoTeamGate.findOne({ teamId }).lean();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -252,7 +345,34 @@ export const batchUpdateFeaturedApps = async (
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
await MongoTeamGate.bulkWrite(operations);
|
const teamId = updates[0]?.teamId;
|
||||||
|
|
||||||
|
const gateConfig = await MongoTeamGate.findOne({ teamId });
|
||||||
|
if (!gateConfig) return Promise.reject('无 gate 配置');
|
||||||
|
|
||||||
|
const updatedAppId = updates[0].featuredApps;
|
||||||
|
const deleteAppId = gateConfig.featuredApps.filter((id) => !updatedAppId.includes(id));
|
||||||
|
|
||||||
|
await mongoSessionRun(async (session) => {
|
||||||
|
await MongoTeamGate.bulkWrite(operations, { session });
|
||||||
|
|
||||||
|
for (const id of deleteAppId) {
|
||||||
|
await removeGatePermission({
|
||||||
|
teamId,
|
||||||
|
appId: id,
|
||||||
|
per: GateFeaturedAppPermission,
|
||||||
|
session
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (const id of updatedAppId) {
|
||||||
|
await addGatePermission({
|
||||||
|
teamId,
|
||||||
|
appId: id,
|
||||||
|
per: GateFeaturedAppPermission,
|
||||||
|
session
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -291,10 +411,12 @@ export const batchUpdateToolsOrder = async (
|
|||||||
*/
|
*/
|
||||||
export const batchDeleteFeaturedApps = async ({
|
export const batchDeleteFeaturedApps = async ({
|
||||||
teamId,
|
teamId,
|
||||||
appIds
|
appIds,
|
||||||
|
session
|
||||||
}: {
|
}: {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
appIds: string[];
|
appIds: string[];
|
||||||
|
session?: ClientSession;
|
||||||
}) => {
|
}) => {
|
||||||
if (!appIds || appIds.length === 0) {
|
if (!appIds || appIds.length === 0) {
|
||||||
return false;
|
return false;
|
||||||
@ -302,7 +424,10 @@ export const batchDeleteFeaturedApps = async ({
|
|||||||
|
|
||||||
await MongoTeamGate.updateOne(
|
await MongoTeamGate.updateOne(
|
||||||
{ teamId },
|
{ teamId },
|
||||||
{ $pull: { featuredApps: { $in: appIds.map((id) => new Types.ObjectId(id)) } } }
|
{ $pull: { featuredApps: { $in: appIds.map((id) => new Types.ObjectId(id)) } } },
|
||||||
|
{
|
||||||
|
session
|
||||||
|
}
|
||||||
);
|
);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@ -384,31 +509,39 @@ export const moveQuickAppToPosition = async ({
|
|||||||
/**
|
/**
|
||||||
* 批量更新快速应用
|
* 批量更新快速应用
|
||||||
*/
|
*/
|
||||||
export const batchUpdateQuickApps = async (
|
export const batchUpdateQuickApps = async (teamId: string, quickApps: string[]) => {
|
||||||
updates: {
|
const gateConfig = await MongoTeamGate.findOne({ teamId });
|
||||||
teamId: string;
|
if (!gateConfig) {
|
||||||
quickApps: string[];
|
return false;
|
||||||
}[]
|
|
||||||
) => {
|
|
||||||
const operations = updates.map((update) => {
|
|
||||||
const { teamId, quickApps } = update;
|
|
||||||
// 将字符串数组转换为 ObjectId 数组
|
|
||||||
const objectIdArray = quickApps.map((id) => new Types.ObjectId(id));
|
|
||||||
return {
|
|
||||||
updateOne: {
|
|
||||||
filter: { teamId },
|
|
||||||
update: { $set: { quickApps: objectIdArray } },
|
|
||||||
upsert: true
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
// 计算删除的appId
|
||||||
|
const deleteAppIds = gateConfig.quickApps.filter((id) => !quickApps.includes(id.toString()));
|
||||||
|
|
||||||
|
return mongoSessionRun(async (session) => {
|
||||||
|
// 1. 删除权限
|
||||||
|
for (const id of deleteAppIds) {
|
||||||
|
await removeGatePermission({
|
||||||
|
teamId,
|
||||||
|
appId: id,
|
||||||
|
per: GateQuickAppPermission,
|
||||||
|
session
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 加权限
|
||||||
|
for (const id of quickApps) {
|
||||||
|
await addGatePermission({
|
||||||
|
teamId,
|
||||||
|
appId: id,
|
||||||
|
per: GateQuickAppPermission,
|
||||||
|
session
|
||||||
});
|
});
|
||||||
|
|
||||||
if (operations.length === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await MongoTeamGate.bulkWrite(operations);
|
gateConfig.quickApps = quickApps;
|
||||||
|
await gateConfig.save({ session });
|
||||||
return true;
|
return true;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -62,12 +62,16 @@ const AccountContainer = ({
|
|||||||
label: t('account:usage_records'),
|
label: t('account:usage_records'),
|
||||||
value: TabEnum.usage
|
value: TabEnum.usage
|
||||||
},
|
},
|
||||||
|
...(userInfo?.team?.permission.hasManagePer
|
||||||
|
? [
|
||||||
{
|
{
|
||||||
icon: 'support/gate/gateLight',
|
icon: 'support/gate/gateLight',
|
||||||
label: t('account:gateways'),
|
label: t('account:gateways'),
|
||||||
value: TabEnum.gateway
|
value: TabEnum.gateway
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
: [])
|
||||||
|
]
|
||||||
: []),
|
: []),
|
||||||
...(feConfigs?.show_pay && userInfo?.team?.permission.hasManagePer
|
...(feConfigs?.show_pay && userInfo?.team?.permission.hasManagePer
|
||||||
? [
|
? [
|
||||||
|
|||||||
@ -338,7 +338,8 @@ const AppTable = () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const { openConfirm: openConfirmDel, ConfirmModal: DelConfirmModal } = useConfirm({
|
const { openConfirm: openConfirmDel, ConfirmModal: DelConfirmModal } = useConfirm({
|
||||||
type: 'delete'
|
type: 'delete',
|
||||||
|
title: '确认删除该应用?'
|
||||||
});
|
});
|
||||||
|
|
||||||
const { runAsync: onDeleteApp } = useRequest2(delAppById, {
|
const { runAsync: onDeleteApp } = useRequest2(delAppById, {
|
||||||
@ -487,12 +488,11 @@ const AppTable = () => {
|
|||||||
flexDirection={{ base: 'column', md: 'row' }}
|
flexDirection={{ base: 'column', md: 'row' }}
|
||||||
alignItems={{ base: 'stretch', md: 'center' }}
|
alignItems={{ base: 'stretch', md: 'center' }}
|
||||||
>
|
>
|
||||||
<Flex flex={1} gap={4}>
|
<Flex gap={4}>
|
||||||
<SearchInput
|
<SearchInput
|
||||||
value={search}
|
value={search}
|
||||||
onChange={(e) => setSearch(e.target.value)}
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
placeholder={t('app:search_app')}
|
placeholder={t('app:search_app')}
|
||||||
flex={1}
|
|
||||||
/>
|
/>
|
||||||
<Box w="200px">
|
<Box w="200px">
|
||||||
<Menu closeOnSelect={false}>
|
<Menu closeOnSelect={false}>
|
||||||
|
|||||||
@ -158,12 +158,7 @@ const CopyrightTable = ({
|
|||||||
{t('account_gate:gate_logo')}
|
{t('account_gate:gate_logo')}
|
||||||
</Text>
|
</Text>
|
||||||
|
|
||||||
<Flex
|
<Flex gap={{ base: 4, md: 8 }} alignItems="center" justifyContent="flex-start">
|
||||||
gap={{ base: 4, md: 8 }}
|
|
||||||
alignItems="center"
|
|
||||||
justifyContent="flex-start"
|
|
||||||
flexDirection={{ base: 'column', md: 'row' }}
|
|
||||||
>
|
|
||||||
{/* 左侧 Banner 显示 - 带文字 */}
|
{/* 左侧 Banner 显示 - 带文字 */}
|
||||||
<Flex direction="column" gap={2} alignItems="center">
|
<Flex direction="column" gap={2} alignItems="center">
|
||||||
<Box
|
<Box
|
||||||
@ -369,8 +364,8 @@ const CopyrightTable = ({
|
|||||||
<LogoFile
|
<LogoFile
|
||||||
onSelect={(e: File[]) =>
|
onSelect={(e: File[]) =>
|
||||||
onSelectLogoImage(e, {
|
onSelectLogoImage(e, {
|
||||||
maxH: 300,
|
maxH: 3000,
|
||||||
maxW: 300,
|
maxW: 3000,
|
||||||
callback: (e: string) => {
|
callback: (e: string) => {
|
||||||
setValue('logo', e);
|
setValue('logo', e);
|
||||||
handleGateLogoChange(e);
|
handleGateLogoChange(e);
|
||||||
@ -382,8 +377,8 @@ const CopyrightTable = ({
|
|||||||
<BannerFile
|
<BannerFile
|
||||||
onSelect={(e: File[]) =>
|
onSelect={(e: File[]) =>
|
||||||
onSelectBannerImage(e, {
|
onSelectBannerImage(e, {
|
||||||
maxH: 300,
|
maxH: 3000,
|
||||||
maxW: 300,
|
maxW: 3000,
|
||||||
callback: (e: string) => {
|
callback: (e: string) => {
|
||||||
setValue('banner', e);
|
setValue('banner', e);
|
||||||
handleGateBannerChange(e);
|
handleGateBannerChange(e);
|
||||||
|
|||||||
@ -35,9 +35,7 @@ const GatewayConfig = () => {
|
|||||||
//从 appForm 中获取 selectedTools的 id 组成 string 数组
|
//从 appForm 中获取 selectedTools的 id 组成 string 数组
|
||||||
|
|
||||||
//gateConfig?.tools 改成
|
//gateConfig?.tools 改成
|
||||||
const [copyRightConfig, setCopyRightConfig] = useState<
|
const [copyRightConfig, setCopyRightConfig] = useState<getGateConfigCopyRightResponse>();
|
||||||
getGateConfigCopyRightResponse | undefined
|
|
||||||
>(undefined);
|
|
||||||
const [tab, setTab] = useState<TabType>('home');
|
const [tab, setTab] = useState<TabType>('home');
|
||||||
const [isLoadingApps, setIsLoadingApps] = useState(true);
|
const [isLoadingApps, setIsLoadingApps] = useState(true);
|
||||||
const [gateApps, setGateApps] = useState<AppListItemType[]>([]);
|
const [gateApps, setGateApps] = useState<AppListItemType[]>([]);
|
||||||
@ -247,8 +245,6 @@ const GatewayConfig = () => {
|
|||||||
}, [
|
}, [
|
||||||
gateConfig,
|
gateConfig,
|
||||||
copyRightConfig,
|
copyRightConfig,
|
||||||
isLoadingApps,
|
|
||||||
gateApps,
|
|
||||||
Tab,
|
Tab,
|
||||||
isAppTab,
|
isAppTab,
|
||||||
tab,
|
tab,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user