refactor: permission role & app read chat log permission (#5416)

* refactor: permission role

* refactor: permission type

* fix: permission manage

* fix: group owner cannot be deleted

* chore: common per map

* chore: openapi

* chore: rename

* fix: type error

* chore: app chat log permission

* chore: add initv4112
This commit is contained in:
Finley Ge
2025-08-11 10:51:44 +08:00
committed by GitHub
parent 29edf1ea5f
commit 57e1ef1176
52 changed files with 730 additions and 402 deletions

View File

@@ -11,8 +11,8 @@ 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 RoleSelect from './RoleSelect';
import RoleTags from './RoleTags';
import { CollaboratorContext } from './context';
export type ManageModalProps = {
onClose: () => void;
@@ -70,17 +70,17 @@ function ManageModal({ onClose }: ManageModalProps) {
</Flex>
</Td>
<Td border="none">
<PermissionTags permission={item.permission.value} />
<RoleTags permission={item.permission.role} />
</Td>
<Td border="none">
{/* Not self; Not owner and other manager */}
{item.tmbId !== userInfo?.team?.tmbId &&
(permission.isOwner || !item.permission.hasManagePer) && (
<PermissionSelect
<RoleSelect
Button={
<MyIcon name={'edit'} w={'16px'} _hover={{ color: 'primary.600' }} />
}
value={item.permission.value}
value={item.permission.role}
onChange={(permission) => {
onUpdate({
members: item.tmbId ? [item.tmbId] : undefined,

View File

@@ -2,8 +2,8 @@ import React from 'react';
import { useTranslation } from 'next-i18next';
import { Box, Checkbox, HStack, VStack } from '@chakra-ui/react';
import Avatar from '@fastgpt/web/components/common/Avatar';
import PermissionTags from './PermissionTags';
import { type PermissionValueType } from '@fastgpt/global/support/permission/type';
import RoleTags from './RoleTags';
import type { RoleValueType } from '@fastgpt/global/support/permission/type';
import MyIcon from '@fastgpt/web/components/common/Icon';
import OrgTags from '../../user/team/OrgTags';
import Tag from '@fastgpt/web/components/common/Tag';
@@ -15,7 +15,7 @@ function MemberItemCard({
isChecked,
onDelete,
name,
permission,
role: permission,
orgs,
addOnly,
rightSlot
@@ -26,7 +26,7 @@ function MemberItemCard({
isChecked?: boolean;
onDelete?: () => void;
name: string;
permission?: PermissionValueType;
role?: RoleValueType;
addOnly?: boolean;
orgs?: string[];
rightSlot?: React.ReactNode;
@@ -61,7 +61,7 @@ function MemberItemCard({
</Box>
<Box lineHeight={1}>{orgs && orgs.length > 0 && <OrgTags orgs={orgs} />}</Box>
</Box>
{!isAdded && permission && <PermissionTags permission={permission} />}
{!isAdded && permission && <RoleTags permission={permission} />}
{isAdded && (
<Tag
mixBlendMode={'multiply'}

View File

@@ -27,7 +27,7 @@ import { useMemo, useRef, useState } from 'react';
import { useContextSelector } from 'use-context-selector';
import { CollaboratorContext } from './context';
import MemberItemCard from './MemberItemCard';
import PermissionSelect from './PermissionSelect';
import RoleSelect from './RoleSelect';
const HoverBoxStyle = {
bgColor: 'myGray.50',
@@ -98,15 +98,13 @@ function MemberModal({
>([]);
const [selectedGroupList, setSelectedGroupList] = useState<MemberGroupListItemType<false>[]>([]);
const permissionList = useContextSelector(CollaboratorContext, (v) => v.permissionList);
const getPerLabelList = useContextSelector(CollaboratorContext, (v) => v.getPerLabelList);
const [selectedPermission, setSelectedPermission] = useState<number | undefined>(
permissionList?.read?.value
);
const perLabel = useMemo(() => {
if (selectedPermission === undefined) return '';
return getPerLabelList(selectedPermission!).join('、');
}, [getPerLabelList, selectedPermission]);
const roleList = useContextSelector(CollaboratorContext, (v) => v.roleList);
const getRoleLabelList = useContextSelector(CollaboratorContext, (v) => v.getRoleLabelList);
const [selectedRole, setSelectedRole] = useState<number | undefined>(roleList?.read?.value);
const roleLabel = useMemo(() => {
if (selectedRole === undefined) return '';
return getRoleLabelList(selectedRole!).join('、');
}, [getRoleLabelList, selectedRole]);
const onUpdateCollaborators = useContextSelector(
CollaboratorContext,
@@ -119,7 +117,7 @@ function MemberModal({
members: selectedMemberList.map((item) => item.tmbId),
groups: selectedGroupList.map((item) => item._id),
orgs: selectedOrgList.map((item) => item._id),
permission: addOnly ? undefined : selectedPermission!
permission: addOnly ? undefined : selectedRole!
} as UpdateClbPermissionProps<ValueOf<typeof addOnly>>),
{
successToast: t('common:add_success'),
@@ -277,7 +275,7 @@ function MemberModal({
avatar={member.avatar}
key={member.tmbId}
name={member.memberName}
permission={collaborator?.permission.value}
role={collaborator?.permission.role}
onChange={onChange}
isChecked={!!selectedMemberList.find((v) => v.tmbId === member.tmbId)}
orgs={member.orgs}
@@ -316,7 +314,7 @@ function MemberModal({
name={org.name}
onChange={onChange}
addOnly={addOnly}
permission={collaborator?.permission.value}
role={collaborator?.permission.role}
isChecked={!!selectedOrgList.find((v) => String(v._id) === String(org._id))}
rightSlot={
org.total && (
@@ -365,8 +363,8 @@ function MemberModal({
});
}}
isChecked={isChecked}
permission={collaborator?.permission.value}
addOnly={addOnly && !!member.permission.value}
role={collaborator?.permission.role}
addOnly={addOnly && !!member.permission.role}
orgs={member.orgs}
/>
);
@@ -392,7 +390,7 @@ function MemberModal({
name={
group.name === DefaultGroupName ? userInfo?.team.teamName ?? '' : group.name
}
permission={collaborator?.permission.value}
role={collaborator?.permission.role}
onChange={onChange}
isChecked={!!selectedGroupList.find((v) => v._id === group._id)}
addOnly={addOnly}
@@ -425,9 +423,9 @@ function MemberModal({
</Grid>
</ModalBody>
<ModalFooter>
{!addOnly && !!permissionList && (
<PermissionSelect
value={selectedPermission}
{!addOnly && !!roleList && (
<RoleSelect
value={selectedRole}
Button={
<Flex
alignItems={'center'}
@@ -438,11 +436,11 @@ function MemberModal({
borderRadius={'md'}
h={'32px'}
>
{t(perLabel as any)}
{roleLabel}
<ChevronDownIcon fontSize={'md'} />
</Flex>
}
onChange={(v) => setSelectedPermission(v)}
onChange={(v) => setSelectedRole(v)}
/>
)}
{addOnly && (

View File

@@ -7,11 +7,12 @@ import {
Radio,
useOutsideClick,
HStack,
MenuButton
MenuButton,
Checkbox
} from '@chakra-ui/react';
import React, { useMemo, useRef, useState } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { type PermissionValueType } from '@fastgpt/global/support/permission/type';
import type { RoleValueType } from '@fastgpt/global/support/permission/type';
import { useContextSelector } from 'use-context-selector';
import { Permission } from '@fastgpt/global/support/permission/controller';
import { CollaboratorContext } from './context';
@@ -19,8 +20,8 @@ import { useTranslation } from 'next-i18next';
import MyDivider from '@fastgpt/web/components/common/MyDivider';
export type PermissionSelectProps = {
value?: PermissionValueType;
onChange: (value: PermissionValueType) => void;
value?: RoleValueType;
onChange: (value: RoleValueType) => void;
trigger?: 'hover' | 'click';
offset?: [number, number];
Button: React.ReactNode;
@@ -39,8 +40,8 @@ const MenuStyle = {
fontSize: 'sm'
};
function PermissionSelect({
value,
function RoleSelect({
value: role,
onChange,
trigger = 'click',
offset = [0, 5],
@@ -49,15 +50,18 @@ function PermissionSelect({
onDelete
}: PermissionSelectProps) {
const { t } = useTranslation();
const ref = useRef<HTMLButtonElement>(null);
const ref = useRef<HTMLDivElement>(null);
const closeTimer = useRef<NodeJS.Timeout>();
const { permission, permissionList } = useContextSelector(CollaboratorContext, (v) => v);
const { permission, roleList: permissionList } = useContextSelector(
CollaboratorContext,
(v) => v
);
const [isOpen, setIsOpen] = useState(false);
const permissionSelectList = useMemo(() => {
if (!permissionList) return { singleCheckBoxList: [], multipleCheckBoxList: [] };
const roleOptions = useMemo(() => {
if (!permissionList) return { singleOptions: [], checkboxList: [] };
const list = Object.entries(permissionList).map(([_, value]) => {
return {
@@ -69,44 +73,42 @@ function PermissionSelect({
});
return {
singleCheckBoxList: list
.filter((item) => item.checkBoxType === 'single')
.filter((item) => {
if (permission.isOwner) return true;
if (item.value === permissionList['manage'].value) return false;
return true;
}),
multipleCheckBoxList: list.filter((item) => item.checkBoxType === 'multiple')
singleOptions: list.filter(
(item) =>
item.checkBoxType === 'single' &&
(permission.isOwner || item.value !== permissionList['manage'].value)
),
checkboxList: list.filter((item) => item.checkBoxType === 'multiple')
};
}, [permission.isOwner, permissionList]);
const selectedSingleValue = useMemo(() => {
if (!permissionList) return undefined;
const per = new Permission({ per: value });
const per = new Permission({ role });
if (per.hasManagePer) return permissionList['manage'].value;
if (per.hasWritePer) return permissionList['write'].value;
return permissionList['read'].value;
}, [permissionList, value]);
// const selectedMultipleValues = useMemo(() => {
// const per = new Permission({ per: value });
//
// return permissionSelectList.multipleCheckBoxList
// .filter((item) => {
// return per.checkPer(item.value);
// })
// .map((item) => item.value);
// }, [permissionSelectList.multipleCheckBoxList, value]);
}, [permissionList, role]);
const selectedMultipleValues = useMemo(() => {
const per = new Permission({ role });
const onSelectPer = (per: PermissionValueType) => {
if (per === value) return;
onChange(per);
setIsOpen(false);
return roleOptions.checkboxList
.filter((item) => {
return per.checkRole(item.value);
})
.map((item) => item.value);
}, [role, roleOptions.checkboxList]);
const onSelectRole = (newRole: RoleValueType) => {
if (newRole === role) return;
onChange(newRole);
// setIsOpen(false);
};
useOutsideClick({
ref: ref,
ref,
handler: () => {
setIsOpen(false);
}
@@ -115,6 +117,7 @@ function PermissionSelect({
return selectedSingleValue !== undefined ? (
<Menu offset={offset} isOpen={isOpen} autoSelect={false} direction={'ltr'}>
<Box
ref={ref}
w="fit-content"
onMouseEnter={() => {
if (trigger === 'hover') {
@@ -131,7 +134,6 @@ function PermissionSelect({
}}
>
<MenuButton
ref={ref}
position={'relative'}
onClickCapture={() => {
if (trigger === 'click') {
@@ -153,12 +155,12 @@ function PermissionSelect({
whiteSpace={'pre-wrap'}
>
{/* The list of single select permissions */}
{permissionSelectList.singleCheckBoxList.map((item) => {
{roleOptions.singleOptions.map((item) => {
const change = () => {
const per = new Permission({ per: value });
per.removePer(selectedSingleValue);
per.addPer(item.value);
onSelectPer(per.value);
const per = new Permission({ role });
per.removeRole(selectedSingleValue);
per.addRole(item.value);
onSelectRole(per.role);
};
return (
@@ -184,50 +186,45 @@ function PermissionSelect({
);
})}
{/* <MyDivider my={3} />
<MyDivider />
{multipleValues.length > 0 && <Box m="4"></Box>} */}
{roleOptions.checkboxList.length > 0 && (
<Box pb="2" px="3" fontSize={'sm'} color={'myGray.900'}>
{t('common:permission_other')}
</Box>
)}
{/* The list of multiple select permissions */}
{/* {list
.filter((item) => item.type === 'multiple')
.map((item) => {
const change = () => {
if (checkPermission(valueState, item.value)) {
setValueState(new Permission(valueState).remove(item.value).value);
} else {
setValueState(new Permission(valueState).add(item.value).value);
}
};
return (
<Flex
key={item.value}
{...(checkPermission(valueState, item.value)
? {
color: 'primary.500',
bg: 'myWhite.300'
}
: {})}
whiteSpace="pre-wrap"
flexDirection="row"
justifyContent="start"
p="2"
_hover={{
bg: 'myGray.50'
}}
>
<Checkbox
size="lg"
isChecked={checkPermission(valueState, item.value)}
onChange={change}
/>
<Flex px="4" flexDirection="column" onClick={change}>
<Box fontWeight="500">{item.name}</Box>
<Box fontWeight="400">{item.description}</Box>
</Flex>
{roleOptions.checkboxList.map((item) => {
const change = () => {
const per = new Permission({ role });
if (per.checkRole(item.value)) {
per.removeRole(item.value);
} else {
per.addRole(item.value);
}
onSelectRole(per.role);
};
const isChecked = selectedMultipleValues.includes(item.value);
return (
<Flex
key={item.value}
{...(isChecked
? {
color: 'primary.600'
}
: {})}
{...MenuStyle}
>
<Checkbox size="lg" isChecked={isChecked} onChange={change} />
<Flex px="4" flexDirection="column" onClick={change}>
<Box>{t(item.name as any)}</Box>
<Box color={'myGray.500'} fontSize={'mini'}>
{t(item.description as any)}
</Box>
</Flex>
);
})}*/}
</Flex>
);
})}
{onDelete && (
<>
<MyDivider my={2} h={'2px'} borderColor={'myGray.200'} />
@@ -235,7 +232,7 @@ function PermissionSelect({
{...MenuStyle}
onClick={() => {
onDelete();
setIsOpen(false);
// setIsOpen(false);
}}
>
<MyIcon name="delete" w="20px" color="red.600" />
@@ -249,4 +246,4 @@ function PermissionSelect({
) : null;
}
export default React.memo(PermissionSelect);
export default React.memo(RoleSelect);

View File

@@ -10,17 +10,17 @@ export type PermissionTagsProp = {
permission?: PermissionValueType;
};
function PermissionTags({ permission }: PermissionTagsProp) {
const { getPerLabelList } = useContextSelector(CollaboratorContext, (v) => v);
function RoleTags({ permission }: PermissionTagsProp) {
const { getRoleLabelList } = useContextSelector(CollaboratorContext, (v) => v);
const { t } = useTranslation();
if (permission === undefined) return null;
const perTagList = getPerLabelList(permission);
const roleTagList = getRoleLabelList(permission);
return (
<Flex gap="2" alignItems="center">
{perTagList.map((item) => (
{roleTagList.map((item) => (
<Tag
mixBlendMode={'multiply'}
key={item}
@@ -37,4 +37,4 @@ function PermissionTags({ permission }: PermissionTagsProp) {
);
}
export default PermissionTags;
export default RoleTags;

View File

@@ -3,11 +3,11 @@ 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 type {
PermissionListType,
PermissionValueType
PermissionValueType,
RoleListType,
RoleValueType
} from '@fastgpt/global/support/permission/type';
import { type ReactNode, useCallback } from 'react';
import { createContext } from 'use-context-selector';
@@ -19,6 +19,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
import { useTranslation } from 'next-i18next';
import { CommonRoleList } from '@fastgpt/global/support/permission/constant';
const MemberModal = dynamic(() => import('./MemberModal'));
const ManageModal = dynamic(() => import('./ManageModal'));
@@ -26,7 +27,7 @@ const ManageModal = dynamic(() => import('./ManageModal'));
export type MemberManagerInputPropsType = {
permission: Permission;
onGetCollaboratorList: () => Promise<CollaboratorItemType[]>;
permissionList?: PermissionListType;
roleList?: RoleListType;
onUpdateCollaborators: (props: UpdateClbPermissionProps) => Promise<any>;
onDelOneCollaborator: (
props: RequireOnlyOne<{ tmbId: string; groupId: string; orgId: string }>
@@ -38,7 +39,7 @@ export type MemberManagerPropsType = MemberManagerInputPropsType & {
collaboratorList: CollaboratorItemType[];
refetchCollaboratorList: () => void;
isFetchingCollaborator: boolean;
getPerLabelList: (per: PermissionValueType) => string[];
getRoleLabelList: (role: RoleValueType) => string[];
};
export type ChildrenProps = {
onOpenAddMember: () => void;
@@ -50,14 +51,14 @@ type CollaboratorContextType = MemberManagerPropsType & {};
export const CollaboratorContext = createContext<CollaboratorContextType>({
collaboratorList: [],
permissionList: PermissionList,
roleList: CommonRoleList,
onUpdateCollaborators: () => {
throw new Error('Function not implemented.');
},
onDelOneCollaborator: () => {
throw new Error('Function not implemented.');
},
getPerLabelList: (): string[] => {
getRoleLabelList: (): string[] => {
throw new Error('Function not implemented.');
},
refetchCollaboratorList: (): void => {
@@ -73,7 +74,7 @@ export const CollaboratorContext = createContext<CollaboratorContextType>({
const CollaboratorContextProvider = ({
permission,
onGetCollaboratorList,
permissionList,
roleList,
onUpdateCollaborators,
onDelOneCollaborator,
children,
@@ -115,7 +116,7 @@ const CollaboratorContextProvider = ({
return {
...item,
permission: new Permission({
per: item.permission.value
role: item.permission.role
})
};
});
@@ -128,32 +129,32 @@ const CollaboratorContextProvider = ({
}
);
const getPerLabelList = useCallback(
(per: PermissionValueType) => {
if (!permissionList) return [];
const getRoleLabelList = useCallback(
(role: PermissionValueType) => {
if (!roleList) return [];
const Per = new Permission({ per });
const Per = new Permission({ role });
const labels: string[] = [];
if (Per.hasManagePer) {
labels.push(permissionList['manage'].name);
labels.push(t(roleList['manage'].name as any));
} else if (Per.hasWritePer) {
labels.push(permissionList['write'].name);
labels.push(t(roleList['write'].name as any));
} else if (Per.hasReadPer) {
labels.push(permissionList['read'].name);
labels.push(t(roleList['read'].name as any));
}
Object.values(permissionList).forEach((item) => {
Object.values(roleList).forEach((item) => {
if (item.checkBoxType === 'multiple') {
if (Per.checkPer(item.value)) {
labels.push(item.name);
if (Per.checkRole(item.value)) {
labels.push(t(item.name as any));
}
}
});
return labels;
},
[permissionList]
[roleList]
);
const { ConfirmModal, openConfirm } = useConfirm({});
@@ -174,10 +175,10 @@ const CollaboratorContextProvider = ({
collaboratorList,
refetchCollaboratorList,
isFetchingCollaborator,
permissionList,
roleList,
onUpdateCollaborators: onUpdateCollaboratorsThen,
onDelOneCollaborator: onDelOneCollaboratorThen,
getPerLabelList
getRoleLabelList
};
const onOpenAddMemberModal = () => {

View File

@@ -1,34 +1,34 @@
import React, { useMemo } from 'react';
import { Permission } from '@fastgpt/global/support/permission/controller';
import { type PermissionListType } from '@fastgpt/global/support/permission/type';
import { PermissionList } from '@fastgpt/global/support/permission/constant';
import type { RoleListType } from '@fastgpt/global/support/permission/type';
import MyTag from '@fastgpt/web/components/common/Tag/index';
import { HStack } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { CommonRoleList } from '@fastgpt/global/support/permission/constant';
const PermissionTag = ({
permission,
permissionList
roleList: roleList
}: {
permission: Permission;
permissionList: PermissionListType;
roleList: RoleListType;
}) => {
const { t } = useTranslation();
const { commonLabel, otherLabels } = useMemo(() => {
const Per = new Permission({ per: permission.value });
const Per = new Permission({ role: permission.role });
const commonLabel = (() => {
if (permission.isOwner) return t('common:permission.Owner');
if (permission.hasManagePer) return t(PermissionList['manage'].name as any);
if (permission.hasWritePer) return t(PermissionList['write'].name as any);
if (permission.hasReadPer) return t(PermissionList['read'].name as any);
if (permission.hasManagePer) return t(CommonRoleList['manage'].name as any);
if (permission.hasWritePer) return t(CommonRoleList['write'].name as any);
if (permission.hasReadPer) return t(CommonRoleList['read'].name as any);
return;
})();
const otherLabels: string[] = [];
Object.values(permissionList).forEach((item) => {
Object.values(roleList).forEach((item) => {
if (item.checkBoxType === 'multiple') {
if (Per.checkPer(item.value)) {
otherLabels.push(item.name);
@@ -45,8 +45,8 @@ const PermissionTag = ({
permission.hasReadPer,
permission.hasWritePer,
permission.isOwner,
permission.value,
permissionList,
permission.role,
roleList,
t
]);
return (

View File

@@ -1,8 +1,8 @@
import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
export const processUpdateAppCollaboratorSpecific = (metadata: any) => {
const permissionValue = parseInt(metadata.permission, 10);
const permission = new AppPermission({ per: permissionValue });
const role = parseInt(metadata.permission, 10);
const permission = new AppPermission({ role });
return {
...metadata,
readPermission: permission.hasReadPer ? '✔' : '✘',

View File

@@ -1,8 +1,8 @@
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
export const processUpdateDatasetCollaboratorSpecific = (metadata: any) => {
const permissionValue = parseInt(metadata.permission, 10);
const permission = new DatasetPermission({ per: permissionValue });
const role = parseInt(metadata.permission, 10);
const permission = new DatasetPermission({ role });
return {
...metadata,
readPermission: permission.hasReadPer ? '✔' : '✘',

View File

@@ -1,8 +1,10 @@
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
// TODO: replace any
export const processAssignPermissionSpecific = (metadata: any) => {
const permissionValue = parseInt(metadata.permission, 10);
const permission = new TeamPermission({ per: permissionValue });
// metadata.permission is a string, parseInt will convert it to number in decimal
const role = parseInt(metadata.permission, 10);
const permission = new TeamPermission({ role });
return {
...metadata,

View File

@@ -124,7 +124,7 @@ function GroupEditModal({
const handleToggleSelect = (memberId: string) => {
if (
myRole === 'owner' &&
memberId === groupMembers.find((item) => item.role === 'owner')?.tmbId
memberId === groupMembers.find((item) => item.groupRole === 'owner')?.tmbId
) {
toast({
title: t('user:team.group.toast.can_not_delete_owner'),

View File

@@ -28,11 +28,14 @@ import MemberTag from '../../../../components/support/user/team/Info/MemberTag';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
import {
TeamApikeyCreatePermissionVal,
TeamApikeyCreateRoleVal,
TeamAppCreatePermissionVal,
TeamAppCreateRoleVal,
TeamDatasetCreatePermissionVal,
TeamDatasetCreateRoleVal,
TeamManagePermissionVal,
TeamPermissionList,
TeamWritePermissionVal
TeamManageRoleVal,
TeamRoleList
} from '@fastgpt/global/support/permission/user/constant';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { useToggle } from 'ahooks';
@@ -117,18 +120,18 @@ function PermissionManage({
if (!clb) return;
const permission = new TeamPermission({ per: clb.permission.value });
const permission = new TeamPermission({ role: clb.permission.role });
if (type === 'add') {
permission.addPer(per);
permission.addRole(per);
} else {
permission.removePer(per);
permission.removeRole(per);
}
return onUpdateCollaborators({
...(clb.tmbId && { members: [clb.tmbId] }),
...(clb.groupId && { groups: [clb.groupId] }),
...(clb.orgId && { orgs: [clb.orgId] }),
permission: permission.value
permission: permission.role
});
}
);
@@ -145,12 +148,12 @@ function PermissionManage({
function PermissionCheckBox({
isDisabled,
per,
role,
clbPer,
id
}: {
isDisabled: boolean;
per: PermissionValueType;
role: PermissionValueType;
clbPer: Permission;
id: string;
}) {
@@ -159,18 +162,18 @@ function PermissionManage({
<Box mx="auto" w="fit-content">
<Checkbox
isDisabled={isDisabled}
isChecked={clbPer.checkPer(per)}
isChecked={clbPer.checkRole(role)}
onChange={(e) =>
e.target.checked
? onUpdatePermission({
id,
type: 'add',
per
per: role
})
: onUpdatePermission({
id,
type: 'remove',
per
per: role
})
}
/>
@@ -266,25 +269,25 @@ function PermissionManage({
</Td>
<PermissionCheckBox
isDisabled={member.permission.isOwner || !userManage}
per={TeamAppCreatePermissionVal}
role={TeamAppCreateRoleVal}
clbPer={member.permission}
id={member.tmbId!}
/>
<PermissionCheckBox
isDisabled={member.permission.isOwner || !userManage}
per={TeamDatasetCreatePermissionVal}
role={TeamDatasetCreateRoleVal}
clbPer={member.permission}
id={member.tmbId!}
/>
<PermissionCheckBox
isDisabled={member.permission.isOwner || !userManage}
per={TeamApikeyCreatePermissionVal}
role={TeamApikeyCreateRoleVal}
clbPer={member.permission}
id={member.tmbId!}
/>
<PermissionCheckBox
isDisabled={member.permission.isOwner || !userInfo?.permission.isOwner}
per={TeamManagePermissionVal}
role={TeamManageRoleVal}
clbPer={member.permission}
id={member.tmbId!}
/>
@@ -323,25 +326,25 @@ function PermissionManage({
</Td>
<PermissionCheckBox
isDisabled={org.permission.isOwner || !userManage}
per={TeamAppCreatePermissionVal}
role={TeamAppCreatePermissionVal}
clbPer={org.permission}
id={org.orgId!}
/>
<PermissionCheckBox
isDisabled={org.permission.isOwner || !userManage}
per={TeamDatasetCreatePermissionVal}
role={TeamDatasetCreatePermissionVal}
clbPer={org.permission}
id={org.orgId!}
/>
<PermissionCheckBox
isDisabled={org.permission.isOwner || !userManage}
per={TeamApikeyCreatePermissionVal}
role={TeamApikeyCreatePermissionVal}
clbPer={org.permission}
id={org.orgId!}
/>
<PermissionCheckBox
isDisabled={org.permission.isOwner || !userInfo?.permission.isOwner}
per={TeamManagePermissionVal}
role={TeamManagePermissionVal}
clbPer={org.permission}
id={org.orgId!}
/>
@@ -385,25 +388,25 @@ function PermissionManage({
</Td>
<PermissionCheckBox
isDisabled={group.permission.isOwner || !userManage}
per={TeamAppCreatePermissionVal}
role={TeamAppCreatePermissionVal}
clbPer={group.permission}
id={group.groupId!}
/>
<PermissionCheckBox
isDisabled={group.permission.isOwner || !userManage}
per={TeamDatasetCreatePermissionVal}
role={TeamDatasetCreatePermissionVal}
clbPer={group.permission}
id={group.groupId!}
/>
<PermissionCheckBox
isDisabled={group.permission.isOwner || !userManage}
per={TeamApikeyCreatePermissionVal}
role={TeamApikeyCreatePermissionVal}
clbPer={group.permission}
id={group.groupId!}
/>
<PermissionCheckBox
isDisabled={group.permission.isOwner || !userInfo?.permission.isOwner}
per={TeamManagePermissionVal}
role={TeamManagePermissionVal}
clbPer={group.permission}
id={group.groupId!}
/>
@@ -434,7 +437,7 @@ export const Render = ({ Tabs }: { Tabs: React.ReactNode }) => {
return userInfo?.team ? (
<CollaboratorContextProvider
permission={userInfo?.team.permission}
permissionList={TeamPermissionList}
roleList={TeamRoleList}
onGetCollaboratorList={getTeamClbs}
onUpdateCollaborators={updateMemberPermission}
onDelOneCollaborator={deleteMemberPermission}

View File

@@ -20,7 +20,7 @@ import {
} from '@chakra-ui/react';
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 { AppRoleList } 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';
@@ -187,7 +187,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
<CollaboratorContextProvider
permission={appDetail.permission}
onGetCollaboratorList={() => getCollaboratorList(appDetail._id)}
permissionList={AppPermissionList}
roleList={AppRoleList}
onUpdateCollaborators={async (props) =>
onUpdateCollaborators({
permission: props.permission,

View File

@@ -36,7 +36,9 @@ const RouteTab = () => {
label: t('app:publish_channel'),
id: TabEnum.publish
},
{ label: t('app:chat_logs'), id: TabEnum.logs }
...(appDetail.permission.hasReadChatLogPer
? [{ label: t('app:chat_logs'), id: TabEnum.logs }]
: [])
]
: [])
],

View File

@@ -17,7 +17,7 @@ import { useFolderDrag } from '@/components/common/folder/useFolderDrag';
import dynamic from 'next/dynamic';
import type { EditResourceInfoFormType } from '@/components/common/Modal/EditResourceModal';
import MyMenu, { type MenuItemType } from '@fastgpt/web/components/common/MyMenu';
import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant';
import { AppRoleList } from '@fastgpt/global/support/permission/app/constant';
import {
deleteAppCollaborators,
getCollaboratorList,
@@ -427,7 +427,7 @@ const ListItem = () => {
managePer={{
permission: editPerApp.permission,
onGetCollaboratorList: () => getCollaboratorList(editPerApp._id),
permissionList: AppPermissionList,
roleList: AppRoleList,
onUpdateCollaborators: (props: {
members?: string[];
groups?: string[];

View File

@@ -17,7 +17,7 @@ import { DatasetTypeEnum, DatasetTypeMap } from '@fastgpt/global/core/dataset/co
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import MyIcon from '@fastgpt/web/components/common/Icon';
import { DatasetPermissionList } from '@fastgpt/global/support/permission/dataset/constant';
import { DatasetRoleList } from '@fastgpt/global/support/permission/dataset/constant';
import MemberManager from '../../MemberManager';
import {
getCollaboratorList,
@@ -382,7 +382,7 @@ const Info = ({ datasetId }: { datasetId: string }) => {
managePer={{
permission: datasetDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(datasetId),
permissionList: DatasetPermissionList,
roleList: DatasetRoleList,
onUpdateCollaborators: (body) =>
postUpdateDatasetCollaborators({
...body,

View File

@@ -17,7 +17,7 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import dynamic from 'next/dynamic';
import { useContextSelector } from 'use-context-selector';
import { DatasetsContext } from '../../../pages/dataset/list/context';
import { DatasetPermissionList } from '@fastgpt/global/support/permission/dataset/constant';
import { DatasetRoleList } from '@fastgpt/global/support/permission/dataset/constant';
import ConfigPerModal from '@/components/support/permission/ConfigPerModal';
import {
deleteDatasetCollaborators,
@@ -436,7 +436,7 @@ function List() {
managePer={{
permission: editPerDataset.permission,
onGetCollaboratorList: () => getCollaboratorList(editPerDataset._id),
permissionList: DatasetPermissionList,
roleList: DatasetRoleList,
onUpdateCollaborators: (props) =>
postUpdateDatasetCollaborators({
...props,

View File

@@ -0,0 +1,39 @@
import { NextAPI } from '@/service/middleware/entry';
import { AppReadChatLogRoleVal } from '@fastgpt/global/support/permission/app/constant';
import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
import type { AnyBulkWriteOperation } from '@fastgpt/service/common/mongo';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { type NextApiRequest, type NextApiResponse } from 'next';
async function handler(req: NextApiRequest, _res: NextApiResponse) {
await authCert({ req, authRoot: true });
// 初始化 app 权限:所有有 write 的都加上 readChatLog role
const rps = await MongoResourcePermission.find({
resourceType: 'app'
}).lean();
const ops: AnyBulkWriteOperation<typeof MongoResourcePermission>[] = [];
for (const rp of rps) {
const per = new AppPermission({ role: rp.permission });
if (per.hasWritePer) {
per.addRole(AppReadChatLogRoleVal);
ops.push({
updateOne: {
filter: { _id: rp._id },
update: { $set: { permission: per.role } }
}
});
}
}
const result = await MongoResourcePermission.bulkWrite(ops);
return {
success: true,
result
};
}
export default NextAPI(handler);

View File

@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
import { DataSetDefaultRoleVal } from '@fastgpt/global/support/permission/dataset/constant';
/* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
@@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
},
{
$set: {
defaultPermission: DatasetDefaultPermissionVal
defaultPermission: DataSetDefaultRoleVal
}
}
);

View File

@@ -22,15 +22,15 @@ async function handler(req: NextApiRequest, _res: NextApiResponse) {
});
for await (const rp of rps) {
const per = new TeamPermission({ per: rp.permission });
console.log(per.hasWritePer, per.value);
if (per.hasWritePer) {
const newPer = per.addPer(
TeamAppCreatePermissionVal,
TeamDatasetCreatePermissionVal,
TeamApikeyCreatePermissionVal
);
rp.permission = newPer.value;
const per = rp.permission;
console.log(per);
if (per & 0b010) {
// has 0b010
rp.permission =
per |
TeamAppCreatePermissionVal |
TeamDatasetCreatePermissionVal |
TeamApikeyCreatePermissionVal;
try {
await retryFn(async () => {

View File

@@ -1,7 +1,6 @@
import type { NextApiResponse } from 'next';
import { responseWriteController } from '@fastgpt/service/common/response';
import { addDays } from 'date-fns';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { readFromSecondary } from '@fastgpt/service/common/mongo/utils';
import { addLog } from '@fastgpt/service/common/system/log';
import dayjs from 'dayjs';
@@ -19,6 +18,7 @@ import type { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
import { type AIChatItemValueItemType } from '@fastgpt/global/core/chat/type';
import { sanitizeCsvField } from '@fastgpt/service/common/file/csv';
import { AppReadChatLogPerVal } from '@fastgpt/global/support/permission/app/constant';
const formatJsonString = (data: any) => {
if (data == null) return '';
@@ -47,7 +47,7 @@ async function handler(req: ApiRequestProps<ExportChatLogsBody, {}>, res: NextAp
throw new Error('缺少参数');
}
const { teamId } = await authApp({ req, authToken: true, appId, per: WritePermissionVal });
const { teamId } = await authApp({ req, authToken: true, appId, per: AppReadChatLogPerVal });
const teamMemberWithContact = await MongoTeamMember.aggregate([
{ $match: { teamId: new Types.ObjectId(teamId) } },

View File

@@ -7,7 +7,6 @@ import type { GetAppChatLogsParams } from '@/global/core/api/appReq.d';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { ChatItemCollectionName } from '@fastgpt/service/core/chat/chatItemSchema';
import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { readFromSecondary } from '@fastgpt/service/common/mongo/utils';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
import { type PaginationResponse } from '@fastgpt/web/common/fetch/type';
@@ -16,6 +15,7 @@ import { addAuditLog } from '@fastgpt/service/support/user/audit/util';
import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants';
import { getI18nAppType } from '@fastgpt/service/support/user/audit/util';
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { AppReadChatLogPerVal } from '@fastgpt/global/support/permission/app/constant';
async function handler(
req: NextApiRequest,
@@ -41,7 +41,7 @@ async function handler(
req,
authToken: true,
appId,
per: WritePermissionVal
per: AppReadChatLogPerVal
});
const where = {

View File

@@ -11,15 +11,15 @@ import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constants';
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
import { AppDefaultRoleVal } from '@fastgpt/global/support/permission/app/constant';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { concatPer } from '@fastgpt/service/support/permission/controller';
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
import { addSourceMember } from '@fastgpt/service/support/user/utils';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { sumPer } from '@fastgpt/global/support/permission/utils';
export type ListAppBody = {
parentId?: ParentIdType;
@@ -63,7 +63,7 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
]);
// Get team all app permissions
const [perList, myGroupMap, myOrgSet] = await Promise.all([
const [roleList, myGroupMap, myOrgSet] = await Promise.all([
MongoResourcePermission.find({
resourceType: PerResourceTypeEnum.app,
teamId,
@@ -87,7 +87,7 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
})
]);
// Get my permissions
const myPerList = perList.filter(
const myPerList = roleList.filter(
(item) =>
String(item.tmbId) === String(tmbId) ||
myGroupMap.has(String(item.groupId)) ||
@@ -160,11 +160,11 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
.map((app) => {
const { Per, privateApp } = (() => {
const getPer = (appId: string) => {
const tmbPer = myPerList.find(
const tmbRole = myPerList.find(
(item) => String(item.resourceId) === appId && !!item.tmbId
)?.permission;
const groupPer = concatPer(
myPerList
const groupRole = sumPer(
...myPerList
.filter(
(item) => String(item.resourceId) === appId && (!!item.groupId || !!item.orgId)
)
@@ -172,13 +172,13 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
);
return new AppPermission({
per: tmbPer ?? groupPer ?? AppDefaultPermissionVal,
role: tmbRole ?? groupRole,
isOwner: String(app.tmbId) === String(tmbId) || teamPer.isOwner
});
};
const getClbCount = (appId: string) => {
return perList.filter((item) => String(item.resourceId) === String(appId)).length;
return roleList.filter((item) => String(item.resourceId) === String(appId)).length;
};
// Inherit app, check parent folder clb

View File

@@ -8,17 +8,17 @@ import {
ReadPermissionVal
} from '@fastgpt/global/support/permission/constant';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema';
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
import { DataSetDefaultRoleVal } from '@fastgpt/global/support/permission/dataset/constant';
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
import { concatPer } from '@fastgpt/service/support/permission/controller';
import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
import { addSourceMember } from '@fastgpt/service/support/user/utils';
import { getEmbeddingModel } from '@fastgpt/service/core/ai/model';
import { sumPer } from '@fastgpt/global/support/permission/utils';
export type GetDatasetListBody = {
parentId: ParentIdType;
@@ -51,7 +51,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
]);
// Get team all app permissions
const [perList, myGroupMap, myOrgSet] = await Promise.all([
const [roleList, myGroupMap, myOrgSet] = await Promise.all([
MongoResourcePermission.find({
resourceType: PerResourceTypeEnum.dataset,
teamId,
@@ -74,7 +74,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
tmbId
})
]);
const myPerList = perList.filter(
const myRoles = roleList.filter(
(item) =>
String(item.tmbId) === String(tmbId) ||
myGroupMap.has(String(item.groupId)) ||
@@ -83,7 +83,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
const findDatasetQuery = (() => {
// Filter apps by permission, if not owner, only get apps that I have permission to access
const idList = { _id: { $in: myPerList.map((item) => item.resourceId) } };
const idList = { _id: { $in: myRoles.map((item) => item.resourceId) } };
const datasetPerQuery = teamPer.isOwner
? {}
: parentId
@@ -127,23 +127,23 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
.map((dataset) => {
const { Per, privateDataset } = (() => {
const getPer = (datasetId: string) => {
const tmbPer = myPerList.find(
const tmbRole = myRoles.find(
(item) => String(item.resourceId) === datasetId && !!item.tmbId
)?.permission;
const groupPer = concatPer(
myPerList
const groupRole = sumPer(
...myRoles
.filter(
(item) => String(item.resourceId) === datasetId && (!!item.groupId || !!item.orgId)
)
.map((item) => item.permission)
);
return new DatasetPermission({
per: tmbPer ?? groupPer ?? DatasetDefaultPermissionVal,
role: tmbRole ?? groupRole,
isOwner: String(dataset.tmbId) === String(tmbId) || teamPer.isOwner
});
};
const getClbCount = (datasetId: string) => {
return perList.filter((item) => String(item.resourceId) === String(datasetId)).length;
return roleList.filter((item) => String(item.resourceId) === String(datasetId)).length;
};
// inherit

View File

@@ -17,7 +17,7 @@ import FolderPath from '@/components/common/folder/Path';
import { useRouter } from 'next/router';
import FolderSlideCard from '@/components/common/folder/SlideCard';
import { delAppById, resumeInheritPer } from '@/web/core/app/api';
import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant';
import { AppRoleList } from '@fastgpt/global/support/permission/app/constant';
import {
deleteAppCollaborators,
getCollaboratorList,
@@ -282,7 +282,7 @@ const MyApps = ({ MenuIcon }: { MenuIcon: JSX.Element }) => {
managePer={{
permission: folderDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(folderDetail._id),
permissionList: AppPermissionList,
roleList: AppRoleList,
onUpdateCollaborators: (props) =>
postUpdateAppCollaborators({
...props,

View File

@@ -18,7 +18,7 @@ import { type EditFolderFormType } from '@fastgpt/web/components/common/MyModal/
import dynamic from 'next/dynamic';
import { postCreateDatasetFolder, resumeInheritPer } from '@/web/core/dataset/api';
import FolderSlideCard from '@/components/common/folder/SlideCard';
import { DatasetPermissionList } from '@fastgpt/global/support/permission/dataset/constant';
import { DatasetRoleList } from '@fastgpt/global/support/permission/dataset/constant';
import {
postUpdateDatasetCollaborators,
deleteDatasetCollaborators,
@@ -256,7 +256,7 @@ const Dataset = () => {
managePer={{
permission: folderDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(folderDetail._id),
permissionList: DatasetPermissionList,
roleList: DatasetRoleList,
onUpdateCollaborators: (params) =>
postUpdateDatasetCollaborators({
...params,