diff --git a/packages/global/support/permission/app/constant.ts b/packages/global/support/permission/app/constant.ts index 0db5149f8..4a3844ad7 100644 --- a/packages/global/support/permission/app/constant.ts +++ b/packages/global/support/permission/app/constant.ts @@ -1,8 +1,10 @@ import { NullPermission, PermissionKeyEnum, PermissionList } from '../constant'; import { type PermissionListType } from '../type'; import { i18nT } from '../../../../web/i18n/utils'; -export enum AppPermissionKeyEnum {} -export const AppPermissionList: PermissionListType = { +export enum AppPermissionKeyEnum { + log = 'log' +} +export const AppPermissionList: PermissionListType = { [PermissionKeyEnum.read]: { ...PermissionList[PermissionKeyEnum.read], description: i18nT('app:permission.des.read') @@ -13,8 +15,16 @@ export const AppPermissionList: PermissionListType = { }, [PermissionKeyEnum.manage]: { ...PermissionList[PermissionKeyEnum.manage], + value: 0b1111, description: i18nT('app:permission.des.manage') + }, + [AppPermissionKeyEnum.log]: { + name: i18nT('app:permission.name.log'), + value: 0b1000, + checkBoxType: 'multiple', + description: i18nT('app:permission.des.log') } }; export const AppDefaultPermissionVal = NullPermission; +export const AppLogPermissionVal = AppPermissionList[AppPermissionKeyEnum.log].value; diff --git a/packages/global/support/permission/app/controller.ts b/packages/global/support/permission/app/controller.ts index 2764e7cae..1b471a643 100644 --- a/packages/global/support/permission/app/controller.ts +++ b/packages/global/support/permission/app/controller.ts @@ -1,7 +1,8 @@ import { type PerConstructPros, Permission } from '../controller'; -import { AppDefaultPermissionVal } from './constant'; +import { AppDefaultPermissionVal, AppPermissionList } from './constant'; export class AppPermission extends Permission { + hasLogPer: boolean = false; constructor(props?: PerConstructPros) { if (!props) { props = { @@ -11,5 +12,11 @@ export class AppPermission extends Permission { props.per = AppDefaultPermissionVal; } super(props); + this.setUpdatePermissionCallback(() => { + this.hasReadPer = this.checkPer(AppPermissionList.read.value); + this.hasWritePer = this.checkPer(AppPermissionList.write.value); + this.hasManagePer = this.checkPer(AppPermissionList.manage.value); + this.hasLogPer = this.checkPer(AppPermissionList.log.value); + }); } } diff --git a/packages/web/i18n/en/app.json b/packages/web/i18n/en/app.json index 7a7ecf51a..579b9c8a3 100644 --- a/packages/web/i18n/en/app.json +++ b/packages/web/i18n/en/app.json @@ -123,6 +123,8 @@ "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.write": "Can view and edit apps", + "permission.des.log": "Can view conversation logs", + "permission.name.log": "View logs", "plugin.Instructions": "Instructions", "plugin_cost_by_token": "Charged based on token usage", "plugin_cost_per_times": "{{cost}} points/time", diff --git a/packages/web/i18n/zh-CN/app.json b/packages/web/i18n/zh-CN/app.json index c88774b4c..a50e7246f 100644 --- a/packages/web/i18n/zh-CN/app.json +++ b/packages/web/i18n/zh-CN/app.json @@ -123,6 +123,8 @@ "permission.des.manage": "写权限基础上,可配置发布渠道、查看对话日志、分配该应用权限", "permission.des.read": "可使用该应用进行对话", "permission.des.write": "可查看和编辑应用", + "permission.des.log": "可查看对话日志", + "permission.name.log": "查看日志", "plugin.Instructions": "使用说明", "plugin_cost_by_token": "依据 token 消耗计费", "plugin_cost_per_times": "{{cost}} 积分/次", diff --git a/packages/web/i18n/zh-Hant/app.json b/packages/web/i18n/zh-Hant/app.json index cdc57c698..8b0cc3ffe 100644 --- a/packages/web/i18n/zh-Hant/app.json +++ b/packages/web/i18n/zh-Hant/app.json @@ -123,6 +123,8 @@ "permission.des.manage": "在寫入權限基礎上,可以設定發布通道、檢視對話紀錄、分配這個應用程式的權限", "permission.des.read": "可以使用這個應用程式進行對話", "permission.des.write": "可以檢視和編輯應用程式", + "permission.des.log": "可查看對話日誌", + "permission.name.log": "查看日誌", "plugin.Instructions": "使用說明", "plugin_cost_by_token": "根據 token 消耗計費", "plugin_cost_per_times": "{{cost}} 積分/次", diff --git a/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx b/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx index ed7a1cbe6..ae38e07b4 100644 --- a/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx +++ b/projects/app/src/components/support/permission/MemberManager/MemberModal.tsx @@ -46,16 +46,22 @@ function MemberModal({ const collaboratorList = useContextSelector(CollaboratorContext, (v) => v.collaboratorList); const [filterClass, setFilterClass] = useState<'member' | 'org' | 'group'>(); const { - paths, + paths: orgPaths, onClickOrg, members: orgMembers, MemberScrollData: OrgMemberScrollData, - onPathClick, + onPathClick: onOrgPathClick, orgs, searchKey, setSearchKey } = useOrg({ withPermission: false }); + const onExpandOrg = (org: OrgListItemType) => { + setFilterClass('org'); + setSearchKey(''); + onClickOrg(org); + }; + const { data: members, ScrollData: TeamMemberScrollData, @@ -104,8 +110,8 @@ function MemberModal({ permissionList?.read?.value ); const perLabel = useMemo(() => { - if (selectedPermission === undefined) return ''; - return getPerLabelList(selectedPermission!).join('、'); + if (selectedPermission === undefined) return []; + return getPerLabelList(selectedPermission!); }, [getPerLabelList, selectedPermission]); const onUpdateCollaborators = useContextSelector( @@ -194,6 +200,7 @@ function MemberModal({ placeholder={t('user:search_group_org_user')} bgColor="myGray.50" onChange={(e) => setSearchKey(e.target.value)} + value={searchKey} /> @@ -238,21 +245,21 @@ function MemberModal({ ? t('user:team.org.org') : t('user:team.group.group') }, - ...paths + ...orgPaths ]} onClick={(parentId) => { if (parentId === '') { setFilterClass(undefined); - onPathClick(''); + onOrgPathClick(''); } else if ( parentId === 'member' || parentId === 'org' || parentId === 'group' ) { setFilterClass(parentId); - onPathClick(''); + onOrgPathClick(''); } else { - onPathClick(parentId); + onOrgPathClick(parentId); } }} rootName={t('common:Team')} @@ -329,8 +336,8 @@ function MemberModal({ bgColor: 'myGray.200' }} onClick={(e) => { - onClickOrg(org); - // setPath(getOrgChildrenPath(org)); + // onClickOrg(org); + onExpandOrg(org); e.stopPropagation(); }} /> @@ -438,7 +445,7 @@ function MemberModal({ borderRadius={'md'} h={'32px'} > - {t(perLabel as any)} + {perLabel.map((item) => t(item as any)).join('、')} } diff --git a/projects/app/src/components/support/permission/MemberManager/PermissionSelect.tsx b/projects/app/src/components/support/permission/MemberManager/PermissionSelect.tsx index 966301d10..674394d4a 100644 --- a/projects/app/src/components/support/permission/MemberManager/PermissionSelect.tsx +++ b/projects/app/src/components/support/permission/MemberManager/PermissionSelect.tsx @@ -7,7 +7,8 @@ import { Radio, useOutsideClick, HStack, - MenuButton + MenuButton, + Checkbox } from '@chakra-ui/react'; import React, { useMemo, useRef, useState } from 'react'; import MyIcon from '@fastgpt/web/components/common/Icon'; @@ -89,19 +90,21 @@ function PermissionSelect({ return permissionList['read'].value; }, [permissionList, value]); - // const selectedMultipleValues = useMemo(() => { - // const per = new Permission({ per: value }); - // - // return permissionSelectList.multipleCheckBoxList - // .filter((item) => { - // return per.checkPer(item.value); - // }) - // .map((item) => item.value); - // }, [permissionSelectList.multipleCheckBoxList, value]); + const selectedMultipleValues = useMemo(() => { + const per = new Permission({ per: value }); - const onSelectPer = (per: PermissionValueType) => { - if (per === value) return; - onChange(per); + return permissionSelectList.multipleCheckBoxList + .filter((item) => { + return per.checkPer(item.value); + }) + .map((item) => item.value); + }, [permissionSelectList.multipleCheckBoxList, value]); + + const onSelectPer = (perValue: PermissionValueType) => { + if (perValue === value) return; + const per = new Permission({ per: perValue }); + per.addPer(...selectedMultipleValues); + onChange(per.value); setIsOpen(false); }; @@ -184,50 +187,61 @@ function PermissionSelect({ ); })} - {/* + - {multipleValues.length > 0 && 其他权限(多选)} */} + {/* {permissionSelectList.multipleCheckBoxList.length > 0 && ( + 其他权限(多选) + )} */} - {/* The list of multiple select permissions */} - {/* {list - .filter((item) => item.type === 'multiple') - .map((item) => { - const change = () => { - if (checkPermission(valueState, item.value)) { - setValueState(new Permission(valueState).remove(item.value).value); - } else { - setValueState(new Permission(valueState).add(item.value).value); - } - }; - return ( - - - - {item.name} - {item.description} - + {permissionSelectList.multipleCheckBoxList.map((item) => { + const isChecked = selectedMultipleValues.includes(item.value); + const isDisabled = new Permission({ per: selectedSingleValue }).checkPer(item.value); + const change = () => { + if (isDisabled) return; + const per = new Permission({ per: value }); + if (isChecked) { + per.removePer(item.value); + } else { + per.addPer(item.value); + } + onChange(per.value); + }; + return ( + + + + {t(item.name as any)} + {t(item.description as any)} - ); - })}*/} + + ); + })} {onDelete && ( <> diff --git a/projects/app/src/pageComponents/app/detail/RouteTab.tsx b/projects/app/src/pageComponents/app/detail/RouteTab.tsx index ea90075be..2d5bcb468 100644 --- a/projects/app/src/pageComponents/app/detail/RouteTab.tsx +++ b/projects/app/src/pageComponents/app/detail/RouteTab.tsx @@ -35,10 +35,10 @@ const RouteTab = () => { { label: t('app:publish_channel'), id: TabEnum.publish - }, - { label: t('app:chat_logs'), id: TabEnum.logs } + } ] - : []) + : []), + ...(appDetail.permission.hasLogPer ? [{ label: t('app:chat_logs'), id: TabEnum.logs }] : []) ], [appDetail.permission.hasManagePer, appDetail.type] ); diff --git a/projects/app/src/pages/api/core/app/exportChatLogs.ts b/projects/app/src/pages/api/core/app/exportChatLogs.ts index a19f81cf8..5c8a086f7 100644 --- a/projects/app/src/pages/api/core/app/exportChatLogs.ts +++ b/projects/app/src/pages/api/core/app/exportChatLogs.ts @@ -18,6 +18,7 @@ import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSc import type { ChatSourceEnum } from '@fastgpt/global/core/chat/constants'; import { ChatItemValueTypeEnum } from '@fastgpt/global/core/chat/constants'; import { type AIChatItemValueItemType } from '@fastgpt/global/core/chat/type'; +import { AppLogPermissionVal } from '@fastgpt/global/support/permission/app/constant'; const formatJsonString = (data: any) => { return JSON.stringify(data).replace(/"/g, '""').replace(/\n/g, '\\n'); @@ -44,7 +45,7 @@ async function handler(req: ApiRequestProps, res: NextAp throw new Error('缺少参数'); } - const { teamId } = await authApp({ req, authToken: true, appId, per: WritePermissionVal }); + const { teamId } = await authApp({ req, authToken: true, appId, per: AppLogPermissionVal }); const teamMemberWithContact = await MongoTeamMember.aggregate([ { $match: { teamId: new Types.ObjectId(teamId) } }, diff --git a/projects/app/src/pages/api/core/app/getChatLogs.ts b/projects/app/src/pages/api/core/app/getChatLogs.ts index 7cd2e9547..7ba7cc618 100644 --- a/projects/app/src/pages/api/core/app/getChatLogs.ts +++ b/projects/app/src/pages/api/core/app/getChatLogs.ts @@ -13,6 +13,7 @@ import { parsePaginationRequest } from '@fastgpt/service/common/api/pagination'; import { type PaginationResponse } from '@fastgpt/web/common/fetch/type'; import { addSourceMember } from '@fastgpt/service/support/user/utils'; import { replaceRegChars } from '@fastgpt/global/common/string/tools'; +import { AppLogPermissionVal } from '@fastgpt/global/support/permission/app/constant'; async function handler( req: NextApiRequest, @@ -33,7 +34,7 @@ async function handler( } // 凭证校验 - const { teamId } = await authApp({ req, authToken: true, appId, per: WritePermissionVal }); + const { teamId } = await authApp({ req, authToken: true, appId, per: AppLogPermissionVal }); const where = { teamId: new Types.ObjectId(teamId),