From dc453efa5c890a37ae49da778d5049f4b2190e58 Mon Sep 17 00:00:00 2001 From: Athanasios Oikonomou Date: Sun, 10 Aug 2025 14:41:24 +0300 Subject: [PATCH] feat: Display assigned user groups in Admin Panel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Description: This PR adds the ability to view a user’s assigned groups in the Admin Panel when editing a user. Backend Changes: Added a new endpoint: GET /api/v1/users/{user_id}/groups Returns the list of groups assigned to a specific user. Requires admin privileges. Frontend Changes: Implemented getUserGroupsById API function to call the new backend endpoint, in lib/apis/users. Updated EditUserModal.svelte to: Load user groups asynchronously when the modal is opened. Display the groups inline in the form before the Save button. Show a loading state while fetching, and a “No groups assigned” message if none exist. Result: Admins can now see which groups a user belongs to directly from the edit user modal, improving visibility and reducing the need to navigate away for group membership checks. --- backend/open_webui/routers/users.py | 10 ++++++ src/lib/apis/users/index.ts | 27 ++++++++++++++++ .../admin/Users/UserList/EditUserModal.svelte | 32 ++++++++++++++++++- 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/backend/open_webui/routers/users.py b/backend/open_webui/routers/users.py index 4bb6956f29..bf951b9a1c 100644 --- a/backend/open_webui/routers/users.py +++ b/backend/open_webui/routers/users.py @@ -501,3 +501,13 @@ async def delete_user_by_id(user_id: str, user=Depends(get_admin_user)): status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.ACTION_PROHIBITED, ) + + +############################ +# GetUserGroupsById +############################ + + +@router.get("/{user_id}/groups") +async def get_user_groups_by_id(user_id: str, user=Depends(get_admin_user)): + return Groups.get_groups_by_member_id(user_id) \ No newline at end of file diff --git a/src/lib/apis/users/index.ts b/src/lib/apis/users/index.ts index 282b5bdca8..bdb44f2627 100644 --- a/src/lib/apis/users/index.ts +++ b/src/lib/apis/users/index.ts @@ -443,3 +443,30 @@ export const updateUserById = async (token: string, userId: string, user: UserUp return res; }; + +export const getUserGroupsById = async (token: string, userId: string) => { + let error = null; + + const res = await fetch(`${WEBUI_API_BASE_URL}/users/${userId}/groups`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + } + }) + .then(async (res) => { + if (!res.ok) throw await res.json(); + return res.json(); + }) + .catch((err) => { + console.error(err); + error = err.detail; + return null; + }); + + if (error) { + throw error; + } + + return res; +}; diff --git a/src/lib/components/admin/Users/UserList/EditUserModal.svelte b/src/lib/components/admin/Users/UserList/EditUserModal.svelte index 0a5301d4c9..e1f5b49768 100644 --- a/src/lib/components/admin/Users/UserList/EditUserModal.svelte +++ b/src/lib/components/admin/Users/UserList/EditUserModal.svelte @@ -4,7 +4,7 @@ import { createEventDispatcher } from 'svelte'; import { onMount, getContext } from 'svelte'; - import { updateUserById } from '$lib/apis/users'; + import { updateUserById, getUserGroupsById } from '$lib/apis/users'; import Modal from '$lib/components/common/Modal.svelte'; import localizedFormat from 'dayjs/plugin/localizedFormat'; @@ -27,6 +27,9 @@ password: '' }; + let _user_groups: any[] = []; + let loadingGroups = false; + const submitHandler = async () => { const res = await updateUserById(localStorage.token, selectedUser.id, _user).catch((error) => { toast.error(`${error}`); @@ -38,10 +41,23 @@ } }; + const loadUserGroups = async () => { + if (!selectedUser?.id) return; + loadingGroups = true; + try { + _user_groups = await getUserGroupsById(localStorage.token, selectedUser.id); + } catch (error) { + toast.error(`${error}`); + } finally { + loadingGroups = false; + } + }; + onMount(() => { if (selectedUser) { _user = selectedUser; _user.password = ''; + loadUserGroups(); } }); @@ -152,6 +168,20 @@ +
+
{$i18n.t('Groups')}
+ + {#if loadingGroups} +
{$i18n.t('Loading groups...')}
+ {:else if _user_groups.length === 0} +
{$i18n.t('No groups assigned')}
+ {:else} +
+ {_user_groups.map(g => g.name).join(', ')} +
+ {/if} +
+