diff --git a/packages/global/support/permission/app/constant.ts b/packages/global/support/permission/app/constant.ts index 4a3844ad7..8dd2e468f 100644 --- a/packages/global/support/permission/app/constant.ts +++ b/packages/global/support/permission/app/constant.ts @@ -1,9 +1,18 @@ import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant'; import { type PermissionListType } from '../type'; import { i18nT } from '../../../../web/i18n/utils'; +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 = { [PermissionKeyEnum.read]: { ...PermissionList[PermissionKeyEnum.read], @@ -15,14 +24,26 @@ export const AppPermissionList: PermissionListType = { }, [PermissionKeyEnum.manage]: { ...PermissionList[PermissionKeyEnum.manage], - value: 0b1111, + value: 0b111111, description: i18nT('app:permission.des.manage') }, [AppPermissionKeyEnum.log]: { name: i18nT('app:permission.name.log'), - value: 0b1000, + value: AppLogPermission, checkBoxType: 'multiple', description: i18nT('app:permission.des.log') + }, + [AppPermissionKeyEnum.quickGate]: { + name: '门户快捷应用权限', + description: '', + value: GateQuickAppPermission, + checkBoxType: 'multiple' // TODO: 加个隐藏选项 + }, + [AppPermissionKeyEnum.featuredGate]: { + name: '门户推荐应用权限', + description: '', + value: GateFeaturedAppPermission, + checkBoxType: 'multiple' // TODO: 加个隐藏选项 } }; diff --git a/packages/service/support/user/team/gate/controller.ts b/packages/service/support/user/team/gate/controller.ts index 478a18b98..2d9961c0e 100644 --- a/packages/service/support/user/team/gate/controller.ts +++ b/packages/service/support/user/team/gate/controller.ts @@ -1,5 +1,86 @@ -import { MongoTeamGate, gateCollectionName } from './schema'; +import { MongoTeamGate } from './schema'; 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 }) => { - await MongoTeamGate.updateOne({ teamId }, { $pull: { featuredApps: new Types.ObjectId(appId) } }); +export const removeFeaturedApp = async ({ + 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(); }; @@ -252,7 +345,34 @@ export const batchUpdateFeaturedApps = async ( 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; }; @@ -291,10 +411,12 @@ export const batchUpdateToolsOrder = async ( */ export const batchDeleteFeaturedApps = async ({ teamId, - appIds + appIds, + session }: { teamId: string; appIds: string[]; + session?: ClientSession; }) => { if (!appIds || appIds.length === 0) { return false; @@ -302,7 +424,10 @@ export const batchDeleteFeaturedApps = async ({ await MongoTeamGate.updateOne( { teamId }, - { $pull: { featuredApps: { $in: appIds.map((id) => new Types.ObjectId(id)) } } } + { $pull: { featuredApps: { $in: appIds.map((id) => new Types.ObjectId(id)) } } }, + { + session + } ); return true; }; @@ -384,31 +509,39 @@ export const moveQuickAppToPosition = async ({ /** * 批量更新快速应用 */ -export const batchUpdateQuickApps = async ( - updates: { - teamId: string; - quickApps: string[]; - }[] -) => { - 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 - } - }; - }); - - if (operations.length === 0) { - return true; +export const batchUpdateQuickApps = async (teamId: string, quickApps: string[]) => { + const gateConfig = await MongoTeamGate.findOne({ teamId }); + if (!gateConfig) { + return false; } - await MongoTeamGate.bulkWrite(operations); - return 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 + }); + } + + gateConfig.quickApps = quickApps; + await gateConfig.save({ session }); + return true; + }); }; /** diff --git a/projects/app/src/pageComponents/account/AccountContainer.tsx b/projects/app/src/pageComponents/account/AccountContainer.tsx index b1833d0e8..88cd6fbb0 100644 --- a/projects/app/src/pageComponents/account/AccountContainer.tsx +++ b/projects/app/src/pageComponents/account/AccountContainer.tsx @@ -62,11 +62,15 @@ const AccountContainer = ({ label: t('account:usage_records'), value: TabEnum.usage }, - { - icon: 'support/gate/gateLight', - label: t('account:gateways'), - value: TabEnum.gateway - } + ...(userInfo?.team?.permission.hasManagePer + ? [ + { + icon: 'support/gate/gateLight', + label: t('account:gateways'), + value: TabEnum.gateway + } + ] + : []) ] : []), ...(feConfigs?.show_pay && userInfo?.team?.permission.hasManagePer diff --git a/projects/app/src/pageComponents/account/gateway/AppTable.tsx b/projects/app/src/pageComponents/account/gateway/AppTable.tsx index 1eb7bb037..c6f68856e 100644 --- a/projects/app/src/pageComponents/account/gateway/AppTable.tsx +++ b/projects/app/src/pageComponents/account/gateway/AppTable.tsx @@ -338,7 +338,8 @@ const AppTable = () => { ); const { openConfirm: openConfirmDel, ConfirmModal: DelConfirmModal } = useConfirm({ - type: 'delete' + type: 'delete', + title: '确认删除该应用?' }); const { runAsync: onDeleteApp } = useRequest2(delAppById, { @@ -487,12 +488,11 @@ const AppTable = () => { flexDirection={{ base: 'column', md: 'row' }} alignItems={{ base: 'stretch', md: 'center' }} > - + setSearch(e.target.value)} placeholder={t('app:search_app')} - flex={1} /> diff --git a/projects/app/src/pageComponents/account/gateway/CopyrightTable.tsx b/projects/app/src/pageComponents/account/gateway/CopyrightTable.tsx index 5bf17fa31..85219dd56 100644 --- a/projects/app/src/pageComponents/account/gateway/CopyrightTable.tsx +++ b/projects/app/src/pageComponents/account/gateway/CopyrightTable.tsx @@ -158,12 +158,7 @@ const CopyrightTable = ({ {t('account_gate:gate_logo')} - + {/* 左侧 Banner 显示 - 带文字 */} onSelectLogoImage(e, { - maxH: 300, - maxW: 300, + maxH: 3000, + maxW: 3000, callback: (e: string) => { setValue('logo', e); handleGateLogoChange(e); @@ -382,8 +377,8 @@ const CopyrightTable = ({ onSelectBannerImage(e, { - maxH: 300, - maxW: 300, + maxH: 3000, + maxW: 3000, callback: (e: string) => { setValue('banner', e); handleGateBannerChange(e); diff --git a/projects/app/src/pages/account/gateway/index.tsx b/projects/app/src/pages/account/gateway/index.tsx index 88e9d3e06..1c3d198d4 100644 --- a/projects/app/src/pages/account/gateway/index.tsx +++ b/projects/app/src/pages/account/gateway/index.tsx @@ -35,9 +35,7 @@ const GatewayConfig = () => { //从 appForm 中获取 selectedTools的 id 组成 string 数组 //gateConfig?.tools 改成 - const [copyRightConfig, setCopyRightConfig] = useState< - getGateConfigCopyRightResponse | undefined - >(undefined); + const [copyRightConfig, setCopyRightConfig] = useState(); const [tab, setTab] = useState('home'); const [isLoadingApps, setIsLoadingApps] = useState(true); const [gateApps, setGateApps] = useState([]); @@ -247,8 +245,6 @@ const GatewayConfig = () => { }, [ gateConfig, copyRightConfig, - isLoadingApps, - gateApps, Tab, isAppTab, tab,