From 55c327ffcbb83fbe593dcd672d26113d524ebefe Mon Sep 17 00:00:00 2001 From: -LAN- Date: Fri, 27 Dec 2024 16:04:50 +0800 Subject: [PATCH 01/29] fix: handle case where member is not found in role update API (#12156) Signed-off-by: -LAN- --- api/controllers/console/workspace/members.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/controllers/console/workspace/members.py b/api/controllers/console/workspace/members.py index 1afb41ea87..a2b41c1d38 100644 --- a/api/controllers/console/workspace/members.py +++ b/api/controllers/console/workspace/members.py @@ -122,7 +122,7 @@ class MemberUpdateRoleApi(Resource): return {"code": "invalid-role", "message": "Invalid role"}, 400 member = db.session.get(Account, str(member_id)) - if member: + if not member: abort(404) try: From 5a3fe61f2af89f73b65157e22513e3bcec2941fa Mon Sep 17 00:00:00 2001 From: Jyong <76649700+JohnJyong@users.noreply.github.com> Date: Fri, 27 Dec 2024 17:08:44 +0800 Subject: [PATCH 02/29] disable all chunks status when disable document (#12149) --- api/tasks/add_document_to_index_task.py | 16 +++++++++++++++- api/tasks/remove_document_from_index_task.py | 11 +++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/api/tasks/add_document_to_index_task.py b/api/tasks/add_document_to_index_task.py index 9a172b2d9d..bd7fcdadea 100644 --- a/api/tasks/add_document_to_index_task.py +++ b/api/tasks/add_document_to_index_task.py @@ -38,7 +38,11 @@ def add_document_to_index_task(dataset_document_id: str): try: segments = ( db.session.query(DocumentSegment) - .filter(DocumentSegment.document_id == dataset_document.id, DocumentSegment.enabled == True) + .filter( + DocumentSegment.document_id == dataset_document.id, + DocumentSegment.enabled == False, + DocumentSegment.status == "completed", + ) .order_by(DocumentSegment.position.asc()) .all() ) @@ -85,6 +89,16 @@ def add_document_to_index_task(dataset_document_id: str): db.session.query(DatasetAutoDisableLog).filter( DatasetAutoDisableLog.document_id == dataset_document.id ).delete() + + # update segment to enable + db.session.query(DocumentSegment).filter(DocumentSegment.document_id == dataset_document.id).update( + { + DocumentSegment.enabled: True, + DocumentSegment.disabled_at: None, + DocumentSegment.disabled_by: None, + DocumentSegment.updated_at: datetime.datetime.now(datetime.UTC).replace(tzinfo=None), + } + ) db.session.commit() end_at = time.perf_counter() diff --git a/api/tasks/remove_document_from_index_task.py b/api/tasks/remove_document_from_index_task.py index 1d580b3802..d0c4382f58 100644 --- a/api/tasks/remove_document_from_index_task.py +++ b/api/tasks/remove_document_from_index_task.py @@ -1,3 +1,4 @@ +import datetime import logging import time @@ -46,6 +47,16 @@ def remove_document_from_index_task(document_id: str): index_processor.clean(dataset, index_node_ids, with_keywords=True, delete_child_chunks=False) except Exception: logging.exception(f"clean dataset {dataset.id} from index failed") + # update segment to disable + db.session.query(DocumentSegment).filter(DocumentSegment.document_id == document.id).update( + { + DocumentSegment.enabled: False, + DocumentSegment.disabled_at: datetime.datetime.now(datetime.UTC).replace(tzinfo=None), + DocumentSegment.disabled_by: document.disabled_by, + DocumentSegment.updated_at: datetime.datetime.now(datetime.UTC).replace(tzinfo=None), + } + ) + db.session.commit() end_at = time.perf_counter() logging.info( From f4f2567105bf2a9c8591b817bc07fa65ff2a83b7 Mon Sep 17 00:00:00 2001 From: Jyong <76649700+JohnJyong@users.noreply.github.com> Date: Fri, 27 Dec 2024 17:09:13 +0800 Subject: [PATCH 03/29] owner and admin have all permission of knowledge base (#12157) --- api/services/dataset_service.py | 73 +++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/api/services/dataset_service.py b/api/services/dataset_service.py index 1fd18568f5..b146179c3a 100644 --- a/api/services/dataset_service.py +++ b/api/services/dataset_service.py @@ -86,25 +86,30 @@ class DatasetService: else: return [], 0 else: - # show all datasets that the user has permission to access - if permitted_dataset_ids: - query = query.filter( - db.or_( - Dataset.permission == DatasetPermissionEnum.ALL_TEAM, - db.and_(Dataset.permission == DatasetPermissionEnum.ONLY_ME, Dataset.created_by == user.id), - db.and_( - Dataset.permission == DatasetPermissionEnum.PARTIAL_TEAM, - Dataset.id.in_(permitted_dataset_ids), - ), + if user.current_role not in (TenantAccountRole.OWNER, TenantAccountRole.ADMIN): + # show all datasets that the user has permission to access + if permitted_dataset_ids: + query = query.filter( + db.or_( + Dataset.permission == DatasetPermissionEnum.ALL_TEAM, + db.and_( + Dataset.permission == DatasetPermissionEnum.ONLY_ME, Dataset.created_by == user.id + ), + db.and_( + Dataset.permission == DatasetPermissionEnum.PARTIAL_TEAM, + Dataset.id.in_(permitted_dataset_ids), + ), + ) ) - ) - else: - query = query.filter( - db.or_( - Dataset.permission == DatasetPermissionEnum.ALL_TEAM, - db.and_(Dataset.permission == DatasetPermissionEnum.ONLY_ME, Dataset.created_by == user.id), + else: + query = query.filter( + db.or_( + Dataset.permission == DatasetPermissionEnum.ALL_TEAM, + db.and_( + Dataset.permission == DatasetPermissionEnum.ONLY_ME, Dataset.created_by == user.id + ), + ) ) - ) else: # if no user, only show datasets that are shared with all team members query = query.filter(Dataset.permission == DatasetPermissionEnum.ALL_TEAM) @@ -377,14 +382,19 @@ class DatasetService: if dataset.tenant_id != user.current_tenant_id: logging.debug(f"User {user.id} does not have permission to access dataset {dataset.id}") raise NoPermissionError("You do not have permission to access this dataset.") - if dataset.permission == DatasetPermissionEnum.ONLY_ME and dataset.created_by != user.id: - logging.debug(f"User {user.id} does not have permission to access dataset {dataset.id}") - raise NoPermissionError("You do not have permission to access this dataset.") - if dataset.permission == "partial_members": - user_permission = DatasetPermission.query.filter_by(dataset_id=dataset.id, account_id=user.id).first() - if not user_permission and dataset.tenant_id != user.current_tenant_id and dataset.created_by != user.id: + if user.current_role not in (TenantAccountRole.OWNER, TenantAccountRole.ADMIN): + if dataset.permission == DatasetPermissionEnum.ONLY_ME and dataset.created_by != user.id: logging.debug(f"User {user.id} does not have permission to access dataset {dataset.id}") raise NoPermissionError("You do not have permission to access this dataset.") + if dataset.permission == "partial_members": + user_permission = DatasetPermission.query.filter_by(dataset_id=dataset.id, account_id=user.id).first() + if ( + not user_permission + and dataset.tenant_id != user.current_tenant_id + and dataset.created_by != user.id + ): + logging.debug(f"User {user.id} does not have permission to access dataset {dataset.id}") + raise NoPermissionError("You do not have permission to access this dataset.") @staticmethod def check_dataset_operator_permission(user: Optional[Account] = None, dataset: Optional[Dataset] = None): @@ -394,15 +404,16 @@ class DatasetService: if not user: raise ValueError("User not found") - if dataset.permission == DatasetPermissionEnum.ONLY_ME: - if dataset.created_by != user.id: - raise NoPermissionError("You do not have permission to access this dataset.") + if user.current_role not in (TenantAccountRole.OWNER, TenantAccountRole.ADMIN): + if dataset.permission == DatasetPermissionEnum.ONLY_ME: + if dataset.created_by != user.id: + raise NoPermissionError("You do not have permission to access this dataset.") - elif dataset.permission == DatasetPermissionEnum.PARTIAL_TEAM: - if not any( - dp.dataset_id == dataset.id for dp in DatasetPermission.query.filter_by(account_id=user.id).all() - ): - raise NoPermissionError("You do not have permission to access this dataset.") + elif dataset.permission == DatasetPermissionEnum.PARTIAL_TEAM: + if not any( + dp.dataset_id == dataset.id for dp in DatasetPermission.query.filter_by(account_id=user.id).all() + ): + raise NoPermissionError("You do not have permission to access this dataset.") @staticmethod def get_dataset_queries(dataset_id: str, page: int, per_page: int): From 89ce9a5db2c24164ee70daf89d89038eb0a45cab Mon Sep 17 00:00:00 2001 From: NFish Date: Fri, 27 Dec 2024 18:10:36 +0800 Subject: [PATCH 04/29] Fix: avatar dropdown keyboard navigation (#12155) --- .../header/account-dropdown/index.tsx | 92 +++++++++++-------- .../workplace-selector/index.tsx | 20 ++-- .../components/workflow/operator/index.tsx | 4 +- 3 files changed, 69 insertions(+), 47 deletions(-) diff --git a/web/app/components/header/account-dropdown/index.tsx b/web/app/components/header/account-dropdown/index.tsx index a30ab175ac..e92b16fd67 100644 --- a/web/app/components/header/account-dropdown/index.tsx +++ b/web/app/components/header/account-dropdown/index.tsx @@ -59,23 +59,21 @@ export default function AppSelector({ isMobile }: IAppSelector) { { ({ open }) => ( <> -
- - - {!isMobile && <> - {userProfile.name} - - } - -
+ > + + {!isMobile && <> + {userProfile.name} + + } + - +
@@ -107,89 +105,107 @@ export default function AppSelector({ isMobile }: IAppSelector) {
-
{t('common.account.account')}
- + }
-
setShowAccountSettingModal({ payload: 'members' })}> + {({ active }) =>
setShowAccountSettingModal({ payload: 'members' })}>
{t('common.userProfile.settings')}
-
+
}
{canEmailSupport && -
{t('common.userProfile.emailSupport')}
-
+ }
} -
{t('common.userProfile.communityFeedback')}
- + }
-
{t('common.userProfile.community')}
- + }
-
{t('common.userProfile.helpCenter')}
- + }
-
{t('common.userProfile.roadmap')}
- + }
{ document?.body?.getAttribute('data-public-site-about') !== 'hide' && ( -
setAboutVisible(true)}> + {({ active }) =>
setAboutVisible(true)}>
{t('common.userProfile.about')}
{langeniusVersionInfo.current_version}
-
+
}
) }
-
handleLogout()}> + {({ active }) =>
handleLogout()}>
{t('common.userProfile.logout')}
-
+
}
diff --git a/web/app/components/header/account-dropdown/workplace-selector/index.tsx b/web/app/components/header/account-dropdown/workplace-selector/index.tsx index 801f0b3d52..bcc72a7bb3 100644 --- a/web/app/components/header/account-dropdown/workplace-selector/index.tsx +++ b/web/app/components/header/account-dropdown/workplace-selector/index.tsx @@ -9,6 +9,7 @@ import { useWorkspacesContext } from '@/context/workspace-context' import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows' import { Check } from '@/app/components/base/icons/src/vender/line/general' import { ToastContext } from '@/app/components/base/toast' +import classNames from '@/utils/classnames' const itemClassName = ` flex items-center px-3 py-2 h-10 cursor-pointer @@ -50,7 +51,7 @@ const WorkplaceSelector = () => {
{currentWorkspace?.name[0].toLocaleUpperCase()}
@@ -70,7 +71,7 @@ const WorkplaceSelector = () => { className={cn( ` absolute top-[1px] min-w-[200px] max-h-[70vh] overflow-y-scroll z-10 bg-white border-[0.5px] border-gray-200 - divide-y divide-gray-100 origin-top-right rounded-xl + divide-y divide-gray-100 origin-top-right rounded-xl focus:outline-none `, s.popup, )} @@ -78,11 +79,16 @@ const WorkplaceSelector = () => {
{ workspaces.map(workspace => ( -
handleSwitchWorkspace(workspace.id)}> -
{workspace.name[0].toLocaleUpperCase()}
-
{workspace.name}
- {workspace.current && } -
+ + {({ active }) =>
handleSwitchWorkspace(workspace.id)}> +
{workspace.name[0].toLocaleUpperCase()}
+
{workspace.name}
+ {workspace.current && } +
} + +
)) }
diff --git a/web/app/components/workflow/operator/index.tsx b/web/app/components/workflow/operator/index.tsx index 80c2bb5306..1ee5fef337 100644 --- a/web/app/components/workflow/operator/index.tsx +++ b/web/app/components/workflow/operator/index.tsx @@ -17,9 +17,9 @@ const Operator = ({ handleUndo, handleRedo }: OperatorProps) => { width: 102, height: 72, }} - maskColor='var(--color-shadow-shadow-5)' + maskColor='var(--color-workflow-minimap-bg)' className='!absolute !left-4 !bottom-14 z-[9] !m-0 !w-[102px] !h-[72px] !border-[0.5px] !border-divider-subtle - !rounded-lg !shadow-md !shadow-shadow-shadow-5 !bg-workflow-minimap-bg' + !rounded-lg !shadow-md !shadow-shadow-shadow-5 !bg-background-default-subtle' />
From eb8963a67309f69b553b51fda6fb3abba2978009 Mon Sep 17 00:00:00 2001 From: NFish Date: Fri, 27 Dec 2024 18:33:15 +0800 Subject: [PATCH 05/29] fix: workflow page throw warning: Attempts to access this ref will fail (#12166) --- web/app/components/base/switch/index.tsx | 101 ++++++++++++----------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/web/app/components/base/switch/index.tsx b/web/app/components/base/switch/index.tsx index 8bf32b1311..48e5c0cd8c 100644 --- a/web/app/components/base/switch/index.tsx +++ b/web/app/components/base/switch/index.tsx @@ -11,59 +11,62 @@ type SwitchProps = { className?: string } -const Switch = ({ onChange, size = 'md', defaultValue = false, disabled = false, className }: SwitchProps) => { - const [enabled, setEnabled] = useState(defaultValue) - useEffect(() => { - setEnabled(defaultValue) - }, [defaultValue]) - const wrapStyle = { - lg: 'h-6 w-11', - l: 'h-5 w-9', - md: 'h-4 w-7', - sm: 'h-3 w-5', - } +const Switch = React.forwardRef( + ({ onChange, size = 'md', defaultValue = false, disabled = false, className }: SwitchProps, + propRef: React.Ref) => { + const [enabled, setEnabled] = useState(defaultValue) + useEffect(() => { + setEnabled(defaultValue) + }, [defaultValue]) + const wrapStyle = { + lg: 'h-6 w-11', + l: 'h-5 w-9', + md: 'h-4 w-7', + sm: 'h-3 w-5', + } - const circleStyle = { - lg: 'h-5 w-5', - l: 'h-4 w-4', - md: 'h-3 w-3', - sm: 'h-2 w-2', - } + const circleStyle = { + lg: 'h-5 w-5', + l: 'h-4 w-4', + md: 'h-3 w-3', + sm: 'h-2 w-2', + } - const translateLeft = { - lg: 'translate-x-5', - l: 'translate-x-4', - md: 'translate-x-3', - sm: 'translate-x-2', - } - return ( - { - if (disabled) - return - setEnabled(checked) - onChange?.(checked) - }} - className={classNames( - wrapStyle[size], - enabled ? 'bg-components-toggle-bg' : 'bg-components-toggle-bg-unchecked', - 'relative inline-flex flex-shrink-0 cursor-pointer rounded-[5px] border-2 border-transparent transition-colors duration-200 ease-in-out', - disabled ? '!opacity-50 !cursor-not-allowed' : '', - className, - )} - > - - ) -} + > +