diff --git a/packages/global/core/app/collaborator.d.ts b/packages/global/core/app/collaborator.d.ts index ca0fec7217f0..66a456379d55 100644 --- a/packages/global/core/app/collaborator.d.ts +++ b/packages/global/core/app/collaborator.d.ts @@ -1,6 +1,6 @@ -import { RequireOnlyOne } from '../../common/type/utils'; +import type { RequireOnlyOne } from '../../common/type/utils'; import { - UpdateClbPermissionProps, + type UpdateClbPermissionProps, UpdatePermissionBody } from '../../support/permission/collaborator'; import { PermissionValueType } from '../../support/permission/type'; @@ -14,4 +14,5 @@ export type AppCollaboratorDeleteParams = { } & RequireOnlyOne<{ tmbId: string; groupId: string; + orgId: string; }>; diff --git a/packages/global/support/permission/collaborator.d.ts b/packages/global/support/permission/collaborator.d.ts index 60a84a9d58e9..d5c9b3060a79 100644 --- a/packages/global/support/permission/collaborator.d.ts +++ b/packages/global/support/permission/collaborator.d.ts @@ -10,17 +10,20 @@ export type CollaboratorItemType = { } & RequireOnlyOne<{ tmbId: string; groupId: string; + orgId: string; }>; export type UpdateClbPermissionProps = { members?: string[]; groups?: string[]; + orgs?: string[]; permission: PermissionValueType; }; export type DeleteClbPermissionProps = RequireOnlyOne<{ tmbId: string; groupId: string; + orgId: string; }>; export type UpdatePermissionBody = { @@ -28,4 +31,5 @@ export type UpdatePermissionBody = { } & RequireOnlyOne<{ memberId: string; groupId: string; + orgId: string; }>; diff --git a/packages/global/support/permission/type.d.ts b/packages/global/support/permission/type.d.ts index f6f29c52a863..df2ba91250b7 100644 --- a/packages/global/support/permission/type.d.ts +++ b/packages/global/support/permission/type.d.ts @@ -1,7 +1,7 @@ -import { RequireOnlyOne } from '../../common/type/utils'; -import { TeamMemberWithUserSchema } from '../user/team/type'; -import { AuthUserTypeEnum, PermissionKeyEnum, PerResourceTypeEnum } from './constant'; -import { MemberGroupSchemaType } from './memberGroup/type'; +import type { RequireOnlyOne } from '../../common/type/utils'; +import type { TeamMemberWithUserSchema } from '../user/team/type'; +import { AuthUserTypeEnum, type PermissionKeyEnum, type PerResourceTypeEnum } from './constant'; +import type { MemberGroupSchemaType } from './memberGroup/type'; // PermissionValueType, the type of permission's value is a number, which is a bit field actually. // It is spired by the permission system in Linux. @@ -28,6 +28,7 @@ export type ResourcePermissionType = { } & RequireOnlyOne<{ tmbId: string; groupId: string; + orgId: string; }>; export type ResourcePerWithTmbWithUser = Omit & { @@ -38,6 +39,10 @@ export type ResourcePerWithGroup = Omit & { groupId: MemberGroupSchemaType; }; +export type ResourcePerWithOrg = Omit & { + orgId: OrgSchemaType; +}; + export type PermissionSchemaType = { defaultPermission: PermissionValueType; inheritPermission: boolean; diff --git a/packages/service/support/permission/controller.ts b/packages/service/support/permission/controller.ts index c68f07d1bef0..90a336cedf2d 100644 --- a/packages/service/support/permission/controller.ts +++ b/packages/service/support/permission/controller.ts @@ -1,26 +1,30 @@ -import Cookie from 'cookie'; +import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode'; -import jwt from 'jsonwebtoken'; -import { NextApiResponse } from 'next'; -import type { AuthModeType, ReqHeaderAuthType } from './type.d'; -import { AuthUserTypeEnum, PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant'; -import { authOpenApiKey } from '../openapi/auth'; -import { FileTokenQuery } from '@fastgpt/global/common/file/type'; -import { MongoResourcePermission } from './schema'; -import { ClientSession } from 'mongoose'; +import { bucketNameMap } from '@fastgpt/global/common/file/constants'; +import type { FileTokenQuery } from '@fastgpt/global/common/file/type'; +import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type'; +import { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; import { + AuthUserTypeEnum, + type PerResourceTypeEnum +} from '@fastgpt/global/support/permission/constant'; +import { Permission } from '@fastgpt/global/support/permission/controller'; +import type { PermissionValueType, - ResourcePermissionType, ResourcePerWithGroup, - ResourcePerWithTmbWithUser + ResourcePerWithOrg, + ResourcePerWithTmbWithUser, + ResourcePermissionType } from '@fastgpt/global/support/permission/type'; -import { bucketNameMap } from '@fastgpt/global/common/file/constants'; +import Cookie from 'cookie'; import { addMinutes } from 'date-fns'; +import jwt from 'jsonwebtoken'; +import type { ClientSession } from 'mongoose'; +import type { NextApiResponse } from 'next'; +import { authOpenApiKey } from '../openapi/auth'; import { getGroupsByTmbId } from './memberGroup/controllers'; -import { Permission } from '@fastgpt/global/support/permission/controller'; -import { ParentIdType } from '@fastgpt/global/common/parentFolder/type'; -import { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; -import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; +import { MongoResourcePermission } from './schema'; +import type { AuthModeType, ReqHeaderAuthType } from './type.d'; /** get resource permission for a team member * If there is no permission for the team member, it will return undefined @@ -185,7 +189,15 @@ export const getClbsAndGroupsWithInfo = async ({ }).populate({ path: 'groupId', select: 'name avatar' - })) as ResourcePerWithGroup[] + })) as ResourcePerWithGroup[], + (await MongoResourcePermission.find({ + teamId, + resourceId, + resourceType, + orgId: { + $exists: true + } + }).populate({ path: 'orgId', select: 'name avatar' })) as ResourcePerWithOrg[] ]); export const delResourcePermissionById = (id: string) => { @@ -195,6 +207,7 @@ export const delResourcePermission = ({ session, tmbId, groupId, + orgId, ...props }: { resourceType: PerResourceTypeEnum; @@ -203,15 +216,18 @@ export const delResourcePermission = ({ session?: ClientSession; tmbId?: string; groupId?: string; + orgId?: string; }) => { - // tmbId or groupId only one and not both - if (!!tmbId === !!groupId) { + // tmbId, groupId, orgId 三选一 + if (!tmbId && !groupId && !orgId) { return Promise.reject(CommonErrEnum.missingParams); } + return MongoResourcePermission.deleteOne( { ...(tmbId ? { tmbId } : {}), ...(groupId ? { groupId } : {}), + ...(orgId ? { orgId } : {}), ...props }, { session } @@ -249,7 +265,7 @@ export function authJWT(token: string) { }>((resolve, reject) => { const key = process.env.TOKEN_KEY as string; - jwt.verify(token, key, function (err, decoded: any) { + jwt.verify(token, key, (err, decoded: any) => { if (err || !decoded?.userId) { reject(ERROR_ENUM.unAuthorization); return; @@ -435,7 +451,7 @@ export const authFileToken = (token?: string) => } const key = (process.env.FILE_TOKEN_KEY as string) ?? 'filetoken'; - jwt.verify(token, key, function (err, decoded: any) { + jwt.verify(token, key, (err, decoded: any) => { if (err || !decoded.bucketName || !decoded?.teamId || !decoded?.fileId) { reject(ERROR_ENUM.unAuthFile); return; diff --git a/packages/service/support/permission/inheritPermission.ts b/packages/service/support/permission/inheritPermission.ts index c3ac42eb8a3f..4f9993f5a9f3 100644 --- a/packages/service/support/permission/inheritPermission.ts +++ b/packages/service/support/permission/inheritPermission.ts @@ -1,11 +1,11 @@ import { mongoSessionRun } from '../../common/mongo/sessionRun'; import { MongoResourcePermission } from './schema'; -import { ClientSession, Model } from 'mongoose'; -import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant'; -import { PermissionValueType } from '@fastgpt/global/support/permission/type'; +import type { ClientSession, Model } from 'mongoose'; +import type { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant'; +import type { PermissionValueType } from '@fastgpt/global/support/permission/type'; import { getResourceClbsAndGroups } from './controller'; -import { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; -import { ParentIdType } from '@fastgpt/global/common/parentFolder/type'; +import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; +import type { ParentIdType } from '@fastgpt/global/common/parentFolder/type'; export type SyncChildrenPermissionResourceType = { _id: string; @@ -18,6 +18,7 @@ export type UpdateCollaboratorItem = { } & RequireOnlyOne<{ tmbId: string; groupId: string; + orgId: string; }>; // sync the permission to all children folders. @@ -161,7 +162,7 @@ export async function resumeInheritPermission({ } } -/* +/* Delete all the collaborators and then insert the new collaborators. */ export async function syncCollaborators({ diff --git a/projects/app/src/components/support/permission/MemberManager/AddMemberModal.tsx b/projects/app/src/components/support/permission/MemberManager/AddMemberModal.tsx index e113fec3dcba..ac2b860a5440 100644 --- a/projects/app/src/components/support/permission/MemberManager/AddMemberModal.tsx +++ b/projects/app/src/components/support/permission/MemberManager/AddMemberModal.tsx @@ -1,27 +1,28 @@ +import { useUserStore } from '@/web/support/user/useUserStore'; +import { ChevronDownIcon } from '@chakra-ui/icons'; import { - Flex, Box, - ModalBody, - Checkbox, - ModalFooter, Button, + Checkbox, + Flex, Grid, - HStack + HStack, + ModalBody, + ModalFooter } from '@chakra-ui/react'; -import MyModal from '@fastgpt/web/components/common/MyModal'; -import MyIcon from '@fastgpt/web/components/common/Icon'; -import { useContextSelector } from 'use-context-selector'; +import { DEFAULT_ORG_AVATAR } from '@fastgpt/global/common/system/constants'; +import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant'; import MyAvatar from '@fastgpt/web/components/common/Avatar'; +import MyIcon from '@fastgpt/web/components/common/Icon'; +import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; +import MyModal from '@fastgpt/web/components/common/MyModal'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; +import { useTranslation } from 'next-i18next'; import { useMemo, useState } from 'react'; +import { useContextSelector } from 'use-context-selector'; import PermissionSelect from './PermissionSelect'; import PermissionTags from './PermissionTags'; import { CollaboratorContext } from './context'; -import { useUserStore } from '@/web/support/user/useUserStore'; -import { ChevronDownIcon } from '@chakra-ui/icons'; -import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; -import { useTranslation } from 'next-i18next'; -import SearchInput from '@fastgpt/web/components/common/Input/SearchInput'; -import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant'; export type AddModalPropsType = { onClose: () => void; @@ -30,16 +31,24 @@ export type AddModalPropsType = { function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) { const { t } = useTranslation(); - const { userInfo, loadAndGetTeamMembers, loadAndGetGroups, myGroups } = useUserStore(); + const { userInfo, loadAndGetTeamMembers, loadAndGetGroups, myGroups, loadAndGetOrgs, myOrgs } = + useUserStore(); const { permissionList, collaboratorList, onUpdateCollaborators, getPerLabelList, permission } = useContextSelector(CollaboratorContext, (v) => v); const [searchText, setSearchText] = useState(''); - const { data: [members = [], groups = []] = [], loading: loadingMembersAndGroups } = useRequest2( + const { + data: [members = [], groups = [], orgs = []] = [], + loading: loadingMembersAndGroups + } = useRequest2( async () => { if (!userInfo?.team?.teamId) return [[], []]; - return await Promise.all([loadAndGetTeamMembers(true), loadAndGetGroups(true)]); + return await Promise.all([ + loadAndGetTeamMembers(true), + loadAndGetGroups(true), + loadAndGetOrgs(true) + ]); }, { manual: false, @@ -65,8 +74,20 @@ function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) { }); }, [groups, searchText, myGroups, mode, permission]); + const filterOrgs = useMemo(() => { + if (mode !== 'all') return []; + return orgs.filter((item) => { + if (item.path === '') return false; // exclude root org + if (!permission.isOwner && myOrgs.find((i) => String(i._id) !== String(item._id))) + return false; + if (!searchText) return true; + return item.name.includes(searchText); + }); + }, [orgs, searchText, myOrgs, mode, permission]); + const [selectedMemberIdList, setSelectedMembers] = useState([]); const [selectedGroupIdList, setSelectedGroupIdList] = useState([]); + const [selectedOrgIdList, setSelectedOrgIdList] = useState([]); const [selectedPermission, setSelectedPermission] = useState(permissionList['read'].value); const perLabel = useMemo(() => { return getPerLabelList(selectedPermission).join('、'); @@ -77,6 +98,7 @@ function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) { onUpdateCollaborators({ members: selectedMemberIdList, groups: selectedGroupIdList, + orgs: selectedOrgIdList, permission: selectedPermission }), { @@ -115,6 +137,48 @@ function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) { /> + {filterOrgs.map((org) => { + const onChange = () => { + setSelectedOrgIdList((state) => { + if (state.includes(org._id)) { + return state.filter((v) => v !== org._id); + } + return [...state, org._id]; + }); + }; + const collaborator = collaboratorList.find((v) => v.orgId === org._id); + return ( + + + + + {org.name} + + {!!collaborator && ( + + )} + + ); + })} {filterGroups.map((group) => { const onChange = () => { setSelectedGroupIdList((state) => { @@ -199,9 +263,43 @@ function AddMemberModal({ onClose, mode = 'member' }: AddModalPropsType) { {t('user:has_chosen') + ': '}{' '} - {selectedMemberIdList.length + selectedGroupIdList.length} + {selectedMemberIdList.length + selectedGroupIdList.length + selectedOrgIdList.length} + {selectedOrgIdList.map((orgId) => { + const org = orgs.find((v) => String(v._id) === orgId); + return ( + + setSelectedOrgIdList(selectedOrgIdList.filter((v) => v !== orgId)) + } + > + + + {org?.name} + + + + ); + })} {selectedGroupIdList.map((groupId) => { const onChange = () => { setSelectedGroupIdList((state) => { diff --git a/projects/app/src/components/support/permission/MemberManager/ManageModal.tsx b/projects/app/src/components/support/permission/MemberManager/ManageModal.tsx index 37dc4260ec02..9376dde338dd 100644 --- a/projects/app/src/components/support/permission/MemberManager/ManageModal.tsx +++ b/projects/app/src/components/support/permission/MemberManager/ManageModal.tsx @@ -1,19 +1,20 @@ -import { ModalBody, Table, TableContainer, Tbody, Th, Thead, Tr, Td, Flex } from '@chakra-ui/react'; +import { useUserStore } from '@/web/support/user/useUserStore'; +import { Flex, ModalBody, Table, TableContainer, Tbody, Td, Th, Thead, Tr } from '@chakra-ui/react'; +import { DEFAULT_ORG_AVATAR } from '@fastgpt/global/common/system/constants'; +import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; +import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant'; +import Avatar from '@fastgpt/web/components/common/Avatar'; +import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; +import MyIcon from '@fastgpt/web/components/common/Icon'; +import Loading from '@fastgpt/web/components/common/MyLoading'; import MyModal from '@fastgpt/web/components/common/MyModal'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; +import { useTranslation } from 'next-i18next'; import React from 'react'; import { useContextSelector } from 'use-context-selector'; import PermissionSelect from './PermissionSelect'; import PermissionTags from './PermissionTags'; -import Avatar from '@fastgpt/web/components/common/Avatar'; import { CollaboratorContext } from './context'; -import MyIcon from '@fastgpt/web/components/common/Icon'; -import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; -import { useUserStore } from '@/web/support/user/useUserStore'; -import EmptyTip from '@fastgpt/web/components/common/EmptyTip'; -import Loading from '@fastgpt/web/components/common/MyLoading'; -import { useTranslation } from 'next-i18next'; -import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant'; -import { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; export type ManageModalProps = { onClose: () => void; }; @@ -65,7 +66,15 @@ function ManageModal({ onClose }: ManageModalProps) { > - + {item.name === DefaultGroupName ? userInfo?.team.teamName : item.name} @@ -85,14 +94,20 @@ function ManageModal({ onClose }: ManageModalProps) { onUpdate({ members: item.tmbId ? [item.tmbId] : undefined, groups: item.groupId ? [item.groupId] : undefined, + orgs: item.orgId ? [item.orgId] : undefined, permission }); }} onDelete={() => { onDelete({ tmbId: item.tmbId, - groupId: item.groupId - } as RequireOnlyOne<{ tmbId: string; groupId: string }>); + groupId: item.groupId, + orgId: item.orgId + } as RequireOnlyOne<{ + tmbId: string; + groupId: string; + orgId: string; + }>); }} /> )} diff --git a/projects/app/src/components/support/permission/MemberManager/MemberListCard.tsx b/projects/app/src/components/support/permission/MemberManager/MemberListCard.tsx index 477c777f7f29..89e7bacdd540 100644 --- a/projects/app/src/components/support/permission/MemberManager/MemberListCard.tsx +++ b/projects/app/src/components/support/permission/MemberManager/MemberListCard.tsx @@ -1,13 +1,14 @@ -import { Box, BoxProps, Flex } from '@chakra-ui/react'; +import { useUserStore } from '@/web/support/user/useUserStore'; +import { Box, type BoxProps, Flex } from '@chakra-ui/react'; +import { DEFAULT_ORG_AVATAR } from '@fastgpt/global/common/system/constants'; +import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant'; +import Avatar from '@fastgpt/web/components/common/Avatar'; import MyBox from '@fastgpt/web/components/common/MyBox'; +import Tag, { type TagProps } from '@fastgpt/web/components/common/Tag'; +import { useTranslation } from 'next-i18next'; import React from 'react'; import { useContextSelector } from 'use-context-selector'; import { CollaboratorContext } from './context'; -import Tag, { TagProps } from '@fastgpt/web/components/common/Tag'; -import Avatar from '@fastgpt/web/components/common/Avatar'; -import { useTranslation } from 'next-i18next'; -import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant'; -import { useUserStore } from '@/web/support/user/useUserStore'; export type MemberListCardProps = BoxProps & { tagStyle?: Omit }; @@ -31,12 +32,18 @@ const MemberListCard = ({ tagStyle, ...props }: MemberListCardProps) => { {collaboratorList?.map((member) => { return ( - + {member.name === DefaultGroupName ? userInfo?.team.teamName : member.name} diff --git a/projects/app/src/components/support/permission/MemberManager/context.tsx b/projects/app/src/components/support/permission/MemberManager/context.tsx index 5ec06bd935af..dd13afafc195 100644 --- a/projects/app/src/components/support/permission/MemberManager/context.tsx +++ b/projects/app/src/components/support/permission/MemberManager/context.tsx @@ -1,21 +1,24 @@ import { useDisclosure } from '@chakra-ui/react'; -import { +import type { CollaboratorItemType, UpdateClbPermissionProps } from '@fastgpt/global/support/permission/collaborator'; import { PermissionList } from '@fastgpt/global/support/permission/constant'; import { Permission } from '@fastgpt/global/support/permission/controller'; -import { PermissionListType, PermissionValueType } from '@fastgpt/global/support/permission/type'; -import { ReactNode, useCallback } from 'react'; +import type { + PermissionListType, + PermissionValueType +} from '@fastgpt/global/support/permission/type'; +import { type ReactNode, useCallback } from 'react'; import { createContext } from 'use-context-selector'; import dynamic from 'next/dynamic'; -import MemberListCard, { MemberListCardProps } from './MemberListCard'; +import MemberListCard, { type MemberListCardProps } from './MemberListCard'; import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; import { useSystemStore } from '@/web/common/system/useSystemStore'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { useI18n } from '@/web/context/I18n'; -import { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; +import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; const AddMemberModal = dynamic(() => import('./AddMemberModal')); const ManageModal = dynamic(() => import('./ManageModal')); @@ -24,7 +27,9 @@ export type MemberManagerInputPropsType = { onGetCollaboratorList: () => Promise; permissionList: PermissionListType; onUpdateCollaborators: (props: UpdateClbPermissionProps) => Promise; - onDelOneCollaborator: (props: RequireOnlyOne<{ tmbId: string; groupId: string }>) => Promise; + onDelOneCollaborator: ( + props: RequireOnlyOne<{ tmbId: string; groupId: string; orgId: string }> + ) => Promise; refreshDeps?: any[]; mode?: 'member' | 'all'; }; @@ -46,19 +51,19 @@ type CollaboratorContextType = MemberManagerPropsType & {}; export const CollaboratorContext = createContext({ collaboratorList: [], permissionList: PermissionList, - onUpdateCollaborators: function () { + onUpdateCollaborators: () => { throw new Error('Function not implemented.'); }, - onDelOneCollaborator: function () { + onDelOneCollaborator: () => { throw new Error('Function not implemented.'); }, - getPerLabelList: function (): string[] { + getPerLabelList: (): string[] => { throw new Error('Function not implemented.'); }, - refetchCollaboratorList: function (): void { + refetchCollaboratorList: (): void => { throw new Error('Function not implemented.'); }, - onGetCollaboratorList: function (): Promise { + onGetCollaboratorList: (): Promise => { throw new Error('Function not implemented.'); }, isFetchingCollaborator: false, @@ -88,7 +93,7 @@ const CollaboratorContextProvider = ({ refetchCollaboratorList(); }; const onDelOneCollaboratorThen = async ( - props: RequireOnlyOne<{ tmbId: string; groupId: string }> + props: RequireOnlyOne<{ tmbId: string; groupId: string; orgId: string }> ) => { await onDelOneCollaborator(props); refetchCollaboratorList(); diff --git a/projects/app/src/pages/app/detail/components/InfoModal.tsx b/projects/app/src/pages/app/detail/components/InfoModal.tsx index 989aa0195fc1..07e2ff1592cf 100644 --- a/projects/app/src/pages/app/detail/components/InfoModal.tsx +++ b/projects/app/src/pages/app/detail/components/InfoModal.tsx @@ -1,40 +1,40 @@ -import React, { useCallback } from 'react'; +import CollaboratorContextProvider from '@/components/support/permission/MemberManager/context'; +import ResumeInherit from '@/components/support/permission/ResumeInheritText'; +import { AppContext } from '@/pages/app/detail/components/context'; +import { compressImgFileAndUpload } from '@/web/common/file/controller'; +import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; +import { useI18n } from '@/web/context/I18n'; +import { resumeInheritPer } from '@/web/core/app/api'; +import { + deleteAppCollaborators, + getCollaboratorList, + postUpdateAppCollaborators +} from '@/web/core/app/api/collaborator'; import { Box, - Flex, Button, + Flex, FormControl, Input, - Textarea, + ModalBody, ModalFooter, - ModalBody + Textarea } from '@chakra-ui/react'; -import { useForm } from 'react-hook-form'; -import { AppSchema } from '@fastgpt/global/core/app/type.d'; -import { useToast } from '@fastgpt/web/hooks/useToast'; -import { useSelectFile } from '@/web/common/file/hooks/useSelectFile'; -import { compressImgFileAndUpload } from '@/web/common/file/controller'; import { getErrText } from '@fastgpt/global/common/error/utils'; -import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; +import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants'; +import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; +import type { AppSchema } from '@fastgpt/global/core/app/type.d'; +import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant'; +import type { PermissionValueType } from '@fastgpt/global/support/permission/type'; import Avatar from '@fastgpt/web/components/common/Avatar'; +import MyIcon from '@fastgpt/web/components/common/Icon'; import MyModal from '@fastgpt/web/components/common/MyModal'; +import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; +import { useToast } from '@fastgpt/web/hooks/useToast'; import { useTranslation } from 'next-i18next'; -import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants'; -import CollaboratorContextProvider from '@/components/support/permission/MemberManager/context'; -import { - postUpdateAppCollaborators, - deleteAppCollaborators, - getCollaboratorList -} from '@/web/core/app/api/collaborator'; +import React, { useCallback } from 'react'; +import { useForm } from 'react-hook-form'; import { useContextSelector } from 'use-context-selector'; -import { AppContext } from '@/pages/app/detail/components/context'; -import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant'; -import MyIcon from '@fastgpt/web/components/common/Icon'; -import { resumeInheritPer } from '@/web/core/app/api'; -import { useI18n } from '@/web/context/I18n'; -import ResumeInherit from '@/components/support/permission/ResumeInheritText'; -import { PermissionValueType } from '@fastgpt/global/support/permission/type'; -import { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; const InfoModal = ({ onClose }: { onClose: () => void }) => { const { t } = useTranslation(); @@ -126,20 +126,23 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => { const onUpdateCollaborators = ({ members, groups, + orgs, permission }: { members?: string[]; groups?: string[]; + orgs?: string[]; permission: PermissionValueType; }) => postUpdateAppCollaborators({ members, groups, permission, + orgs, appId: appDetail._id }); - const onDelCollaborator = async (props: RequireOnlyOne<{ tmbId: string; groupId: string }>) => + const onDelCollaborator = async (props: RequireOnlyOne<{ tmbId: string; groupId: string; orgId: string }>) => deleteAppCollaborators({ appId: appDetail._id, ...props @@ -211,7 +214,8 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => { onUpdateCollaborators({ permission: props.permission, members: props.members, - groups: props.groups + groups: props.groups, + orgs: props.orgs }) } onDelOneCollaborator={onDelCollaborator} diff --git a/projects/app/src/web/support/user/useUserStore.ts b/projects/app/src/web/support/user/useUserStore.ts index 5cf25a22325c..d18c391e82c8 100644 --- a/projects/app/src/web/support/user/useUserStore.ts +++ b/projects/app/src/web/support/user/useUserStore.ts @@ -1,16 +1,18 @@ +import type { UserUpdateParams } from '@/types/user'; +import { useSystemStore } from '@/web/common/system/useSystemStore'; +import { getTokenLogin, putUserInfo } from '@/web/support/user/api'; +import { getTeamMembers } from '@/web/support/user/team/api'; +import type { MemberGroupListType } from '@fastgpt/global/support/permission/memberGroup/type'; +import type { OrgMemberSchemaType, OrgType } from '@fastgpt/global/support/user/team/org/type'; +import type { TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; +import type { UserType } from '@fastgpt/global/support/user/type.d'; +import type { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type'; import { create } from 'zustand'; import { devtools, persist } from 'zustand/middleware'; import { immer } from 'zustand/middleware/immer'; -import type { UserUpdateParams } from '@/types/user'; -import type { UserType } from '@fastgpt/global/support/user/type.d'; -import { getTokenLogin, putUserInfo } from '@/web/support/user/api'; -import { FeTeamPlanStatusType } from '@fastgpt/global/support/wallet/sub/type'; import { getTeamPlanStatus } from './team/api'; -import { getTeamMembers } from '@/web/support/user/team/api'; -import { TeamMemberItemType } from '@fastgpt/global/support/user/team/type'; -import { useSystemStore } from '@/web/common/system/useSystemStore'; -import { MemberGroupListType } from '@fastgpt/global/support/permission/memberGroup/type'; import { getGroupList } from './team/group/api'; +import { getOrgList } from './team/org/api'; type State = { systemMsgReadId: string; @@ -30,6 +32,10 @@ type State = { teamMemberGroups: MemberGroupListType; myGroups: MemberGroupListType; loadAndGetGroups: (init?: boolean) => Promise; + + teamOrgs: OrgType[]; + myOrgs: OrgType[]; + loadAndGetOrgs: (init?: boolean) => Promise; }; export const useUserStore = create()( @@ -107,6 +113,7 @@ export const useUserStore = create()( return res; }, teamMemberGroups: [], + teamOrgs: [], myGroups: [], loadAndGetGroups: async (init = false) => { if (!useSystemStore.getState()?.feConfigs?.isPlus) return []; @@ -123,6 +130,23 @@ export const useUserStore = create()( ); }); + return res; + }, + myOrgs: [], + loadAndGetOrgs: async (init = false) => { + if (!useSystemStore.getState()?.feConfigs?.isPlus) return []; + + const randomRefresh = Math.random() > 0.7; + if (!randomRefresh && !init && get().myOrgs.length) return Promise.resolve(get().myOrgs); + + const res = await getOrgList(); + set((state) => { + state.teamOrgs = res; + state.myOrgs = res.filter((item) => + item.members.map((i) => String(i.tmbId)).includes(String(state.userInfo?.team?.tmbId)) + ); + }); + return res; } })),