diff --git a/backend/adapter_processor_v2/serializers.py b/backend/adapter_processor_v2/serializers.py index f129f65c55..8c45e37596 100644 --- a/backend/adapter_processor_v2/serializers.py +++ b/backend/adapter_processor_v2/serializers.py @@ -159,6 +159,7 @@ class Meta(BaseAdapterSerializer.Meta): "adapter_type", "created_by", "shared_users", + "shared_to_org", ) # type: ignore diff --git a/backend/adapter_processor_v2/views.py b/backend/adapter_processor_v2/views.py index 36a2db0116..78f2880633 100644 --- a/backend/adapter_processor_v2/views.py +++ b/backend/adapter_processor_v2/views.py @@ -289,9 +289,12 @@ def destroy( def partial_update( self, request: Request, *args: tuple[Any], **kwargs: dict[str, Any] ) -> Response: + # Store current shared users before update (for email notifications) + adapter = self.get_object() + current_shared_users = set(adapter.shared_users.all()) + if AdapterKeys.SHARED_USERS in request.data: # find the deleted users - adapter = self.get_object() shared_users = { int(user_id) for user_id in request.data.get("shared_users", {}) } @@ -333,7 +336,47 @@ def partial_update( ) continue - return super().partial_update(request, *args, **kwargs) + # Perform the update + response = super().partial_update(request, *args, **kwargs) + + # Send email notifications to newly shared users + if response.status_code == 200 and AdapterKeys.SHARED_USERS in request.data: + try: + from plugins.notification.constants import ResourceType + from plugins.notification.sharing_notification import ( + SharingNotificationService, + ) + + adapter.refresh_from_db() + new_shared_users = set(adapter.shared_users.all()) + newly_shared_users = new_shared_users - current_shared_users + + if newly_shared_users: + # Map adapter type to specific resource type + adapter_type_to_resource = { + "LLM": ResourceType.LLM.value, + "EMBEDDING": ResourceType.EMBEDDING.value, + "VECTOR_DB": ResourceType.VECTOR_DB.value, + "X2TEXT": ResourceType.X2TEXT.value, + } + + resource_type = adapter_type_to_resource.get( + adapter.adapter_type, ResourceType.LLM.value + ) + + notification_service = SharingNotificationService() + notification_service.send_sharing_notification( + resource_type=resource_type, + resource_name=adapter.adapter_name, + resource_id=str(adapter.id), + shared_by=request.user, + shared_to=list(newly_shared_users), + resource_instance=adapter, + ) + except Exception as e: + logger.exception(f"Failed to send sharing notification: {e}") + + return response @action(detail=True, methods=["get"]) def list_of_shared_users(self, request: HttpRequest, pk: Any = None) -> Response: diff --git a/frontend/src/components/tool-settings/tool-settings/ToolSettings.jsx b/frontend/src/components/tool-settings/tool-settings/ToolSettings.jsx index 3f633e4a00..b8766c6524 100644 --- a/frontend/src/components/tool-settings/tool-settings/ToolSettings.jsx +++ b/frontend/src/components/tool-settings/tool-settings/ToolSettings.jsx @@ -170,21 +170,28 @@ function ToolSettings({ type }) { }); }; - const onShare = (userIds, adapter) => { + const onShare = (userIds, adapter, shareWithEveryone) => { const requestOptions = { method: "PATCH", url: `/api/v1/unstract/${sessionDetails?.orgId}/adapter/${adapter?.id}/`, headers: { "X-CSRFToken": sessionDetails?.csrfToken, }, - data: { shared_users: userIds }, + data: { + shared_users: userIds, + shared_to_org: shareWithEveryone || false, + }, }; axiosPrivate(requestOptions) .then((response) => { setOpenSharePermissionModal(false); + setAlertDetails({ + type: "success", + content: "Sharing settings updated successfully", + }); }) .catch((err) => { - setAlertDetails(handleException(err, "Failed to load")); + setAlertDetails(handleException(err, "Failed to update sharing")); }); }; @@ -255,6 +262,7 @@ function ToolSettings({ type }) { loading={isShareLoading} allUsers={userList} onApply={onShare} + isSharableToOrg={true} /> ); diff --git a/frontend/src/components/widgets/share-permission/SharePermission.jsx b/frontend/src/components/widgets/share-permission/SharePermission.jsx index 65637acb83..13fa5791e3 100644 --- a/frontend/src/components/widgets/share-permission/SharePermission.jsx +++ b/frontend/src/components/widgets/share-permission/SharePermission.jsx @@ -166,6 +166,7 @@ function SharePermission({ checked={shareWithEveryone} onChange={(e) => handleShareWithEveryone(e.target.checked)} className="share-per-checkbox" + disabled={!permissionEdit} > Share with everyone in current org