Commit 57e1ef11 by Finley Ge Committed by GitHub

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
parent 29edf1ea
import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant'; import {
import { type PermissionListType } from '../type'; NullRoleVal,
CommonPerKeyEnum,
CommonRoleList,
CommonPerList,
CommonRolePerMap
} from '../constant';
import type { PermissionListType, PermissionValueType, RolePerMapType } from '../type';
import { type RoleListType } from '../type';
import { i18nT } from '../../../../web/i18n/utils'; import { i18nT } from '../../../../web/i18n/utils';
export enum AppPermissionKeyEnum {} import { sumPer } from '../utils';
export const AppPermissionList: PermissionListType = {
[PermissionKeyEnum.read]: { export enum AppPermissionKeyEnum {
...PermissionList[PermissionKeyEnum.read], ReadChatLog = 'readChatLog'
}
export const AppPerList: PermissionListType<AppPermissionKeyEnum> = {
...CommonPerList,
readChatLog: 0b1000
};
export const AppRoleList: RoleListType<AppPermissionKeyEnum> = {
[CommonPerKeyEnum.read]: {
...CommonRoleList[CommonPerKeyEnum.read],
description: i18nT('app:permission.des.read') description: i18nT('app:permission.des.read')
}, },
[PermissionKeyEnum.write]: { [CommonPerKeyEnum.write]: {
...PermissionList[PermissionKeyEnum.write], ...CommonRoleList[CommonPerKeyEnum.write],
description: i18nT('app:permission.des.write') description: i18nT('app:permission.des.write')
}, },
[PermissionKeyEnum.manage]: { [CommonPerKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage], ...CommonRoleList[CommonPerKeyEnum.manage],
description: i18nT('app:permission.des.manage') description: i18nT('app:permission.des.manage')
},
[AppPermissionKeyEnum.ReadChatLog]: {
value: 0b1000,
checkBoxType: 'multiple',
name: i18nT('app:permission.name.readChatLog'),
description: i18nT('app:permission.des.readChatLog')
} }
}; };
export const AppDefaultPermissionVal = NullPermission; export const AppRolePerMap: RolePerMapType = new Map([
...CommonRolePerMap,
[
AppRoleList[AppPermissionKeyEnum.ReadChatLog].value,
sumPer(
CommonPerList[CommonPerKeyEnum.read],
AppPerList[AppPermissionKeyEnum.ReadChatLog]
) as PermissionValueType
]
]);
export const AppDefaultRoleVal = NullRoleVal;
export const AppReadChatLogPerVal = AppPerList[AppPermissionKeyEnum.ReadChatLog];
export const AppReadChatLogRoleVal = AppRoleList[AppPermissionKeyEnum.ReadChatLog].value;
import { type PerConstructPros, Permission } from '../controller'; import { type PerConstructPros, Permission } from '../controller';
import { AppDefaultPermissionVal } from './constant'; import { AppDefaultRoleVal, AppPerList, AppRoleList, AppRolePerMap } from './constant';
export class AppPermission extends Permission { export class AppPermission extends Permission {
hasReadChatLogPer: boolean = false;
hasReadChatLogRole: boolean = false;
constructor(props?: PerConstructPros) { constructor(props?: PerConstructPros) {
if (!props) { if (!props) {
props = { props = {
per: AppDefaultPermissionVal role: AppDefaultRoleVal
}; };
} else if (!props?.per) { } else if (!props?.role) {
props.per = AppDefaultPermissionVal; props.role = AppDefaultRoleVal;
} }
props.roleList = AppRoleList;
props.rolePerMap = AppRolePerMap;
props.perList = AppPerList;
super(props); super(props);
this.setUpdatePermissionCallback(() => {
this.hasReadChatLogPer = this.checkPer(AppPerList.readChatLog);
this.hasReadChatLogRole = this.checkRole(AppRoleList.readChatLog.value);
});
} }
} }
import { type PermissionListType } from './type'; import type { PermissionListType, PermissionValueType, RolePerMapType } from './type';
import { type RoleListType } from './type';
import { i18nT } from '../../../web/i18n/utils'; import { i18nT } from '../../../web/i18n/utils';
import { sumPer } from './utils';
export enum AuthUserTypeEnum { export enum AuthUserTypeEnum {
token = 'token', token = 'token',
root = 'root', root = 'root',
...@@ -15,6 +17,12 @@ export enum PermissionTypeEnum { ...@@ -15,6 +17,12 @@ export enum PermissionTypeEnum {
publicRead = 'publicRead', publicRead = 'publicRead',
publicWrite = 'publicWrite' publicWrite = 'publicWrite'
} }
export const NullRoleVal = 0;
export const NullPermissionVal = 0;
export const OwnerRoleVal = ~0 >>> 0;
export const OwnerPermissionVal = ~0 >>> 0;
export const PermissionTypeMap = { export const PermissionTypeMap = {
[PermissionTypeEnum.private]: { [PermissionTypeEnum.private]: {
iconLight: 'support/permission/privateLight', iconLight: 'support/permission/privateLight',
...@@ -45,34 +53,63 @@ export enum PerResourceTypeEnum { ...@@ -45,34 +53,63 @@ export enum PerResourceTypeEnum {
} }
/* new permission */ /* new permission */
export enum PermissionKeyEnum { export enum CommonPerKeyEnum {
owner = 'owner',
read = 'read',
write = 'write',
manage = 'manage'
}
export enum CommonRoleKeyEnum {
read = 'read', read = 'read',
write = 'write', write = 'write',
manage = 'manage' manage = 'manage'
} }
export const PermissionList: PermissionListType = {
[PermissionKeyEnum.read]: { export const CommonPerList: PermissionListType = {
[CommonPerKeyEnum.owner]: OwnerRoleVal,
[CommonPerKeyEnum.read]: 0b100,
[CommonPerKeyEnum.write]: 0b010,
[CommonPerKeyEnum.manage]: 0b001
} as const;
export const CommonRoleList: RoleListType = {
[CommonRoleKeyEnum.read]: {
name: i18nT('common:permission.read'), name: i18nT('common:permission.read'),
description: '', description: '',
value: 0b100, value: 0b100,
checkBoxType: 'single' checkBoxType: 'single'
}, },
[PermissionKeyEnum.write]: { [CommonRoleKeyEnum.write]: {
name: i18nT('common:permission.write'), name: i18nT('common:permission.write'),
description: '', description: '',
value: 0b110, value: 0b010,
checkBoxType: 'single' checkBoxType: 'single'
}, },
[PermissionKeyEnum.manage]: { [CommonRoleKeyEnum.manage]: {
name: i18nT('common:permission.manager'), name: i18nT('common:permission.manager'),
description: '', description: '',
value: 0b111, value: 0b001,
checkBoxType: 'single' checkBoxType: 'single'
} }
}; } as const;
export const NullPermission = 0; export const CommonRolePerMap: RolePerMapType = new Map([
export const OwnerPermissionVal = ~0 >>> 0; [CommonRoleList['read'].value, CommonPerList.read],
export const ReadPermissionVal = PermissionList['read'].value; [
export const WritePermissionVal = PermissionList['write'].value; CommonRoleList['write'].value,
export const ManagePermissionVal = PermissionList['manage'].value; sumPer(CommonPerList.write, CommonPerList.read) as PermissionValueType
],
[
CommonRoleList['manage'].value,
sumPer(CommonPerList.manage, CommonPerList.write, CommonPerList.read) as PermissionValueType
]
]);
export const ReadRoleVal = CommonRoleList['read'].value;
export const WriteRoleVal = CommonRoleList['write'].value;
export const ManageRoleVal = CommonRoleList['manage'].value;
export const ManagePermissionVal = CommonPerList.manage;
export const ReadPermissionVal = CommonPerList.read;
export const WritePermissionVal = CommonPerList.write;
import { type PermissionListType, type PermissionValueType } from './type'; import type {
import { PermissionList, NullPermission, OwnerPermissionVal } from './constant'; RoleValueType,
RoleListType,
PermissionValueType,
PermissionListType,
RolePerMapType
} from './type';
import {
CommonPerList,
CommonRoleList,
CommonRolePerMap,
NullPermissionVal,
NullRoleVal,
OwnerPermissionVal,
OwnerRoleVal
} from './constant';
export type PerConstructPros = { export type PerConstructPros = {
per?: PermissionValueType; role?: RoleValueType;
isOwner?: boolean; isOwner?: boolean;
permissionList?: PermissionListType;
childUpdatePermissionCallback?: () => void; roleList?: RoleListType;
perList?: PermissionListType;
rolePerMap?: RolePerMapType;
}; };
// the Permission helper class /**
* the Permission helper class
*/
export class Permission { export class Permission {
value: PermissionValueType; role: PermissionValueType;
private permission: PermissionValueType = NullRoleVal; // default role
isOwner: boolean = false; isOwner: boolean = false;
hasManagePer: boolean = false; hasManagePer: boolean = false;
hasWritePer: boolean = false; hasWritePer: boolean = false;
hasReadPer: boolean = false; hasReadPer: boolean = false;
_permissionList: PermissionListType; hasManageRole: boolean = false;
hasWriteRole: boolean = false;
hasReadRole: boolean = false;
readonly roleList: RoleListType;
readonly perList: PermissionListType;
readonly rolePerMap: RolePerMapType;
constructor(props?: PerConstructPros) { constructor({
const { per = NullPermission, isOwner = false, permissionList = PermissionList } = props || {}; role = NullRoleVal,
isOwner = false,
roleList = CommonRoleList,
perList = CommonPerList,
rolePerMap = CommonRolePerMap
}: PerConstructPros = {}) {
if (isOwner) { if (isOwner) {
this.value = OwnerPermissionVal; this.role = OwnerRoleVal;
} else { } else {
this.value = per; this.role = role;
} }
this._permissionList = permissionList; this.roleList = roleList;
this.perList = perList;
this.rolePerMap = rolePerMap;
this.updatePermissions(); this.updatePermissions();
} }
// add permission(s) addRole(...roleList: RoleValueType[]) {
// it can be chaining called.
// @example
// const perm = new Permission(permission)
// perm.add(PermissionList['read'])
// perm.add(PermissionList['read'], PermissionList['write'])
// perm.add(PermissionList['read']).add(PermissionList['write'])
addPer(...perList: PermissionValueType[]) {
if (this.isOwner) { if (this.isOwner) {
return this; return this;
} }
for (const per of perList) { for (const per of roleList) {
this.value = this.value | per; this.role = this.role | per;
} }
this.updatePermissions(); this.updatePermissions();
return this; return this;
} }
removePer(...perList: PermissionValueType[]) { removeRole(...roleList: RoleValueType[]) {
if (this.isOwner) { if (this.isOwner) {
return this.value; return this.role;
} }
for (const per of perList) { for (const per of roleList) {
this.value = this.value & ~per; this.role = this.role & ~per;
} }
this.updatePermissions(); this.updatePermissions();
return this; return this;
...@@ -61,9 +89,16 @@ export class Permission { ...@@ -61,9 +89,16 @@ export class Permission {
checkPer(perm: PermissionValueType): boolean { checkPer(perm: PermissionValueType): boolean {
// if the permission is owner permission, only owner has this permission. // if the permission is owner permission, only owner has this permission.
if (perm === OwnerPermissionVal) { if (perm === OwnerPermissionVal) {
return this.value === OwnerPermissionVal; return this.permission === OwnerPermissionVal;
} }
return (this.value & perm) === perm; return (this.permission & perm) === perm;
}
checkRole(role: RoleValueType): boolean {
if (role === OwnerRoleVal) {
return this.role === OwnerRoleVal;
}
return (this.role & role) === role;
} }
private updatePermissionCallback?: () => void; private updatePermissionCallback?: () => void;
...@@ -72,15 +107,32 @@ export class Permission { ...@@ -72,15 +107,32 @@ export class Permission {
this.updatePermissionCallback = callback; this.updatePermissionCallback = callback;
} }
private updatePermissions() { private calculatePer() {
this.isOwner = this.value === OwnerPermissionVal; if (this.role === OwnerRoleVal) {
this.hasManagePer = this.checkPer(this._permissionList['manage'].value); this.permission = OwnerPermissionVal;
this.hasWritePer = this.checkPer(this._permissionList['write'].value); return;
this.hasReadPer = this.checkPer(this._permissionList['read'].value); }
this.updatePermissionCallback?.();
let role = this.role;
this.permission = 0;
while (role > 0) {
// Binary Magic
this.permission |= this.rolePerMap.get(role & -role) ?? 0;
role &= role - 1;
}
} }
toBinary() { private updatePermissions() {
return this.value.toString(2); this.calculatePer();
this.isOwner = this.permission === OwnerRoleVal;
this.hasManagePer = this.checkPer(this.roleList['manage'].value);
this.hasWritePer = this.checkPer(this.roleList['write'].value);
this.hasReadPer = this.checkPer(this.roleList['read'].value);
this.hasManageRole = this.checkRole(this.roleList['manage'].value);
this.hasWriteRole = this.checkRole(this.roleList['write'].value);
this.hasReadRole = this.checkRole(this.roleList['read'].value);
this.updatePermissionCallback?.();
} }
} }
import { i18nT } from '../../../../web/i18n/utils'; import { i18nT } from '../../../../web/i18n/utils';
import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant'; import {
NullRoleVal,
CommonPerKeyEnum,
CommonRoleList,
CommonRolePerMap,
CommonPerList
} from '../constant';
import type { RolePerMapType } from '../type';
export enum DatasetPermissionKeyEnum {} export const DatasetRoleList = {
[CommonPerKeyEnum.read]: {
export const DatasetPermissionList = { ...CommonRoleList[CommonPerKeyEnum.read],
[PermissionKeyEnum.read]: {
...PermissionList[PermissionKeyEnum.read],
description: i18nT('dataset:permission.des.read') description: i18nT('dataset:permission.des.read')
}, },
[PermissionKeyEnum.write]: { [CommonPerKeyEnum.write]: {
...PermissionList[PermissionKeyEnum.write], ...CommonRoleList[CommonPerKeyEnum.write],
description: i18nT('dataset:permission.des.write') description: i18nT('dataset:permission.des.write')
}, },
[PermissionKeyEnum.manage]: { [CommonPerKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage], ...CommonRoleList[CommonPerKeyEnum.manage],
description: i18nT('dataset:permission.des.manage') description: i18nT('dataset:permission.des.manage')
} }
}; };
export const DatasetDefaultPermissionVal = NullPermission; export const DatasetRolePerMap: RolePerMapType = CommonRolePerMap;
export const DatasetPerList = CommonPerList;
export const DataSetDefaultRoleVal = NullRoleVal;
import { NullPermission } from '../constant';
import { type PerConstructPros, Permission } from '../controller'; import { type PerConstructPros, Permission } from '../controller';
import {
DataSetDefaultRoleVal,
DatasetPerList,
DatasetRoleList,
DatasetRolePerMap
} from './constant';
export class DatasetPermission extends Permission { export class DatasetPermission extends Permission {
constructor(props?: PerConstructPros) { constructor(props?: PerConstructPros) {
if (!props) { if (!props) {
props = { props = {
per: NullPermission role: DataSetDefaultRoleVal
}; };
} else if (!props?.per) { } else if (!props?.role) {
props.per = NullPermission; props.role = DataSetDefaultRoleVal;
} }
props.roleList = DatasetRoleList;
props.rolePerMap = DatasetRolePerMap;
props.perList = DatasetPerList;
super(props); super(props);
} }
} }
import { PermissionKeyEnum, PermissionList } from '../constant'; import { CommonPerKeyEnum, CommonRoleList } from '../constant';
import { type PermissionListType } from '../type'; import { type RoleListType } from '../type';
export enum GroupMemberRole { export enum GroupMemberRole {
owner = 'owner', owner = 'owner',
...@@ -7,17 +7,17 @@ export enum GroupMemberRole { ...@@ -7,17 +7,17 @@ export enum GroupMemberRole {
member = 'member' member = 'member'
} }
export const memberGroupPermissionList: PermissionListType = { export const memberGroupPermissionList: RoleListType = {
[PermissionKeyEnum.read]: { [CommonPerKeyEnum.read]: {
...PermissionList[PermissionKeyEnum.read], ...CommonRoleList[CommonPerKeyEnum.read],
value: 0b100 value: 0b100
}, },
[PermissionKeyEnum.write]: { [CommonPerKeyEnum.write]: {
...PermissionList[PermissionKeyEnum.write], ...CommonRoleList[CommonPerKeyEnum.write],
value: 0b010 value: 0b010
}, },
[PermissionKeyEnum.manage]: { [CommonPerKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage], ...CommonRoleList[CommonPerKeyEnum.manage],
value: 0b001 value: 0b001
} }
}; };
...@@ -3,25 +3,63 @@ import type { RequireOnlyOne } from '../../common/type/utils'; ...@@ -3,25 +3,63 @@ import type { RequireOnlyOne } from '../../common/type/utils';
import type { TeamMemberSchema } from '../user/team/type'; import type { TeamMemberSchema } from '../user/team/type';
import { MemberGroupSchemaType } from './memberGroup/type'; import { MemberGroupSchemaType } from './memberGroup/type';
import type { TeamMemberWithUserSchema } from '../user/team/type'; import type { TeamMemberWithUserSchema } from '../user/team/type';
import { AuthUserTypeEnum, type PermissionKeyEnum, type PerResourceTypeEnum } from './constant'; import type { CommonPerKeyEnum, CommonRoleKeyEnum } from './constant';
import { AuthUserTypeEnum, type CommonPerKeyEnum, type PerResourceTypeEnum } from './constant';
// PermissionValueType, the type of permission's value is a number, which is a bit field actually. // 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. // It is spired by the permission system in Linux.
// The lowest 3 bits present the permission of reading, writing and managing. // The lowest 3 bits present the permission of reading, writing and managing.
// The higher bits are advanced permissions or extended permissions, which could be customized. // The higher bits are advanced permissions or extended permissions, which could be customized.
export type PermissionValueType = number; export type PermissionValueType = number;
export type RoleValueType = number;
export type ResourceType = `${PerResourceTypeEnum}`; export type ResourceType = `${PerResourceTypeEnum}`;
export type PermissionListType<T = {}> = Record< /**
T | PermissionKeyEnum, * Define the roles. Each role is a binary number, only one bit is set to 1.
{ */
name: string; export type RoleListType<T = {}> = Readonly<
description: string; Record<
value: PermissionValueType; T | CommonRoleKeyEnum,
checkBoxType: 'single' | 'multiple'; Readonly<{
} name: string;
description: string;
value: RoleValueType;
checkBoxType: 'single' | 'multiple';
}>
>
>;
/**
* Define the permissions. Each permission is a binary number, only one bit is set to 1.
* @example
* CommonPerList = {
* read: 0b100,
* write: 0b010,
* manage: 0b001
* }
* @example_bad
* CommonPerList = {
* write: 0b110, // bad, should be 0b010
* }
*/
export type PermissionListType<T = {}> = Readonly<
Record<T | CommonPerKeyEnum, PermissionValueType>
>; >;
/**
* Define the role-permission map. Each role has a permission.
* @key: role (binary number), only one bit is set to 1.
* @value: permission (binary number), multiple bits are set to 1.
* @example
* CommonRolePerMap = {
* 0b100: 0b100,
* 0b010: 0b110,
* 0b001: 0b111
* }
*/
export type RolePerMapType = Readonly<Map<RoleValueType, PermissionValueType>>;
export type ResourcePermissionType = { export type ResourcePermissionType = {
teamId: string; teamId: string;
resourceType: ResourceType; resourceType: ResourceType;
......
import { PermissionKeyEnum } from '../constant'; import { CommonPerKeyEnum, CommonRolePerMap } from '../constant';
import { type PermissionListType } from '../type'; import type {
import { PermissionList } from '../constant'; PermissionListType,
PermissionValueType,
RoleListType,
RolePerMapType
} from '../type';
import { CommonRoleList, CommonPerList } from '../constant';
import { i18nT } from '../../../../web/i18n/utils'; import { i18nT } from '../../../../web/i18n/utils';
export enum TeamPermissionKeyEnum { import { sumPer } from '../utils';
export enum TeamPerKeyEnum {
appCreate = 'appCreate',
datasetCreate = 'datasetCreate',
apikeyCreate = 'apikeyCreate'
}
export enum TeamRoleKeyEnum {
appCreate = 'appCreate', appCreate = 'appCreate',
datasetCreate = 'datasetCreate', datasetCreate = 'datasetCreate',
apikeyCreate = 'apikeyCreate' apikeyCreate = 'apikeyCreate'
} }
export const TeamPermissionList: PermissionListType<TeamPermissionKeyEnum> = { export const TeamPerList: PermissionListType<TeamPerKeyEnum> = {
[PermissionKeyEnum.read]: { ...CommonPerList,
...PermissionList[PermissionKeyEnum.read], apikeyCreate: 0b100000,
appCreate: 0b001000,
datasetCreate: 0b010000
};
export const TeamRoleList: RoleListType<TeamRoleKeyEnum> = {
[CommonPerKeyEnum.read]: {
...CommonRoleList[CommonPerKeyEnum.read],
value: 0b000100 value: 0b000100
}, },
[PermissionKeyEnum.write]: { [CommonPerKeyEnum.write]: {
...PermissionList[PermissionKeyEnum.write], ...CommonRoleList[CommonPerKeyEnum.write],
value: 0b000010 value: 0b000010
}, },
[PermissionKeyEnum.manage]: { [CommonPerKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage], ...CommonRoleList[CommonPerKeyEnum.manage],
value: 0b000001 value: 0b000001
}, },
[TeamPermissionKeyEnum.appCreate]: { [TeamRoleKeyEnum.appCreate]: {
checkBoxType: 'multiple', checkBoxType: 'multiple',
description: '', description: '',
name: i18nT('account_team:permission_appCreate'), name: i18nT('account_team:permission_appCreate'),
value: 0b001000 value: 0b001000
}, },
[TeamPermissionKeyEnum.datasetCreate]: { [TeamRoleKeyEnum.datasetCreate]: {
checkBoxType: 'multiple', checkBoxType: 'multiple',
description: '', description: '',
name: i18nT('account_team:permission_datasetCreate'), name: i18nT('account_team:permission_datasetCreate'),
value: 0b010000 value: 0b010000
}, },
[TeamPermissionKeyEnum.apikeyCreate]: { [TeamRoleKeyEnum.apikeyCreate]: {
checkBoxType: 'multiple', checkBoxType: 'multiple',
description: '', description: '',
name: i18nT('account_team:permission_apikeyCreate'), name: i18nT('account_team:permission_apikeyCreate'),
...@@ -41,10 +61,38 @@ export const TeamPermissionList: PermissionListType<TeamPermissionKeyEnum> = { ...@@ -41,10 +61,38 @@ export const TeamPermissionList: PermissionListType<TeamPermissionKeyEnum> = {
} }
}; };
export const TeamReadPermissionVal = TeamPermissionList['read'].value; export const TeamRolePerMap: RolePerMapType = new Map([
export const TeamWritePermissionVal = TeamPermissionList['write'].value; ...CommonRolePerMap,
export const TeamManagePermissionVal = TeamPermissionList['manage'].value; [
export const TeamAppCreatePermissionVal = TeamPermissionList['appCreate'].value; TeamRoleList['appCreate'].value,
export const TeamDatasetCreatePermissionVal = TeamPermissionList['datasetCreate'].value; sumPer(TeamPerList.appCreate, CommonPerList.read, CommonPerList.write) as PermissionValueType
export const TeamApikeyCreatePermissionVal = TeamPermissionList['apikeyCreate'].value; ],
[
TeamRoleList['datasetCreate'].value,
sumPer(
TeamPerList.datasetCreate,
CommonPerList.read,
CommonPerList.write
) as PermissionValueType
],
[
TeamRoleList['apikeyCreate'].value,
sumPer(TeamPerList.apikeyCreate, CommonPerList.read, CommonPerList.write) as PermissionValueType
]
]);
export const TeamReadRoleVal = TeamRoleList['read'].value;
export const TeamWriteRoleVal = TeamRoleList['write'].value;
export const TeamManageRoleVal = TeamRoleList['manage'].value;
export const TeamAppCreateRoleVal = TeamRoleList['appCreate'].value;
export const TeamDatasetCreateRoleVal = TeamRoleList['datasetCreate'].value;
export const TeamApikeyCreateRoleVal = TeamRoleList['apikeyCreate'].value;
export const TeamDefaultRoleVal = TeamReadRoleVal;
export const TeamReadPermissionVal = TeamPerList.read;
export const TeamWritePermissionVal = TeamPerList.write;
export const TeamManagePermissionVal = TeamPerList.manage;
export const TeamAppCreatePermissionVal = TeamPerList.appCreate;
export const TeamDatasetCreatePermissionVal = TeamPerList.datasetCreate;
export const TeamApikeyCreatePermissionVal = TeamPerList.apikeyCreate;
export const TeamDefaultPermissionVal = TeamReadPermissionVal; export const TeamDefaultPermissionVal = TeamReadPermissionVal;
import { type PerConstructPros, Permission } from '../controller'; import { type PerConstructPros, Permission } from '../controller';
import { import {
TeamApikeyCreatePermissionVal, TeamApikeyCreateRoleVal,
TeamAppCreatePermissionVal, TeamAppCreateRoleVal,
TeamDatasetCreatePermissionVal, TeamDatasetCreateRoleVal,
TeamDefaultPermissionVal, TeamDefaultRoleVal,
TeamPermissionList TeamPerList,
TeamRoleList,
TeamRolePerMap
} from './constant'; } from './constant';
export class TeamPermission extends Permission { export class TeamPermission extends Permission {
hasAppCreateRole: boolean = false;
hasDatasetCreateRole: boolean = false;
hasApikeyCreateRole: boolean = false;
hasAppCreatePer: boolean = false; hasAppCreatePer: boolean = false;
hasDatasetCreatePer: boolean = false; hasDatasetCreatePer: boolean = false;
hasApikeyCreatePer: boolean = false; hasApikeyCreatePer: boolean = false;
...@@ -15,18 +20,23 @@ export class TeamPermission extends Permission { ...@@ -15,18 +20,23 @@ export class TeamPermission extends Permission {
constructor(props?: PerConstructPros) { constructor(props?: PerConstructPros) {
if (!props) { if (!props) {
props = { props = {
per: TeamDefaultPermissionVal role: TeamDefaultRoleVal
}; };
} else if (!props?.per) { } else if (!props?.role) {
props.per = TeamDefaultPermissionVal; props.role = TeamDefaultRoleVal;
} }
props.permissionList = TeamPermissionList; props.roleList = TeamRoleList;
props.rolePerMap = TeamRolePerMap;
props.perList = TeamPerList;
super(props); super(props);
this.setUpdatePermissionCallback(() => { this.setUpdatePermissionCallback(() => {
this.hasAppCreatePer = this.checkPer(TeamAppCreatePermissionVal); this.hasAppCreateRole = this.checkRole(TeamAppCreateRoleVal);
this.hasDatasetCreatePer = this.checkPer(TeamDatasetCreatePermissionVal); this.hasDatasetCreateRole = this.checkRole(TeamDatasetCreateRoleVal);
this.hasApikeyCreatePer = this.checkPer(TeamApikeyCreatePermissionVal); this.hasApikeyCreateRole = this.checkRole(TeamApikeyCreateRoleVal);
this.hasAppCreatePer = this.checkPer(TeamAppCreateRoleVal);
this.hasDatasetCreatePer = this.checkPer(TeamDatasetCreateRoleVal);
this.hasApikeyCreatePer = this.checkPer(TeamApikeyCreateRoleVal);
}); });
} }
} }
import { type PermissionValueType } from './type'; import { type PermissionValueType } from './type';
import { NullPermission, PermissionTypeEnum } from './constant'; import { NullRoleVal, PermissionTypeEnum } from './constant';
import type { Permission } from './controller'; import type { Permission } from './controller';
/* team public source, or owner source in team */ /* team public source, or owner source in team */
...@@ -30,7 +30,7 @@ export function mongoOwnerPermission({ teamId, tmbId }: { teamId: string; tmbId: ...@@ -30,7 +30,7 @@ export function mongoOwnerPermission({ teamId, tmbId }: { teamId: string; tmbId:
} }
// return permission-related schema to define the schema of resources // return permission-related schema to define the schema of resources
export function getPermissionSchema(defaultPermission: PermissionValueType = NullPermission) { export function getPermissionSchema(defaultPermission: PermissionValueType = NullRoleVal) {
return { return {
defaultPermission: { defaultPermission: {
type: Number, type: Number,
...@@ -42,3 +42,11 @@ export function getPermissionSchema(defaultPermission: PermissionValueType = Nul ...@@ -42,3 +42,11 @@ export function getPermissionSchema(defaultPermission: PermissionValueType = Nul
} }
}; };
} }
export const sumPer = (...per: PermissionValueType[]) => {
if (per.length === 0) {
// prevent sum 0 value, to fallback to default value
return undefined;
}
return per.reduce((acc, cur) => acc | cur, 0);
};
...@@ -16,17 +16,17 @@ export const filterDatasetsByTmbId = async ({ ...@@ -16,17 +16,17 @@ export const filterDatasetsByTmbId = async ({
// First get all permissions // First get all permissions
const permissions = await Promise.all( const permissions = await Promise.all(
datasetIds.map(async (datasetId) => { datasetIds.map(async (datasetId) => {
const per = await getResourcePermission({ const role = await getResourcePermission({
teamId, teamId,
tmbId, tmbId,
resourceId: datasetId, resourceId: datasetId,
resourceType: PerResourceTypeEnum.dataset resourceType: PerResourceTypeEnum.dataset
}); });
if (per === undefined) return false; if (role === undefined) return false;
const datasetPer = new DatasetPermission({ const datasetPer = new DatasetPermission({
per, role,
isOwner: tmbPer.isOwner isOwner: tmbPer.isOwner
}); });
......
...@@ -12,7 +12,6 @@ import { AppFolderTypeList } from '@fastgpt/global/core/app/constants'; ...@@ -12,7 +12,6 @@ import { AppFolderTypeList } from '@fastgpt/global/core/app/constants';
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type'; import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { PluginSourceEnum } from '@fastgpt/global/core/app/plugin/constants'; import { PluginSourceEnum } from '@fastgpt/global/core/app/plugin/constants';
import { type AuthModeType, type AuthResponseType } from '../type'; import { type AuthModeType, type AuthResponseType } from '../type';
import { AppDefaultPermissionVal } from '@fastgpt/global/support/permission/app/constant';
import { splitCombinePluginId } from '@fastgpt/global/core/app/plugin/utils'; import { splitCombinePluginId } from '@fastgpt/global/core/app/plugin/utils';
export const authPluginByTmbId = async ({ export const authPluginByTmbId = async ({
...@@ -83,16 +82,16 @@ export const authAppByTmbId = async ({ ...@@ -83,16 +82,16 @@ export const authAppByTmbId = async ({
app.inheritPermission === false || app.inheritPermission === false ||
!app.parentId !app.parentId
) { ) {
// 1. is a folder. (Folders have compeletely permission) // 1. is a folder. (Folders have completely permission)
// 2. inheritPermission is false. // 2. inheritPermission is false.
// 3. is root folder/app. // 3. is root folder/app.
const rp = await getResourcePermission({ const role = await getResourcePermission({
teamId, teamId,
tmbId, tmbId,
resourceId: appId, resourceId: appId,
resourceType: PerResourceTypeEnum.app resourceType: PerResourceTypeEnum.app
}); });
const Per = new AppPermission({ per: rp ?? AppDefaultPermissionVal, isOwner }); const Per = new AppPermission({ role, isOwner });
return { return {
Per Per
}; };
...@@ -105,7 +104,7 @@ export const authAppByTmbId = async ({ ...@@ -105,7 +104,7 @@ export const authAppByTmbId = async ({
}); });
const Per = new AppPermission({ const Per = new AppPermission({
per: parent.permission.value, role: parent.permission.role,
isOwner isOwner
}); });
return { return {
......
...@@ -4,7 +4,7 @@ import { parseHeaderCert } from '../controller'; ...@@ -4,7 +4,7 @@ import { parseHeaderCert } from '../controller';
import { getFileById } from '../../../common/file/gridfs/controller'; import { getFileById } from '../../../common/file/gridfs/controller';
import { BucketNameEnum } from '@fastgpt/global/common/file/constants'; import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common'; import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { OwnerPermissionVal, ReadPermissionVal } from '@fastgpt/global/support/permission/constant'; import { OwnerPermissionVal, ReadRoleVal } from '@fastgpt/global/support/permission/constant';
import { Permission } from '@fastgpt/global/support/permission/controller'; import { Permission } from '@fastgpt/global/support/permission/controller';
export const authCollectionFile = async ({ export const authCollectionFile = async ({
...@@ -32,7 +32,7 @@ export const authCollectionFile = async ({ ...@@ -32,7 +32,7 @@ export const authCollectionFile = async ({
} }
const permission = new Permission({ const permission = new Permission({
per: ReadPermissionVal, role: ReadRoleVal,
isOwner: file.metadata?.uid === tmbId || file.metadata?.tmbId === tmbId isOwner: file.metadata?.uid === tmbId || file.metadata?.tmbId === tmbId
}); });
......
...@@ -6,7 +6,6 @@ import { MongoOpenApi } from '../../openapi/schema'; ...@@ -6,7 +6,6 @@ import { MongoOpenApi } from '../../openapi/schema';
import { OpenApiErrEnum } from '@fastgpt/global/common/error/code/openapi'; import { OpenApiErrEnum } from '@fastgpt/global/common/error/code/openapi';
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant'; import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
import { authAppByTmbId } from '../app/auth'; import { authAppByTmbId } from '../app/auth';
import { Permission } from '@fastgpt/global/support/permission/controller';
export async function authOpenApiKeyCrud({ export async function authOpenApiKeyCrud({
id, id,
...@@ -49,9 +48,7 @@ export async function authOpenApiKeyCrud({ ...@@ -49,9 +48,7 @@ export async function authOpenApiKeyCrud({
return { return {
openapi, openapi,
permission: new Permission({ permission: tmbPer
per
})
}; };
})(); })();
......
...@@ -21,6 +21,7 @@ import { type TeamMemberSchema } from '@fastgpt/global/support/user/team/type'; ...@@ -21,6 +21,7 @@ import { type TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
import { type OrgSchemaType } from '@fastgpt/global/support/user/team/org/type'; import { type OrgSchemaType } from '@fastgpt/global/support/user/team/org/type';
import { getOrgIdSetWithParentByTmbId } from './org/controllers'; import { getOrgIdSetWithParentByTmbId } from './org/controllers';
import { authUserSession } from '../user/session'; import { authUserSession } from '../user/session';
import { sumPer } from '@fastgpt/global/support/permission/utils';
/** get resource permission for a team member /** get resource permission for a team member
* If there is no permission for the team member, it will return undefined * If there is no permission for the team member, it will return undefined
...@@ -102,7 +103,7 @@ export const getResourcePermission = async ({ ...@@ -102,7 +103,7 @@ export const getResourcePermission = async ({
.then((perList) => perList.map((item) => item.permission)) .then((perList) => perList.map((item) => item.permission))
]); ]);
return concatPer([...groupPers, ...orgPers]); return sumPer(...groupPers, ...orgPers);
}; };
export async function getResourceClbsAndGroups({ export async function getResourceClbsAndGroups({
...@@ -402,11 +403,3 @@ export const authFileToken = (token?: string) => ...@@ -402,11 +403,3 @@ export const authFileToken = (token?: string) =>
}); });
}); });
}); });
export const concatPer = (perList: PermissionValueType[] = []) => {
if (perList.length === 0) {
return undefined;
}
return new Permission().addPer(...perList).value;
};
...@@ -7,7 +7,10 @@ import { ...@@ -7,7 +7,10 @@ import {
} from '@fastgpt/global/core/dataset/type'; } from '@fastgpt/global/core/dataset/type';
import { getTmbInfoByTmbId } from '../../user/team/controller'; import { getTmbInfoByTmbId } from '../../user/team/controller';
import { MongoDataset } from '../../../core/dataset/schema'; import { MongoDataset } from '../../../core/dataset/schema';
import { NullPermission, PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant'; import {
NullPermissionVal,
PerResourceTypeEnum
} from '@fastgpt/global/support/permission/constant';
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset'; import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller'; import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
import { getCollectionWithDataset } from '../../../core/dataset/controller'; import { getCollectionWithDataset } from '../../../core/dataset/controller';
...@@ -15,7 +18,7 @@ import { MongoDatasetData } from '../../../core/dataset/data/schema'; ...@@ -15,7 +18,7 @@ import { MongoDatasetData } from '../../../core/dataset/data/schema';
import { type AuthModeType, type AuthResponseType } from '../type'; import { type AuthModeType, type AuthResponseType } from '../type';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants'; import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type'; import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant'; import { DataSetDefaultRoleVal } from '@fastgpt/global/support/permission/dataset/constant';
import { getDatasetImagePreviewUrl } from '../../../core/dataset/image/utils'; import { getDatasetImagePreviewUrl } from '../../../core/dataset/image/utils';
import { i18nT } from '../../../../web/i18n/utils'; import { i18nT } from '../../../../web/i18n/utils';
...@@ -71,7 +74,7 @@ export const authDatasetByTmbId = async ({ ...@@ -71,7 +74,7 @@ export const authDatasetByTmbId = async ({
dataset.inheritPermission === false || dataset.inheritPermission === false ||
!dataset.parentId !dataset.parentId
) { ) {
// 1. is a folder. (Folders have compeletely permission) // 1. is a folder. (Folders have completely permission)
// 2. inheritPermission is false. // 2. inheritPermission is false.
// 3. is root folder/dataset. // 3. is root folder/dataset.
const rp = await getResourcePermission({ const rp = await getResourcePermission({
...@@ -81,7 +84,7 @@ export const authDatasetByTmbId = async ({ ...@@ -81,7 +84,7 @@ export const authDatasetByTmbId = async ({
resourceType: PerResourceTypeEnum.dataset resourceType: PerResourceTypeEnum.dataset
}); });
const Per = new DatasetPermission({ const Per = new DatasetPermission({
per: rp ?? DatasetDefaultPermissionVal, role: rp,
isOwner isOwner
}); });
return { return {
...@@ -97,7 +100,7 @@ export const authDatasetByTmbId = async ({ ...@@ -97,7 +100,7 @@ export const authDatasetByTmbId = async ({
}); });
const Per = new DatasetPermission({ const Per = new DatasetPermission({
per: parent.permission.value, role: parent.permission.role,
isOwner isOwner
}); });
...@@ -158,7 +161,7 @@ export const authDataset = async ({ ...@@ -158,7 +161,7 @@ export const authDataset = async ({
// the temporary solution for authDatasetCollection is getting the // the temporary solution for authDatasetCollection is getting the
export async function authDatasetCollection({ export async function authDatasetCollection({
collectionId, collectionId,
per = NullPermission, per = NullPermissionVal,
isRoot = false, isRoot = false,
...props ...props
}: AuthModeType & { }: AuthModeType & {
...@@ -242,7 +245,7 @@ export async function authDatasetCollection({ ...@@ -242,7 +245,7 @@ export async function authDatasetCollection({
// } // }
// } // }
/* /*
DatasetData permission is inherited from collection. DatasetData permission is inherited from collection.
*/ */
export async function authDatasetData({ export async function authDatasetData({
......
...@@ -3,7 +3,7 @@ import { parseHeaderCert } from '../controller'; ...@@ -3,7 +3,7 @@ import { parseHeaderCert } from '../controller';
import { getTmbInfoByTmbId } from '../../user/team/controller'; import { getTmbInfoByTmbId } from '../../user/team/controller';
import { TeamErrEnum } from '@fastgpt/global/common/error/code/team'; import { TeamErrEnum } from '@fastgpt/global/common/error/code/team';
import { type AuthModeType, type AuthResponseType } from '../type'; import { type AuthModeType, type AuthResponseType } from '../type';
import { NullPermission } from '@fastgpt/global/support/permission/constant'; import { NullPermissionVal } from '@fastgpt/global/support/permission/constant';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller'; import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { authCert } from '../auth/common'; import { authCert } from '../auth/common';
import { MongoUser } from '../../user/schema'; import { MongoUser } from '../../user/schema';
...@@ -28,7 +28,7 @@ export async function authUserPer(props: AuthModeType): Promise< ...@@ -28,7 +28,7 @@ export async function authUserPer(props: AuthModeType): Promise<
tmb tmb
}; };
} }
if (!tmb.permission.checkPer(props.per ?? NullPermission)) { if (!tmb.permission.checkPer(props.per ?? NullPermissionVal)) {
return Promise.reject(TeamErrEnum.unAuthTeam); return Promise.reject(TeamErrEnum.unAuthTeam);
} }
......
...@@ -11,7 +11,7 @@ import { type UpdateTeamProps } from '@fastgpt/global/support/user/team/controll ...@@ -11,7 +11,7 @@ import { type UpdateTeamProps } from '@fastgpt/global/support/user/team/controll
import { getResourcePermission } from '../../permission/controller'; import { getResourcePermission } from '../../permission/controller';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant'; import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller'; import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { TeamDefaultPermissionVal } from '@fastgpt/global/support/permission/user/constant'; import { TeamDefaultRoleVal } from '@fastgpt/global/support/permission/user/constant';
import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupSchema'; import { MongoMemberGroupModel } from '../../permission/memberGroup/memberGroupSchema';
import { mongoSessionRun } from '../../../common/mongo/sessionRun'; import { mongoSessionRun } from '../../../common/mongo/sessionRun';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant'; import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
...@@ -25,11 +25,12 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp ...@@ -25,11 +25,12 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
return Promise.reject('member not exist'); return Promise.reject('member not exist');
} }
const Per = await getResourcePermission({ const role =
resourceType: PerResourceTypeEnum.team, (await getResourcePermission({
teamId: tmb.teamId, resourceType: PerResourceTypeEnum.team,
tmbId: tmb._id teamId: tmb.teamId,
}); tmbId: tmb._id
})) ?? TeamDefaultRoleVal;
return { return {
userId: String(tmb.userId), userId: String(tmb.userId),
...@@ -44,7 +45,7 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp ...@@ -44,7 +45,7 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
role: tmb.role, role: tmb.role,
status: tmb.status, status: tmb.status,
permission: new TeamPermission({ permission: new TeamPermission({
per: Per ?? TeamDefaultPermissionVal, role,
isOwner: tmb.role === TeamMemberRoleEnum.owner isOwner: tmb.role === TeamMemberRoleEnum.owner
}), }),
notificationAccount: tmb.team.notificationAccount, notificationAccount: tmb.team.notificationAccount,
......
...@@ -143,6 +143,8 @@ ...@@ -143,6 +143,8 @@
"permission.des.manage": "Based on write permissions, you can configure publishing channels, view conversation logs, and assign permissions to the application.", "permission.des.manage": "Based on write permissions, you can configure publishing channels, view conversation logs, and assign permissions to the application.",
"permission.des.read": "Use the app to have conversations", "permission.des.read": "Use the app to have conversations",
"permission.des.write": "Can view and edit apps", "permission.des.write": "Can view and edit apps",
"permission.des.readChatLog": "Can view chat logs",
"permission.name.readChatLog": "View chat logs",
"plugin.Instructions": "Instructions", "plugin.Instructions": "Instructions",
"plugin_cost_by_token": "Charged based on token usage", "plugin_cost_by_token": "Charged based on token usage",
"plugin_cost_per_times": "{{cost}} points/time", "plugin_cost_per_times": "{{cost}} points/time",
......
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,7 @@
"Parse": "Analysis", "Parse": "Analysis",
"Permission": "Permission", "Permission": "Permission",
"Permission_tip": "Individual permissions are greater than group permissions", "Permission_tip": "Individual permissions are greater than group permissions",
"permission_other": "Other permissions (multiple)",
"Preview": "Preview", "Preview": "Preview",
"Remove": "Remove", "Remove": "Remove",
"Rename": "Rename", "Rename": "Rename",
......
...@@ -143,6 +143,8 @@ ...@@ -143,6 +143,8 @@
"permission.des.manage": "写权限基础上,可配置发布渠道、查看对话日志、分配该应用权限", "permission.des.manage": "写权限基础上,可配置发布渠道、查看对话日志、分配该应用权限",
"permission.des.read": "可使用该应用进行对话", "permission.des.read": "可使用该应用进行对话",
"permission.des.write": "可查看和编辑应用", "permission.des.write": "可查看和编辑应用",
"permission.des.readChatLog": "可查看对话日志",
"permission.name.readChatLog": "查看对话日志",
"plugin.Instructions": "使用说明", "plugin.Instructions": "使用说明",
"plugin_cost_by_token": "依据 token 消耗计费", "plugin_cost_by_token": "依据 token 消耗计费",
"plugin_cost_per_times": "{{cost}} 积分/次", "plugin_cost_per_times": "{{cost}} 积分/次",
......
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,7 @@
"Parse": "解析", "Parse": "解析",
"Permission": "权限", "Permission": "权限",
"Permission_tip": "个人权限大于群组权限", "Permission_tip": "个人权限大于群组权限",
"permission_other": "其他权限(多选)",
"Preview": "预览", "Preview": "预览",
"Remove": "移除", "Remove": "移除",
"Rename": "重命名", "Rename": "重命名",
......
...@@ -143,6 +143,8 @@ ...@@ -143,6 +143,8 @@
"permission.des.manage": "在寫入權限基礎上,可以設定發布通道、檢視對話紀錄、分配這個應用程式的權限", "permission.des.manage": "在寫入權限基礎上,可以設定發布通道、檢視對話紀錄、分配這個應用程式的權限",
"permission.des.read": "可以使用這個應用程式進行對話", "permission.des.read": "可以使用這個應用程式進行對話",
"permission.des.write": "可以檢視和編輯應用程式", "permission.des.write": "可以檢視和編輯應用程式",
"permission.des.readChatLog": "可以檢視對話紀錄",
"permission.name.readChatLog": "檢視對話紀錄",
"plugin.Instructions": "使用說明", "plugin.Instructions": "使用說明",
"plugin_cost_by_token": "根據 token 消耗計費", "plugin_cost_by_token": "根據 token 消耗計費",
"plugin_cost_per_times": "{{cost}} 積分/次", "plugin_cost_per_times": "{{cost}} 積分/次",
......
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,7 @@
"Parse": "解析", "Parse": "解析",
"Permission": "權限", "Permission": "權限",
"Permission_tip": "個人權限大於群組權限", "Permission_tip": "個人權限大於群組權限",
"permission_other": "其他權限(多選)",
"Preview": "預覽", "Preview": "預覽",
"Remove": "移除", "Remove": "移除",
"Rename": "重新命名", "Rename": "重新命名",
......
...@@ -11,8 +11,8 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest'; ...@@ -11,8 +11,8 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import React from 'react'; import React from 'react';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import PermissionSelect from './PermissionSelect'; import RoleSelect from './RoleSelect';
import PermissionTags from './PermissionTags'; import RoleTags from './RoleTags';
import { CollaboratorContext } from './context'; import { CollaboratorContext } from './context';
export type ManageModalProps = { export type ManageModalProps = {
onClose: () => void; onClose: () => void;
...@@ -70,17 +70,17 @@ function ManageModal({ onClose }: ManageModalProps) { ...@@ -70,17 +70,17 @@ function ManageModal({ onClose }: ManageModalProps) {
</Flex> </Flex>
</Td> </Td>
<Td border="none"> <Td border="none">
<PermissionTags permission={item.permission.value} /> <RoleTags permission={item.permission.role} />
</Td> </Td>
<Td border="none"> <Td border="none">
{/* Not self; Not owner and other manager */} {/* Not self; Not owner and other manager */}
{item.tmbId !== userInfo?.team?.tmbId && {item.tmbId !== userInfo?.team?.tmbId &&
(permission.isOwner || !item.permission.hasManagePer) && ( (permission.isOwner || !item.permission.hasManagePer) && (
<PermissionSelect <RoleSelect
Button={ Button={
<MyIcon name={'edit'} w={'16px'} _hover={{ color: 'primary.600' }} /> <MyIcon name={'edit'} w={'16px'} _hover={{ color: 'primary.600' }} />
} }
value={item.permission.value} value={item.permission.role}
onChange={(permission) => { onChange={(permission) => {
onUpdate({ onUpdate({
members: item.tmbId ? [item.tmbId] : undefined, members: item.tmbId ? [item.tmbId] : undefined,
......
...@@ -2,8 +2,8 @@ import React from 'react'; ...@@ -2,8 +2,8 @@ import React from 'react';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { Box, Checkbox, HStack, VStack } from '@chakra-ui/react'; import { Box, Checkbox, HStack, VStack } from '@chakra-ui/react';
import Avatar from '@fastgpt/web/components/common/Avatar'; import Avatar from '@fastgpt/web/components/common/Avatar';
import PermissionTags from './PermissionTags'; import RoleTags from './RoleTags';
import { type PermissionValueType } from '@fastgpt/global/support/permission/type'; import type { RoleValueType } from '@fastgpt/global/support/permission/type';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import OrgTags from '../../user/team/OrgTags'; import OrgTags from '../../user/team/OrgTags';
import Tag from '@fastgpt/web/components/common/Tag'; import Tag from '@fastgpt/web/components/common/Tag';
...@@ -15,7 +15,7 @@ function MemberItemCard({ ...@@ -15,7 +15,7 @@ function MemberItemCard({
isChecked, isChecked,
onDelete, onDelete,
name, name,
permission, role: permission,
orgs, orgs,
addOnly, addOnly,
rightSlot rightSlot
...@@ -26,7 +26,7 @@ function MemberItemCard({ ...@@ -26,7 +26,7 @@ function MemberItemCard({
isChecked?: boolean; isChecked?: boolean;
onDelete?: () => void; onDelete?: () => void;
name: string; name: string;
permission?: PermissionValueType; role?: RoleValueType;
addOnly?: boolean; addOnly?: boolean;
orgs?: string[]; orgs?: string[];
rightSlot?: React.ReactNode; rightSlot?: React.ReactNode;
...@@ -61,7 +61,7 @@ function MemberItemCard({ ...@@ -61,7 +61,7 @@ function MemberItemCard({
</Box> </Box>
<Box lineHeight={1}>{orgs && orgs.length > 0 && <OrgTags orgs={orgs} />}</Box> <Box lineHeight={1}>{orgs && orgs.length > 0 && <OrgTags orgs={orgs} />}</Box>
</Box> </Box>
{!isAdded && permission && <PermissionTags permission={permission} />} {!isAdded && permission && <RoleTags permission={permission} />}
{isAdded && ( {isAdded && (
<Tag <Tag
mixBlendMode={'multiply'} mixBlendMode={'multiply'}
......
...@@ -27,7 +27,7 @@ import { useMemo, useRef, useState } from 'react'; ...@@ -27,7 +27,7 @@ import { useMemo, useRef, useState } from 'react';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { CollaboratorContext } from './context'; import { CollaboratorContext } from './context';
import MemberItemCard from './MemberItemCard'; import MemberItemCard from './MemberItemCard';
import PermissionSelect from './PermissionSelect'; import RoleSelect from './RoleSelect';
const HoverBoxStyle = { const HoverBoxStyle = {
bgColor: 'myGray.50', bgColor: 'myGray.50',
...@@ -98,15 +98,13 @@ function MemberModal({ ...@@ -98,15 +98,13 @@ function MemberModal({
>([]); >([]);
const [selectedGroupList, setSelectedGroupList] = useState<MemberGroupListItemType<false>[]>([]); const [selectedGroupList, setSelectedGroupList] = useState<MemberGroupListItemType<false>[]>([]);
const permissionList = useContextSelector(CollaboratorContext, (v) => v.permissionList); const roleList = useContextSelector(CollaboratorContext, (v) => v.roleList);
const getPerLabelList = useContextSelector(CollaboratorContext, (v) => v.getPerLabelList); const getRoleLabelList = useContextSelector(CollaboratorContext, (v) => v.getRoleLabelList);
const [selectedPermission, setSelectedPermission] = useState<number | undefined>( const [selectedRole, setSelectedRole] = useState<number | undefined>(roleList?.read?.value);
permissionList?.read?.value const roleLabel = useMemo(() => {
); if (selectedRole === undefined) return '';
const perLabel = useMemo(() => { return getRoleLabelList(selectedRole!).join('、');
if (selectedPermission === undefined) return ''; }, [getRoleLabelList, selectedRole]);
return getPerLabelList(selectedPermission!).join('、');
}, [getPerLabelList, selectedPermission]);
const onUpdateCollaborators = useContextSelector( const onUpdateCollaborators = useContextSelector(
CollaboratorContext, CollaboratorContext,
...@@ -119,7 +117,7 @@ function MemberModal({ ...@@ -119,7 +117,7 @@ function MemberModal({
members: selectedMemberList.map((item) => item.tmbId), members: selectedMemberList.map((item) => item.tmbId),
groups: selectedGroupList.map((item) => item._id), groups: selectedGroupList.map((item) => item._id),
orgs: selectedOrgList.map((item) => item._id), orgs: selectedOrgList.map((item) => item._id),
permission: addOnly ? undefined : selectedPermission! permission: addOnly ? undefined : selectedRole!
} as UpdateClbPermissionProps<ValueOf<typeof addOnly>>), } as UpdateClbPermissionProps<ValueOf<typeof addOnly>>),
{ {
successToast: t('common:add_success'), successToast: t('common:add_success'),
...@@ -277,7 +275,7 @@ function MemberModal({ ...@@ -277,7 +275,7 @@ function MemberModal({
avatar={member.avatar} avatar={member.avatar}
key={member.tmbId} key={member.tmbId}
name={member.memberName} name={member.memberName}
permission={collaborator?.permission.value} role={collaborator?.permission.role}
onChange={onChange} onChange={onChange}
isChecked={!!selectedMemberList.find((v) => v.tmbId === member.tmbId)} isChecked={!!selectedMemberList.find((v) => v.tmbId === member.tmbId)}
orgs={member.orgs} orgs={member.orgs}
...@@ -316,7 +314,7 @@ function MemberModal({ ...@@ -316,7 +314,7 @@ function MemberModal({
name={org.name} name={org.name}
onChange={onChange} onChange={onChange}
addOnly={addOnly} addOnly={addOnly}
permission={collaborator?.permission.value} role={collaborator?.permission.role}
isChecked={!!selectedOrgList.find((v) => String(v._id) === String(org._id))} isChecked={!!selectedOrgList.find((v) => String(v._id) === String(org._id))}
rightSlot={ rightSlot={
org.total && ( org.total && (
...@@ -365,8 +363,8 @@ function MemberModal({ ...@@ -365,8 +363,8 @@ function MemberModal({
}); });
}} }}
isChecked={isChecked} isChecked={isChecked}
permission={collaborator?.permission.value} role={collaborator?.permission.role}
addOnly={addOnly && !!member.permission.value} addOnly={addOnly && !!member.permission.role}
orgs={member.orgs} orgs={member.orgs}
/> />
); );
...@@ -392,7 +390,7 @@ function MemberModal({ ...@@ -392,7 +390,7 @@ function MemberModal({
name={ name={
group.name === DefaultGroupName ? userInfo?.team.teamName ?? '' : group.name group.name === DefaultGroupName ? userInfo?.team.teamName ?? '' : group.name
} }
permission={collaborator?.permission.value} role={collaborator?.permission.role}
onChange={onChange} onChange={onChange}
isChecked={!!selectedGroupList.find((v) => v._id === group._id)} isChecked={!!selectedGroupList.find((v) => v._id === group._id)}
addOnly={addOnly} addOnly={addOnly}
...@@ -425,9 +423,9 @@ function MemberModal({ ...@@ -425,9 +423,9 @@ function MemberModal({
</Grid> </Grid>
</ModalBody> </ModalBody>
<ModalFooter> <ModalFooter>
{!addOnly && !!permissionList && ( {!addOnly && !!roleList && (
<PermissionSelect <RoleSelect
value={selectedPermission} value={selectedRole}
Button={ Button={
<Flex <Flex
alignItems={'center'} alignItems={'center'}
...@@ -438,11 +436,11 @@ function MemberModal({ ...@@ -438,11 +436,11 @@ function MemberModal({
borderRadius={'md'} borderRadius={'md'}
h={'32px'} h={'32px'}
> >
{t(perLabel as any)} {roleLabel}
<ChevronDownIcon fontSize={'md'} /> <ChevronDownIcon fontSize={'md'} />
</Flex> </Flex>
} }
onChange={(v) => setSelectedPermission(v)} onChange={(v) => setSelectedRole(v)}
/> />
)} )}
{addOnly && ( {addOnly && (
......
...@@ -7,11 +7,12 @@ import { ...@@ -7,11 +7,12 @@ import {
Radio, Radio,
useOutsideClick, useOutsideClick,
HStack, HStack,
MenuButton MenuButton,
Checkbox
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import React, { useMemo, useRef, useState } from 'react'; import React, { useMemo, useRef, useState } from 'react';
import MyIcon from '@fastgpt/web/components/common/Icon'; 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 { useContextSelector } from 'use-context-selector';
import { Permission } from '@fastgpt/global/support/permission/controller'; import { Permission } from '@fastgpt/global/support/permission/controller';
import { CollaboratorContext } from './context'; import { CollaboratorContext } from './context';
...@@ -19,8 +20,8 @@ import { useTranslation } from 'next-i18next'; ...@@ -19,8 +20,8 @@ import { useTranslation } from 'next-i18next';
import MyDivider from '@fastgpt/web/components/common/MyDivider'; import MyDivider from '@fastgpt/web/components/common/MyDivider';
export type PermissionSelectProps = { export type PermissionSelectProps = {
value?: PermissionValueType; value?: RoleValueType;
onChange: (value: PermissionValueType) => void; onChange: (value: RoleValueType) => void;
trigger?: 'hover' | 'click'; trigger?: 'hover' | 'click';
offset?: [number, number]; offset?: [number, number];
Button: React.ReactNode; Button: React.ReactNode;
...@@ -39,8 +40,8 @@ const MenuStyle = { ...@@ -39,8 +40,8 @@ const MenuStyle = {
fontSize: 'sm' fontSize: 'sm'
}; };
function PermissionSelect({ function RoleSelect({
value, value: role,
onChange, onChange,
trigger = 'click', trigger = 'click',
offset = [0, 5], offset = [0, 5],
...@@ -49,15 +50,18 @@ function PermissionSelect({ ...@@ -49,15 +50,18 @@ function PermissionSelect({
onDelete onDelete
}: PermissionSelectProps) { }: PermissionSelectProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const ref = useRef<HTMLButtonElement>(null); const ref = useRef<HTMLDivElement>(null);
const closeTimer = useRef<NodeJS.Timeout>(); 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 [isOpen, setIsOpen] = useState(false);
const permissionSelectList = useMemo(() => { const roleOptions = useMemo(() => {
if (!permissionList) return { singleCheckBoxList: [], multipleCheckBoxList: [] }; if (!permissionList) return { singleOptions: [], checkboxList: [] };
const list = Object.entries(permissionList).map(([_, value]) => { const list = Object.entries(permissionList).map(([_, value]) => {
return { return {
...@@ -69,44 +73,42 @@ function PermissionSelect({ ...@@ -69,44 +73,42 @@ function PermissionSelect({
}); });
return { return {
singleCheckBoxList: list singleOptions: list.filter(
.filter((item) => item.checkBoxType === 'single') (item) =>
.filter((item) => { item.checkBoxType === 'single' &&
if (permission.isOwner) return true; (permission.isOwner || item.value !== permissionList['manage'].value)
if (item.value === permissionList['manage'].value) return false; ),
return true; checkboxList: list.filter((item) => item.checkBoxType === 'multiple')
}),
multipleCheckBoxList: list.filter((item) => item.checkBoxType === 'multiple')
}; };
}, [permission.isOwner, permissionList]); }, [permission.isOwner, permissionList]);
const selectedSingleValue = useMemo(() => { const selectedSingleValue = useMemo(() => {
if (!permissionList) return undefined; if (!permissionList) return undefined;
const per = new Permission({ per: value }); const per = new Permission({ role });
if (per.hasManagePer) return permissionList['manage'].value; if (per.hasManagePer) return permissionList['manage'].value;
if (per.hasWritePer) return permissionList['write'].value; if (per.hasWritePer) return permissionList['write'].value;
return permissionList['read'].value; return permissionList['read'].value;
}, [permissionList, value]); }, [permissionList, role]);
// const selectedMultipleValues = useMemo(() => { const selectedMultipleValues = useMemo(() => {
// const per = new Permission({ per: value }); const per = new Permission({ role });
//
// return permissionSelectList.multipleCheckBoxList return roleOptions.checkboxList
// .filter((item) => { .filter((item) => {
// return per.checkPer(item.value); return per.checkRole(item.value);
// }) })
// .map((item) => item.value); .map((item) => item.value);
// }, [permissionSelectList.multipleCheckBoxList, value]); }, [role, roleOptions.checkboxList]);
const onSelectPer = (per: PermissionValueType) => { const onSelectRole = (newRole: RoleValueType) => {
if (per === value) return; if (newRole === role) return;
onChange(per); onChange(newRole);
setIsOpen(false); // setIsOpen(false);
}; };
useOutsideClick({ useOutsideClick({
ref: ref, ref,
handler: () => { handler: () => {
setIsOpen(false); setIsOpen(false);
} }
...@@ -115,6 +117,7 @@ function PermissionSelect({ ...@@ -115,6 +117,7 @@ function PermissionSelect({
return selectedSingleValue !== undefined ? ( return selectedSingleValue !== undefined ? (
<Menu offset={offset} isOpen={isOpen} autoSelect={false} direction={'ltr'}> <Menu offset={offset} isOpen={isOpen} autoSelect={false} direction={'ltr'}>
<Box <Box
ref={ref}
w="fit-content" w="fit-content"
onMouseEnter={() => { onMouseEnter={() => {
if (trigger === 'hover') { if (trigger === 'hover') {
...@@ -131,7 +134,6 @@ function PermissionSelect({ ...@@ -131,7 +134,6 @@ function PermissionSelect({
}} }}
> >
<MenuButton <MenuButton
ref={ref}
position={'relative'} position={'relative'}
onClickCapture={() => { onClickCapture={() => {
if (trigger === 'click') { if (trigger === 'click') {
...@@ -153,12 +155,12 @@ function PermissionSelect({ ...@@ -153,12 +155,12 @@ function PermissionSelect({
whiteSpace={'pre-wrap'} whiteSpace={'pre-wrap'}
> >
{/* The list of single select permissions */} {/* The list of single select permissions */}
{permissionSelectList.singleCheckBoxList.map((item) => { {roleOptions.singleOptions.map((item) => {
const change = () => { const change = () => {
const per = new Permission({ per: value }); const per = new Permission({ role });
per.removePer(selectedSingleValue); per.removeRole(selectedSingleValue);
per.addPer(item.value); per.addRole(item.value);
onSelectPer(per.value); onSelectRole(per.role);
}; };
return ( return (
...@@ -184,50 +186,45 @@ function PermissionSelect({ ...@@ -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'}>
{/* The list of multiple select permissions */} {t('common:permission_other')}
{/* {list </Box>
.filter((item) => item.type === 'multiple') )}
.map((item) => {
const change = () => { {roleOptions.checkboxList.map((item) => {
if (checkPermission(valueState, item.value)) { const change = () => {
setValueState(new Permission(valueState).remove(item.value).value); const per = new Permission({ role });
} else { if (per.checkRole(item.value)) {
setValueState(new Permission(valueState).add(item.value).value); per.removeRole(item.value);
} } else {
}; per.addRole(item.value);
return ( }
<Flex onSelectRole(per.role);
key={item.value} };
{...(checkPermission(valueState, item.value) const isChecked = selectedMultipleValues.includes(item.value);
? { return (
color: 'primary.500', <Flex
bg: 'myWhite.300' key={item.value}
} {...(isChecked
: {})} ? {
whiteSpace="pre-wrap" color: 'primary.600'
flexDirection="row" }
justifyContent="start" : {})}
p="2" {...MenuStyle}
_hover={{ >
bg: 'myGray.50' <Checkbox size="lg" isChecked={isChecked} onChange={change} />
}} <Flex px="4" flexDirection="column" onClick={change}>
> <Box>{t(item.name as any)}</Box>
<Checkbox <Box color={'myGray.500'} fontSize={'mini'}>
size="lg" {t(item.description as any)}
isChecked={checkPermission(valueState, item.value)} </Box>
onChange={change}
/>
<Flex px="4" flexDirection="column" onClick={change}>
<Box fontWeight="500">{item.name}</Box>
<Box fontWeight="400">{item.description}</Box>
</Flex>
</Flex> </Flex>
); </Flex>
})}*/} );
})}
{onDelete && ( {onDelete && (
<> <>
<MyDivider my={2} h={'2px'} borderColor={'myGray.200'} /> <MyDivider my={2} h={'2px'} borderColor={'myGray.200'} />
...@@ -235,7 +232,7 @@ function PermissionSelect({ ...@@ -235,7 +232,7 @@ function PermissionSelect({
{...MenuStyle} {...MenuStyle}
onClick={() => { onClick={() => {
onDelete(); onDelete();
setIsOpen(false); // setIsOpen(false);
}} }}
> >
<MyIcon name="delete" w="20px" color="red.600" /> <MyIcon name="delete" w="20px" color="red.600" />
...@@ -249,4 +246,4 @@ function PermissionSelect({ ...@@ -249,4 +246,4 @@ function PermissionSelect({
) : null; ) : null;
} }
export default React.memo(PermissionSelect); export default React.memo(RoleSelect);
...@@ -10,17 +10,17 @@ export type PermissionTagsProp = { ...@@ -10,17 +10,17 @@ export type PermissionTagsProp = {
permission?: PermissionValueType; permission?: PermissionValueType;
}; };
function PermissionTags({ permission }: PermissionTagsProp) { function RoleTags({ permission }: PermissionTagsProp) {
const { getPerLabelList } = useContextSelector(CollaboratorContext, (v) => v); const { getRoleLabelList } = useContextSelector(CollaboratorContext, (v) => v);
const { t } = useTranslation(); const { t } = useTranslation();
if (permission === undefined) return null; if (permission === undefined) return null;
const perTagList = getPerLabelList(permission); const roleTagList = getRoleLabelList(permission);
return ( return (
<Flex gap="2" alignItems="center"> <Flex gap="2" alignItems="center">
{perTagList.map((item) => ( {roleTagList.map((item) => (
<Tag <Tag
mixBlendMode={'multiply'} mixBlendMode={'multiply'}
key={item} key={item}
...@@ -37,4 +37,4 @@ function PermissionTags({ permission }: PermissionTagsProp) { ...@@ -37,4 +37,4 @@ function PermissionTags({ permission }: PermissionTagsProp) {
); );
} }
export default PermissionTags; export default RoleTags;
...@@ -3,11 +3,11 @@ import type { ...@@ -3,11 +3,11 @@ import type {
CollaboratorItemType, CollaboratorItemType,
UpdateClbPermissionProps UpdateClbPermissionProps
} from '@fastgpt/global/support/permission/collaborator'; } from '@fastgpt/global/support/permission/collaborator';
import { PermissionList } from '@fastgpt/global/support/permission/constant';
import { Permission } from '@fastgpt/global/support/permission/controller'; import { Permission } from '@fastgpt/global/support/permission/controller';
import type { import type {
PermissionListType, PermissionValueType,
PermissionValueType RoleListType,
RoleValueType
} from '@fastgpt/global/support/permission/type'; } from '@fastgpt/global/support/permission/type';
import { type ReactNode, useCallback } from 'react'; import { type ReactNode, useCallback } from 'react';
import { createContext } from 'use-context-selector'; import { createContext } from 'use-context-selector';
...@@ -19,6 +19,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore'; ...@@ -19,6 +19,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useConfirm } from '@fastgpt/web/hooks/useConfirm'; import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { CommonRoleList } from '@fastgpt/global/support/permission/constant';
const MemberModal = dynamic(() => import('./MemberModal')); const MemberModal = dynamic(() => import('./MemberModal'));
const ManageModal = dynamic(() => import('./ManageModal')); const ManageModal = dynamic(() => import('./ManageModal'));
...@@ -26,7 +27,7 @@ const ManageModal = dynamic(() => import('./ManageModal')); ...@@ -26,7 +27,7 @@ const ManageModal = dynamic(() => import('./ManageModal'));
export type MemberManagerInputPropsType = { export type MemberManagerInputPropsType = {
permission: Permission; permission: Permission;
onGetCollaboratorList: () => Promise<CollaboratorItemType[]>; onGetCollaboratorList: () => Promise<CollaboratorItemType[]>;
permissionList?: PermissionListType; roleList?: RoleListType;
onUpdateCollaborators: (props: UpdateClbPermissionProps) => Promise<any>; onUpdateCollaborators: (props: UpdateClbPermissionProps) => Promise<any>;
onDelOneCollaborator: ( onDelOneCollaborator: (
props: RequireOnlyOne<{ tmbId: string; groupId: string; orgId: string }> props: RequireOnlyOne<{ tmbId: string; groupId: string; orgId: string }>
...@@ -38,7 +39,7 @@ export type MemberManagerPropsType = MemberManagerInputPropsType & { ...@@ -38,7 +39,7 @@ export type MemberManagerPropsType = MemberManagerInputPropsType & {
collaboratorList: CollaboratorItemType[]; collaboratorList: CollaboratorItemType[];
refetchCollaboratorList: () => void; refetchCollaboratorList: () => void;
isFetchingCollaborator: boolean; isFetchingCollaborator: boolean;
getPerLabelList: (per: PermissionValueType) => string[]; getRoleLabelList: (role: RoleValueType) => string[];
}; };
export type ChildrenProps = { export type ChildrenProps = {
onOpenAddMember: () => void; onOpenAddMember: () => void;
...@@ -50,14 +51,14 @@ type CollaboratorContextType = MemberManagerPropsType & {}; ...@@ -50,14 +51,14 @@ type CollaboratorContextType = MemberManagerPropsType & {};
export const CollaboratorContext = createContext<CollaboratorContextType>({ export const CollaboratorContext = createContext<CollaboratorContextType>({
collaboratorList: [], collaboratorList: [],
permissionList: PermissionList, roleList: CommonRoleList,
onUpdateCollaborators: () => { onUpdateCollaborators: () => {
throw new Error('Function not implemented.'); throw new Error('Function not implemented.');
}, },
onDelOneCollaborator: () => { onDelOneCollaborator: () => {
throw new Error('Function not implemented.'); throw new Error('Function not implemented.');
}, },
getPerLabelList: (): string[] => { getRoleLabelList: (): string[] => {
throw new Error('Function not implemented.'); throw new Error('Function not implemented.');
}, },
refetchCollaboratorList: (): void => { refetchCollaboratorList: (): void => {
...@@ -73,7 +74,7 @@ export const CollaboratorContext = createContext<CollaboratorContextType>({ ...@@ -73,7 +74,7 @@ export const CollaboratorContext = createContext<CollaboratorContextType>({
const CollaboratorContextProvider = ({ const CollaboratorContextProvider = ({
permission, permission,
onGetCollaboratorList, onGetCollaboratorList,
permissionList, roleList,
onUpdateCollaborators, onUpdateCollaborators,
onDelOneCollaborator, onDelOneCollaborator,
children, children,
...@@ -115,7 +116,7 @@ const CollaboratorContextProvider = ({ ...@@ -115,7 +116,7 @@ const CollaboratorContextProvider = ({
return { return {
...item, ...item,
permission: new Permission({ permission: new Permission({
per: item.permission.value role: item.permission.role
}) })
}; };
}); });
...@@ -128,32 +129,32 @@ const CollaboratorContextProvider = ({ ...@@ -128,32 +129,32 @@ const CollaboratorContextProvider = ({
} }
); );
const getPerLabelList = useCallback( const getRoleLabelList = useCallback(
(per: PermissionValueType) => { (role: PermissionValueType) => {
if (!permissionList) return []; if (!roleList) return [];
const Per = new Permission({ per }); const Per = new Permission({ role });
const labels: string[] = []; const labels: string[] = [];
if (Per.hasManagePer) { if (Per.hasManagePer) {
labels.push(permissionList['manage'].name); labels.push(t(roleList['manage'].name as any));
} else if (Per.hasWritePer) { } else if (Per.hasWritePer) {
labels.push(permissionList['write'].name); labels.push(t(roleList['write'].name as any));
} else if (Per.hasReadPer) { } 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 (item.checkBoxType === 'multiple') {
if (Per.checkPer(item.value)) { if (Per.checkRole(item.value)) {
labels.push(item.name); labels.push(t(item.name as any));
} }
} }
}); });
return labels; return labels;
}, },
[permissionList] [roleList]
); );
const { ConfirmModal, openConfirm } = useConfirm({}); const { ConfirmModal, openConfirm } = useConfirm({});
...@@ -174,10 +175,10 @@ const CollaboratorContextProvider = ({ ...@@ -174,10 +175,10 @@ const CollaboratorContextProvider = ({
collaboratorList, collaboratorList,
refetchCollaboratorList, refetchCollaboratorList,
isFetchingCollaborator, isFetchingCollaborator,
permissionList, roleList,
onUpdateCollaborators: onUpdateCollaboratorsThen, onUpdateCollaborators: onUpdateCollaboratorsThen,
onDelOneCollaborator: onDelOneCollaboratorThen, onDelOneCollaborator: onDelOneCollaboratorThen,
getPerLabelList getRoleLabelList
}; };
const onOpenAddMemberModal = () => { const onOpenAddMemberModal = () => {
......
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { Permission } from '@fastgpt/global/support/permission/controller'; import { Permission } from '@fastgpt/global/support/permission/controller';
import { type PermissionListType } from '@fastgpt/global/support/permission/type'; import type { RoleListType } from '@fastgpt/global/support/permission/type';
import { PermissionList } from '@fastgpt/global/support/permission/constant';
import MyTag from '@fastgpt/web/components/common/Tag/index'; import MyTag from '@fastgpt/web/components/common/Tag/index';
import { HStack } from '@chakra-ui/react'; import { HStack } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { CommonRoleList } from '@fastgpt/global/support/permission/constant';
const PermissionTag = ({ const PermissionTag = ({
permission, permission,
permissionList roleList: roleList
}: { }: {
permission: Permission; permission: Permission;
permissionList: PermissionListType; roleList: RoleListType;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { commonLabel, otherLabels } = useMemo(() => { const { commonLabel, otherLabels } = useMemo(() => {
const Per = new Permission({ per: permission.value }); const Per = new Permission({ role: permission.role });
const commonLabel = (() => { const commonLabel = (() => {
if (permission.isOwner) return t('common:permission.Owner'); if (permission.isOwner) return t('common:permission.Owner');
if (permission.hasManagePer) return t(PermissionList['manage'].name as any); if (permission.hasManagePer) return t(CommonRoleList['manage'].name as any);
if (permission.hasWritePer) return t(PermissionList['write'].name as any); if (permission.hasWritePer) return t(CommonRoleList['write'].name as any);
if (permission.hasReadPer) return t(PermissionList['read'].name as any); if (permission.hasReadPer) return t(CommonRoleList['read'].name as any);
return; return;
})(); })();
const otherLabels: string[] = []; const otherLabels: string[] = [];
Object.values(permissionList).forEach((item) => { Object.values(roleList).forEach((item) => {
if (item.checkBoxType === 'multiple') { if (item.checkBoxType === 'multiple') {
if (Per.checkPer(item.value)) { if (Per.checkPer(item.value)) {
otherLabels.push(item.name); otherLabels.push(item.name);
...@@ -45,8 +45,8 @@ const PermissionTag = ({ ...@@ -45,8 +45,8 @@ const PermissionTag = ({
permission.hasReadPer, permission.hasReadPer,
permission.hasWritePer, permission.hasWritePer,
permission.isOwner, permission.isOwner,
permission.value, permission.role,
permissionList, roleList,
t t
]); ]);
return ( return (
......
import { AppPermission } from '@fastgpt/global/support/permission/app/controller'; import { AppPermission } from '@fastgpt/global/support/permission/app/controller';
export const processUpdateAppCollaboratorSpecific = (metadata: any) => { export const processUpdateAppCollaboratorSpecific = (metadata: any) => {
const permissionValue = parseInt(metadata.permission, 10); const role = parseInt(metadata.permission, 10);
const permission = new AppPermission({ per: permissionValue }); const permission = new AppPermission({ role });
return { return {
...metadata, ...metadata,
readPermission: permission.hasReadPer ? '✔' : '✘', readPermission: permission.hasReadPer ? '✔' : '✘',
......
import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller'; import { DatasetPermission } from '@fastgpt/global/support/permission/dataset/controller';
export const processUpdateDatasetCollaboratorSpecific = (metadata: any) => { export const processUpdateDatasetCollaboratorSpecific = (metadata: any) => {
const permissionValue = parseInt(metadata.permission, 10); const role = parseInt(metadata.permission, 10);
const permission = new DatasetPermission({ per: permissionValue }); const permission = new DatasetPermission({ role });
return { return {
...metadata, ...metadata,
readPermission: permission.hasReadPer ? '✔' : '✘', readPermission: permission.hasReadPer ? '✔' : '✘',
......
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller'; import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
// TODO: replace any
export const processAssignPermissionSpecific = (metadata: any) => { export const processAssignPermissionSpecific = (metadata: any) => {
const permissionValue = parseInt(metadata.permission, 10); // metadata.permission is a string, parseInt will convert it to number in decimal
const permission = new TeamPermission({ per: permissionValue }); const role = parseInt(metadata.permission, 10);
const permission = new TeamPermission({ role });
return { return {
...metadata, ...metadata,
......
...@@ -124,7 +124,7 @@ function GroupEditModal({ ...@@ -124,7 +124,7 @@ function GroupEditModal({
const handleToggleSelect = (memberId: string) => { const handleToggleSelect = (memberId: string) => {
if ( if (
myRole === 'owner' && myRole === 'owner' &&
memberId === groupMembers.find((item) => item.role === 'owner')?.tmbId memberId === groupMembers.find((item) => item.groupRole === 'owner')?.tmbId
) { ) {
toast({ toast({
title: t('user:team.group.toast.can_not_delete_owner'), title: t('user:team.group.toast.can_not_delete_owner'),
......
...@@ -28,11 +28,14 @@ import MemberTag from '../../../../components/support/user/team/Info/MemberTag'; ...@@ -28,11 +28,14 @@ import MemberTag from '../../../../components/support/user/team/Info/MemberTag';
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant'; import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
import { import {
TeamApikeyCreatePermissionVal, TeamApikeyCreatePermissionVal,
TeamApikeyCreateRoleVal,
TeamAppCreatePermissionVal, TeamAppCreatePermissionVal,
TeamAppCreateRoleVal,
TeamDatasetCreatePermissionVal, TeamDatasetCreatePermissionVal,
TeamDatasetCreateRoleVal,
TeamManagePermissionVal, TeamManagePermissionVal,
TeamPermissionList, TeamManageRoleVal,
TeamWritePermissionVal TeamRoleList
} from '@fastgpt/global/support/permission/user/constant'; } from '@fastgpt/global/support/permission/user/constant';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller'; import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { useToggle } from 'ahooks'; import { useToggle } from 'ahooks';
...@@ -117,18 +120,18 @@ function PermissionManage({ ...@@ -117,18 +120,18 @@ function PermissionManage({
if (!clb) return; if (!clb) return;
const permission = new TeamPermission({ per: clb.permission.value }); const permission = new TeamPermission({ role: clb.permission.role });
if (type === 'add') { if (type === 'add') {
permission.addPer(per); permission.addRole(per);
} else { } else {
permission.removePer(per); permission.removeRole(per);
} }
return onUpdateCollaborators({ return onUpdateCollaborators({
...(clb.tmbId && { members: [clb.tmbId] }), ...(clb.tmbId && { members: [clb.tmbId] }),
...(clb.groupId && { groups: [clb.groupId] }), ...(clb.groupId && { groups: [clb.groupId] }),
...(clb.orgId && { orgs: [clb.orgId] }), ...(clb.orgId && { orgs: [clb.orgId] }),
permission: permission.value permission: permission.role
}); });
} }
); );
...@@ -145,12 +148,12 @@ function PermissionManage({ ...@@ -145,12 +148,12 @@ function PermissionManage({
function PermissionCheckBox({ function PermissionCheckBox({
isDisabled, isDisabled,
per, role,
clbPer, clbPer,
id id
}: { }: {
isDisabled: boolean; isDisabled: boolean;
per: PermissionValueType; role: PermissionValueType;
clbPer: Permission; clbPer: Permission;
id: string; id: string;
}) { }) {
...@@ -159,18 +162,18 @@ function PermissionManage({ ...@@ -159,18 +162,18 @@ function PermissionManage({
<Box mx="auto" w="fit-content"> <Box mx="auto" w="fit-content">
<Checkbox <Checkbox
isDisabled={isDisabled} isDisabled={isDisabled}
isChecked={clbPer.checkPer(per)} isChecked={clbPer.checkRole(role)}
onChange={(e) => onChange={(e) =>
e.target.checked e.target.checked
? onUpdatePermission({ ? onUpdatePermission({
id, id,
type: 'add', type: 'add',
per per: role
}) })
: onUpdatePermission({ : onUpdatePermission({
id, id,
type: 'remove', type: 'remove',
per per: role
}) })
} }
/> />
...@@ -266,25 +269,25 @@ function PermissionManage({ ...@@ -266,25 +269,25 @@ function PermissionManage({
</Td> </Td>
<PermissionCheckBox <PermissionCheckBox
isDisabled={member.permission.isOwner || !userManage} isDisabled={member.permission.isOwner || !userManage}
per={TeamAppCreatePermissionVal} role={TeamAppCreateRoleVal}
clbPer={member.permission} clbPer={member.permission}
id={member.tmbId!} id={member.tmbId!}
/> />
<PermissionCheckBox <PermissionCheckBox
isDisabled={member.permission.isOwner || !userManage} isDisabled={member.permission.isOwner || !userManage}
per={TeamDatasetCreatePermissionVal} role={TeamDatasetCreateRoleVal}
clbPer={member.permission} clbPer={member.permission}
id={member.tmbId!} id={member.tmbId!}
/> />
<PermissionCheckBox <PermissionCheckBox
isDisabled={member.permission.isOwner || !userManage} isDisabled={member.permission.isOwner || !userManage}
per={TeamApikeyCreatePermissionVal} role={TeamApikeyCreateRoleVal}
clbPer={member.permission} clbPer={member.permission}
id={member.tmbId!} id={member.tmbId!}
/> />
<PermissionCheckBox <PermissionCheckBox
isDisabled={member.permission.isOwner || !userInfo?.permission.isOwner} isDisabled={member.permission.isOwner || !userInfo?.permission.isOwner}
per={TeamManagePermissionVal} role={TeamManageRoleVal}
clbPer={member.permission} clbPer={member.permission}
id={member.tmbId!} id={member.tmbId!}
/> />
...@@ -323,25 +326,25 @@ function PermissionManage({ ...@@ -323,25 +326,25 @@ function PermissionManage({
</Td> </Td>
<PermissionCheckBox <PermissionCheckBox
isDisabled={org.permission.isOwner || !userManage} isDisabled={org.permission.isOwner || !userManage}
per={TeamAppCreatePermissionVal} role={TeamAppCreatePermissionVal}
clbPer={org.permission} clbPer={org.permission}
id={org.orgId!} id={org.orgId!}
/> />
<PermissionCheckBox <PermissionCheckBox
isDisabled={org.permission.isOwner || !userManage} isDisabled={org.permission.isOwner || !userManage}
per={TeamDatasetCreatePermissionVal} role={TeamDatasetCreatePermissionVal}
clbPer={org.permission} clbPer={org.permission}
id={org.orgId!} id={org.orgId!}
/> />
<PermissionCheckBox <PermissionCheckBox
isDisabled={org.permission.isOwner || !userManage} isDisabled={org.permission.isOwner || !userManage}
per={TeamApikeyCreatePermissionVal} role={TeamApikeyCreatePermissionVal}
clbPer={org.permission} clbPer={org.permission}
id={org.orgId!} id={org.orgId!}
/> />
<PermissionCheckBox <PermissionCheckBox
isDisabled={org.permission.isOwner || !userInfo?.permission.isOwner} isDisabled={org.permission.isOwner || !userInfo?.permission.isOwner}
per={TeamManagePermissionVal} role={TeamManagePermissionVal}
clbPer={org.permission} clbPer={org.permission}
id={org.orgId!} id={org.orgId!}
/> />
...@@ -385,25 +388,25 @@ function PermissionManage({ ...@@ -385,25 +388,25 @@ function PermissionManage({
</Td> </Td>
<PermissionCheckBox <PermissionCheckBox
isDisabled={group.permission.isOwner || !userManage} isDisabled={group.permission.isOwner || !userManage}
per={TeamAppCreatePermissionVal} role={TeamAppCreatePermissionVal}
clbPer={group.permission} clbPer={group.permission}
id={group.groupId!} id={group.groupId!}
/> />
<PermissionCheckBox <PermissionCheckBox
isDisabled={group.permission.isOwner || !userManage} isDisabled={group.permission.isOwner || !userManage}
per={TeamDatasetCreatePermissionVal} role={TeamDatasetCreatePermissionVal}
clbPer={group.permission} clbPer={group.permission}
id={group.groupId!} id={group.groupId!}
/> />
<PermissionCheckBox <PermissionCheckBox
isDisabled={group.permission.isOwner || !userManage} isDisabled={group.permission.isOwner || !userManage}
per={TeamApikeyCreatePermissionVal} role={TeamApikeyCreatePermissionVal}
clbPer={group.permission} clbPer={group.permission}
id={group.groupId!} id={group.groupId!}
/> />
<PermissionCheckBox <PermissionCheckBox
isDisabled={group.permission.isOwner || !userInfo?.permission.isOwner} isDisabled={group.permission.isOwner || !userInfo?.permission.isOwner}
per={TeamManagePermissionVal} role={TeamManagePermissionVal}
clbPer={group.permission} clbPer={group.permission}
id={group.groupId!} id={group.groupId!}
/> />
...@@ -434,7 +437,7 @@ export const Render = ({ Tabs }: { Tabs: React.ReactNode }) => { ...@@ -434,7 +437,7 @@ export const Render = ({ Tabs }: { Tabs: React.ReactNode }) => {
return userInfo?.team ? ( return userInfo?.team ? (
<CollaboratorContextProvider <CollaboratorContextProvider
permission={userInfo?.team.permission} permission={userInfo?.team.permission}
permissionList={TeamPermissionList} roleList={TeamRoleList}
onGetCollaboratorList={getTeamClbs} onGetCollaboratorList={getTeamClbs}
onUpdateCollaborators={updateMemberPermission} onUpdateCollaborators={updateMemberPermission}
onDelOneCollaborator={deleteMemberPermission} onDelOneCollaborator={deleteMemberPermission}
......
...@@ -20,7 +20,7 @@ import { ...@@ -20,7 +20,7 @@ import {
} from '@chakra-ui/react'; } from '@chakra-ui/react';
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils'; import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
import type { AppSchema } from '@fastgpt/global/core/app/type.d'; 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 type { PermissionValueType } from '@fastgpt/global/support/permission/type';
import Avatar from '@fastgpt/web/components/common/Avatar'; import Avatar from '@fastgpt/web/components/common/Avatar';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
...@@ -187,7 +187,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => { ...@@ -187,7 +187,7 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
<CollaboratorContextProvider <CollaboratorContextProvider
permission={appDetail.permission} permission={appDetail.permission}
onGetCollaboratorList={() => getCollaboratorList(appDetail._id)} onGetCollaboratorList={() => getCollaboratorList(appDetail._id)}
permissionList={AppPermissionList} roleList={AppRoleList}
onUpdateCollaborators={async (props) => onUpdateCollaborators={async (props) =>
onUpdateCollaborators({ onUpdateCollaborators({
permission: props.permission, permission: props.permission,
......
...@@ -36,7 +36,9 @@ const RouteTab = () => { ...@@ -36,7 +36,9 @@ const RouteTab = () => {
label: t('app:publish_channel'), label: t('app:publish_channel'),
id: TabEnum.publish id: TabEnum.publish
}, },
{ label: t('app:chat_logs'), id: TabEnum.logs } ...(appDetail.permission.hasReadChatLogPer
? [{ label: t('app:chat_logs'), id: TabEnum.logs }]
: [])
] ]
: []) : [])
], ],
......
...@@ -17,7 +17,7 @@ import { useFolderDrag } from '@/components/common/folder/useFolderDrag'; ...@@ -17,7 +17,7 @@ import { useFolderDrag } from '@/components/common/folder/useFolderDrag';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import type { EditResourceInfoFormType } from '@/components/common/Modal/EditResourceModal'; import type { EditResourceInfoFormType } from '@/components/common/Modal/EditResourceModal';
import MyMenu, { type MenuItemType } from '@fastgpt/web/components/common/MyMenu'; 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 { import {
deleteAppCollaborators, deleteAppCollaborators,
getCollaboratorList, getCollaboratorList,
...@@ -427,7 +427,7 @@ const ListItem = () => { ...@@ -427,7 +427,7 @@ const ListItem = () => {
managePer={{ managePer={{
permission: editPerApp.permission, permission: editPerApp.permission,
onGetCollaboratorList: () => getCollaboratorList(editPerApp._id), onGetCollaboratorList: () => getCollaboratorList(editPerApp._id),
permissionList: AppPermissionList, roleList: AppRoleList,
onUpdateCollaborators: (props: { onUpdateCollaborators: (props: {
members?: string[]; members?: string[];
groups?: string[]; groups?: string[];
......
...@@ -17,7 +17,7 @@ import { DatasetTypeEnum, DatasetTypeMap } from '@fastgpt/global/core/dataset/co ...@@ -17,7 +17,7 @@ import { DatasetTypeEnum, DatasetTypeMap } from '@fastgpt/global/core/dataset/co
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip'; import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel'; import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
import MyIcon from '@fastgpt/web/components/common/Icon'; 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 MemberManager from '../../MemberManager';
import { import {
getCollaboratorList, getCollaboratorList,
...@@ -382,7 +382,7 @@ const Info = ({ datasetId }: { datasetId: string }) => { ...@@ -382,7 +382,7 @@ const Info = ({ datasetId }: { datasetId: string }) => {
managePer={{ managePer={{
permission: datasetDetail.permission, permission: datasetDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(datasetId), onGetCollaboratorList: () => getCollaboratorList(datasetId),
permissionList: DatasetPermissionList, roleList: DatasetRoleList,
onUpdateCollaborators: (body) => onUpdateCollaborators: (body) =>
postUpdateDatasetCollaborators({ postUpdateDatasetCollaborators({
...body, ...body,
......
...@@ -17,7 +17,7 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; ...@@ -17,7 +17,7 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { DatasetsContext } from '../../../pages/dataset/list/context'; 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 ConfigPerModal from '@/components/support/permission/ConfigPerModal';
import { import {
deleteDatasetCollaborators, deleteDatasetCollaborators,
...@@ -436,7 +436,7 @@ function List() { ...@@ -436,7 +436,7 @@ function List() {
managePer={{ managePer={{
permission: editPerDataset.permission, permission: editPerDataset.permission,
onGetCollaboratorList: () => getCollaboratorList(editPerDataset._id), onGetCollaboratorList: () => getCollaboratorList(editPerDataset._id),
permissionList: DatasetPermissionList, roleList: DatasetRoleList,
onUpdateCollaborators: (props) => onUpdateCollaborators: (props) =>
postUpdateDatasetCollaborators({ postUpdateDatasetCollaborators({
...props, ...props,
......
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);
...@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next'; ...@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response'; import { jsonRes } from '@fastgpt/service/common/response';
import { authCert } from '@fastgpt/service/support/permission/auth/common'; import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema'; 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 中,并做映射 */ /* pg 中的数据搬到 mongo dataset.datas 中,并做映射 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
...@@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ...@@ -25,7 +25,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}, },
{ {
$set: { $set: {
defaultPermission: DatasetDefaultPermissionVal defaultPermission: DataSetDefaultRoleVal
} }
} }
); );
......
...@@ -22,15 +22,15 @@ async function handler(req: NextApiRequest, _res: NextApiResponse) { ...@@ -22,15 +22,15 @@ async function handler(req: NextApiRequest, _res: NextApiResponse) {
}); });
for await (const rp of rps) { for await (const rp of rps) {
const per = new TeamPermission({ per: rp.permission }); const per = rp.permission;
console.log(per.hasWritePer, per.value); console.log(per);
if (per.hasWritePer) { if (per & 0b010) {
const newPer = per.addPer( // has 0b010
TeamAppCreatePermissionVal, rp.permission =
TeamDatasetCreatePermissionVal, per |
TeamApikeyCreatePermissionVal TeamAppCreatePermissionVal |
); TeamDatasetCreatePermissionVal |
rp.permission = newPer.value; TeamApikeyCreatePermissionVal;
try { try {
await retryFn(async () => { await retryFn(async () => {
......
import type { NextApiResponse } from 'next'; import type { NextApiResponse } from 'next';
import { responseWriteController } from '@fastgpt/service/common/response'; import { responseWriteController } from '@fastgpt/service/common/response';
import { addDays } from 'date-fns'; import { addDays } from 'date-fns';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { readFromSecondary } from '@fastgpt/service/common/mongo/utils'; import { readFromSecondary } from '@fastgpt/service/common/mongo/utils';
import { addLog } from '@fastgpt/service/common/system/log'; import { addLog } from '@fastgpt/service/common/system/log';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
...@@ -19,6 +18,7 @@ import type { ChatSourceEnum } from '@fastgpt/global/core/chat/constants'; ...@@ -19,6 +18,7 @@ import type { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants'; import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants';
import { type AIChatItemValueItemType } from '@fastgpt/global/core/chat/type'; import { type AIChatItemValueItemType } from '@fastgpt/global/core/chat/type';
import { sanitizeCsvField } from '@fastgpt/service/common/file/csv'; import { sanitizeCsvField } from '@fastgpt/service/common/file/csv';
import { AppReadChatLogPerVal } from '@fastgpt/global/support/permission/app/constant';
const formatJsonString = (data: any) => { const formatJsonString = (data: any) => {
if (data == null) return ''; if (data == null) return '';
...@@ -47,7 +47,7 @@ async function handler(req: ApiRequestProps<ExportChatLogsBody, {}>, res: NextAp ...@@ -47,7 +47,7 @@ async function handler(req: ApiRequestProps<ExportChatLogsBody, {}>, res: NextAp
throw new Error('缺少参数'); 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([ const teamMemberWithContact = await MongoTeamMember.aggregate([
{ $match: { teamId: new Types.ObjectId(teamId) } }, { $match: { teamId: new Types.ObjectId(teamId) } },
......
...@@ -7,7 +7,6 @@ import type { GetAppChatLogsParams } from '@/global/core/api/appReq.d'; ...@@ -7,7 +7,6 @@ import type { GetAppChatLogsParams } from '@/global/core/api/appReq.d';
import { authApp } from '@fastgpt/service/support/permission/app/auth'; import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { ChatItemCollectionName } from '@fastgpt/service/core/chat/chatItemSchema'; import { ChatItemCollectionName } from '@fastgpt/service/core/chat/chatItemSchema';
import { NextAPI } from '@/service/middleware/entry'; import { NextAPI } from '@/service/middleware/entry';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { readFromSecondary } from '@fastgpt/service/common/mongo/utils'; import { readFromSecondary } from '@fastgpt/service/common/mongo/utils';
import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination';
import { type PaginationResponse } from '@fastgpt/web/common/fetch/type'; import { type PaginationResponse } from '@fastgpt/web/common/fetch/type';
...@@ -16,6 +15,7 @@ import { addAuditLog } from '@fastgpt/service/support/user/audit/util'; ...@@ -16,6 +15,7 @@ import { addAuditLog } from '@fastgpt/service/support/user/audit/util';
import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants'; import { AuditEventEnum } from '@fastgpt/global/support/user/audit/constants';
import { getI18nAppType } from '@fastgpt/service/support/user/audit/util'; import { getI18nAppType } from '@fastgpt/service/support/user/audit/util';
import { replaceRegChars } from '@fastgpt/global/common/string/tools'; import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { AppReadChatLogPerVal } from '@fastgpt/global/support/permission/app/constant';
async function handler( async function handler(
req: NextApiRequest, req: NextApiRequest,
...@@ -41,7 +41,7 @@ async function handler( ...@@ -41,7 +41,7 @@ async function handler(
req, req,
authToken: true, authToken: true,
appId, appId,
per: WritePermissionVal per: AppReadChatLogPerVal
}); });
const where = { const where = {
......
...@@ -11,15 +11,15 @@ import { type ApiRequestProps } from '@fastgpt/service/type/next'; ...@@ -11,15 +11,15 @@ import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type'; import { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils'; import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
import { AppFolderTypeList, AppTypeEnum } from '@fastgpt/global/core/app/constants'; 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 { authApp } from '@fastgpt/service/support/permission/app/auth';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth'; import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { replaceRegChars } from '@fastgpt/global/common/string/tools'; 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 { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers';
import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers'; import { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
import { addSourceMember } from '@fastgpt/service/support/user/utils'; import { addSourceMember } from '@fastgpt/service/support/user/utils';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { sumPer } from '@fastgpt/global/support/permission/utils';
export type ListAppBody = { export type ListAppBody = {
parentId?: ParentIdType; parentId?: ParentIdType;
...@@ -63,7 +63,7 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy ...@@ -63,7 +63,7 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
]); ]);
// Get team all app permissions // Get team all app permissions
const [perList, myGroupMap, myOrgSet] = await Promise.all([ const [roleList, myGroupMap, myOrgSet] = await Promise.all([
MongoResourcePermission.find({ MongoResourcePermission.find({
resourceType: PerResourceTypeEnum.app, resourceType: PerResourceTypeEnum.app,
teamId, teamId,
...@@ -87,7 +87,7 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy ...@@ -87,7 +87,7 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
}) })
]); ]);
// Get my permissions // Get my permissions
const myPerList = perList.filter( const myPerList = roleList.filter(
(item) => (item) =>
String(item.tmbId) === String(tmbId) || String(item.tmbId) === String(tmbId) ||
myGroupMap.has(String(item.groupId)) || myGroupMap.has(String(item.groupId)) ||
...@@ -160,11 +160,11 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy ...@@ -160,11 +160,11 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
.map((app) => { .map((app) => {
const { Per, privateApp } = (() => { const { Per, privateApp } = (() => {
const getPer = (appId: string) => { const getPer = (appId: string) => {
const tmbPer = myPerList.find( const tmbRole = myPerList.find(
(item) => String(item.resourceId) === appId && !!item.tmbId (item) => String(item.resourceId) === appId && !!item.tmbId
)?.permission; )?.permission;
const groupPer = concatPer( const groupRole = sumPer(
myPerList ...myPerList
.filter( .filter(
(item) => String(item.resourceId) === appId && (!!item.groupId || !!item.orgId) (item) => String(item.resourceId) === appId && (!!item.groupId || !!item.orgId)
) )
...@@ -172,13 +172,13 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy ...@@ -172,13 +172,13 @@ async function handler(req: ApiRequestProps<ListAppBody>): Promise<AppListItemTy
); );
return new AppPermission({ return new AppPermission({
per: tmbPer ?? groupPer ?? AppDefaultPermissionVal, role: tmbRole ?? groupRole,
isOwner: String(app.tmbId) === String(tmbId) || teamPer.isOwner isOwner: String(app.tmbId) === String(tmbId) || teamPer.isOwner
}); });
}; };
const getClbCount = (appId: string) => { 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 // Inherit app, check parent folder clb
......
...@@ -8,17 +8,17 @@ import { ...@@ -8,17 +8,17 @@ import {
ReadPermissionVal ReadPermissionVal
} from '@fastgpt/global/support/permission/constant'; } from '@fastgpt/global/support/permission/constant';
import { MongoResourcePermission } from '@fastgpt/service/support/permission/schema'; 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 { type ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils'; import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
import { type ApiRequestProps } from '@fastgpt/service/type/next'; import { type ApiRequestProps } from '@fastgpt/service/type/next';
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth'; import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
import { replaceRegChars } from '@fastgpt/global/common/string/tools'; import { replaceRegChars } from '@fastgpt/global/common/string/tools';
import { getGroupsByTmbId } from '@fastgpt/service/support/permission/memberGroup/controllers'; 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 { getOrgIdSetWithParentByTmbId } from '@fastgpt/service/support/permission/org/controllers';
import { addSourceMember } from '@fastgpt/service/support/user/utils'; import { addSourceMember } from '@fastgpt/service/support/user/utils';
import { getEmbeddingModel } from '@fastgpt/service/core/ai/model'; import { getEmbeddingModel } from '@fastgpt/service/core/ai/model';
import { sumPer } from '@fastgpt/global/support/permission/utils';
export type GetDatasetListBody = { export type GetDatasetListBody = {
parentId: ParentIdType; parentId: ParentIdType;
...@@ -51,7 +51,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) { ...@@ -51,7 +51,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
]); ]);
// Get team all app permissions // Get team all app permissions
const [perList, myGroupMap, myOrgSet] = await Promise.all([ const [roleList, myGroupMap, myOrgSet] = await Promise.all([
MongoResourcePermission.find({ MongoResourcePermission.find({
resourceType: PerResourceTypeEnum.dataset, resourceType: PerResourceTypeEnum.dataset,
teamId, teamId,
...@@ -74,7 +74,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) { ...@@ -74,7 +74,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
tmbId tmbId
}) })
]); ]);
const myPerList = perList.filter( const myRoles = roleList.filter(
(item) => (item) =>
String(item.tmbId) === String(tmbId) || String(item.tmbId) === String(tmbId) ||
myGroupMap.has(String(item.groupId)) || myGroupMap.has(String(item.groupId)) ||
...@@ -83,7 +83,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) { ...@@ -83,7 +83,7 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
const findDatasetQuery = (() => { const findDatasetQuery = (() => {
// Filter apps by permission, if not owner, only get apps that I have permission to access // 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 const datasetPerQuery = teamPer.isOwner
? {} ? {}
: parentId : parentId
...@@ -127,23 +127,23 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) { ...@@ -127,23 +127,23 @@ async function handler(req: ApiRequestProps<GetDatasetListBody>) {
.map((dataset) => { .map((dataset) => {
const { Per, privateDataset } = (() => { const { Per, privateDataset } = (() => {
const getPer = (datasetId: string) => { const getPer = (datasetId: string) => {
const tmbPer = myPerList.find( const tmbRole = myRoles.find(
(item) => String(item.resourceId) === datasetId && !!item.tmbId (item) => String(item.resourceId) === datasetId && !!item.tmbId
)?.permission; )?.permission;
const groupPer = concatPer( const groupRole = sumPer(
myPerList ...myRoles
.filter( .filter(
(item) => String(item.resourceId) === datasetId && (!!item.groupId || !!item.orgId) (item) => String(item.resourceId) === datasetId && (!!item.groupId || !!item.orgId)
) )
.map((item) => item.permission) .map((item) => item.permission)
); );
return new DatasetPermission({ return new DatasetPermission({
per: tmbPer ?? groupPer ?? DatasetDefaultPermissionVal, role: tmbRole ?? groupRole,
isOwner: String(dataset.tmbId) === String(tmbId) || teamPer.isOwner isOwner: String(dataset.tmbId) === String(tmbId) || teamPer.isOwner
}); });
}; };
const getClbCount = (datasetId: string) => { 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 // inherit
......
...@@ -17,7 +17,7 @@ import FolderPath from '@/components/common/folder/Path'; ...@@ -17,7 +17,7 @@ import FolderPath from '@/components/common/folder/Path';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import FolderSlideCard from '@/components/common/folder/SlideCard'; import FolderSlideCard from '@/components/common/folder/SlideCard';
import { delAppById, resumeInheritPer } from '@/web/core/app/api'; 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 { import {
deleteAppCollaborators, deleteAppCollaborators,
getCollaboratorList, getCollaboratorList,
...@@ -282,7 +282,7 @@ const MyApps = ({ MenuIcon }: { MenuIcon: JSX.Element }) => { ...@@ -282,7 +282,7 @@ const MyApps = ({ MenuIcon }: { MenuIcon: JSX.Element }) => {
managePer={{ managePer={{
permission: folderDetail.permission, permission: folderDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(folderDetail._id), onGetCollaboratorList: () => getCollaboratorList(folderDetail._id),
permissionList: AppPermissionList, roleList: AppRoleList,
onUpdateCollaborators: (props) => onUpdateCollaborators: (props) =>
postUpdateAppCollaborators({ postUpdateAppCollaborators({
...props, ...props,
......
...@@ -18,7 +18,7 @@ import { type EditFolderFormType } from '@fastgpt/web/components/common/MyModal/ ...@@ -18,7 +18,7 @@ import { type EditFolderFormType } from '@fastgpt/web/components/common/MyModal/
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { postCreateDatasetFolder, resumeInheritPer } from '@/web/core/dataset/api'; import { postCreateDatasetFolder, resumeInheritPer } from '@/web/core/dataset/api';
import FolderSlideCard from '@/components/common/folder/SlideCard'; 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 { import {
postUpdateDatasetCollaborators, postUpdateDatasetCollaborators,
deleteDatasetCollaborators, deleteDatasetCollaborators,
...@@ -256,7 +256,7 @@ const Dataset = () => { ...@@ -256,7 +256,7 @@ const Dataset = () => {
managePer={{ managePer={{
permission: folderDetail.permission, permission: folderDetail.permission,
onGetCollaboratorList: () => getCollaboratorList(folderDetail._id), onGetCollaboratorList: () => getCollaboratorList(folderDetail._id),
permissionList: DatasetPermissionList, roleList: DatasetRoleList,
onUpdateCollaborators: (params) => onUpdateCollaborators: (params) =>
postUpdateDatasetCollaborators({ postUpdateDatasetCollaborators({
...params, ...params,
......
import { CommonPerList, CommonRoleList } from '@fastgpt/global/support/permission/constant';
import { Permission } from '@fastgpt/global/support/permission/controller';
import { sumPer } from '@fastgpt/global/support/permission/utils';
import { describe, expect, it } from 'vitest';
describe('Permission Helper Class Test', () => {
it('Permission Helper Class Test', () => {
const permission = new Permission();
expect(permission.role).toBe(0);
permission.addRole(CommonRoleList.manage.value);
expect(permission.role).toBe(CommonRoleList.manage.value);
expect(permission.checkPer(CommonPerList.manage)).toBe(true);
permission.removeRole(CommonRoleList.read.value);
expect(permission.checkPer(CommonPerList.manage)).toBe(true);
});
});
describe('Tool Functions', () => {
it('sumPer', () => {
expect(sumPer(0b001, 0b010)).toBe(0b011);
expect(sumPer(0b000, 0b000)).toBe(0b000);
expect(sumPer(0b100, 0b001)).toBe(0b101);
expect(sumPer(0b111, 0b010)).toBe(0b111);
expect(sumPer(sumPer(0b001, 0b010), 0b100)).toBe(0b111);
expect(sumPer(0b10000000, 0b01000000)).toBe(0b11000000);
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment