mirror of
https://github.com/open-webui/open-webui
synced 2025-06-26 18:26:48 +00:00
Support pagination in /admin/users API
Update Users.svelte Update index.ts Update users.py Update users.py Update UserList.svelte
This commit is contained in:
parent
1349bc47b1
commit
2112067054
@ -160,11 +160,26 @@ class UsersTable:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_users(
|
def get_users(
|
||||||
self, skip: Optional[int] = None, limit: Optional[int] = None
|
self,
|
||||||
|
skip: Optional[int] = None,
|
||||||
|
limit: Optional[int] = None,
|
||||||
|
query_key: Optional[int] = None
|
||||||
) -> list[UserModel]:
|
) -> list[UserModel]:
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
|
|
||||||
|
if not query_key:
|
||||||
query = db.query(User).order_by(User.created_at.desc())
|
query = db.query(User).order_by(User.created_at.desc())
|
||||||
|
else:
|
||||||
|
query = (
|
||||||
|
db.query(User)
|
||||||
|
.filter(
|
||||||
|
or_(
|
||||||
|
User.name.ilike(f'%{query_key}%'),
|
||||||
|
User.email.ilike(f'%{query_key}%')
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.order_by(User.created_at.desc())
|
||||||
|
)
|
||||||
|
|
||||||
if skip:
|
if skip:
|
||||||
query = query.offset(skip)
|
query = query.offset(skip)
|
||||||
|
@ -35,11 +35,21 @@ router = APIRouter()
|
|||||||
|
|
||||||
@router.get("/", response_model=list[UserModel])
|
@router.get("/", response_model=list[UserModel])
|
||||||
async def get_users(
|
async def get_users(
|
||||||
skip: Optional[int] = None,
|
page: Optional[int] = None,
|
||||||
limit: Optional[int] = None,
|
limit: Optional[int] = None,
|
||||||
|
q: Optional[str] = None,
|
||||||
user=Depends(get_admin_user),
|
user=Depends(get_admin_user),
|
||||||
):
|
):
|
||||||
return Users.get_users(skip, limit)
|
if q:
|
||||||
|
skip: Optional[int] = None
|
||||||
|
if page:
|
||||||
|
skip = (page - 1) * limit
|
||||||
|
return Users.get_users(skip=skip, limit=limit, query_key=q)
|
||||||
|
else:
|
||||||
|
skip: Optional[int] = None
|
||||||
|
if page:
|
||||||
|
skip = (page - 1) * limit
|
||||||
|
return Users.get_users(skip=skip, limit=limit)
|
||||||
|
|
||||||
|
|
||||||
############################
|
############################
|
||||||
|
@ -116,10 +116,11 @@ export const updateUserRole = async (token: string, id: string, role: string) =>
|
|||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getUsers = async (token: string) => {
|
export const getUsers = async (token: string, page?: number, limit: number = 10, q?: string) => {
|
||||||
let error = null;
|
let error = null;
|
||||||
|
let res = null;
|
||||||
const res = await fetch(`${WEBUI_API_BASE_URL}/users/`, {
|
if (q !== undefined) {
|
||||||
|
res = await fetch(`${WEBUI_API_BASE_URL}/users/?q=${q}`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -135,11 +136,44 @@ export const getUsers = async (token: string) => {
|
|||||||
error = err.detail;
|
error = err.detail;
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
} else if (page !== undefined) {
|
||||||
|
res = await fetch(`${WEBUI_API_BASE_URL}/users/?page=${page}&limit=${limit}`, {
|
||||||
|
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.log(err);
|
||||||
|
error = err.detail;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
res = await fetch(`${WEBUI_API_BASE_URL}/users/`, {
|
||||||
|
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.log(err);
|
||||||
|
error = err.detail;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
if (error) {
|
if (error) {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return res ? res : [];
|
return res ? res : [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
let users = [];
|
let users = [];
|
||||||
|
let totalUsers = 0;
|
||||||
|
|
||||||
let selectedTab = 'overview';
|
let selectedTab = 'overview';
|
||||||
let loaded = false;
|
let loaded = false;
|
||||||
@ -30,6 +31,7 @@
|
|||||||
await goto('/');
|
await goto('/');
|
||||||
} else {
|
} else {
|
||||||
users = await getUsers(localStorage.token);
|
users = await getUsers(localStorage.token);
|
||||||
|
totalUsers = users.length;
|
||||||
}
|
}
|
||||||
loaded = true;
|
loaded = true;
|
||||||
|
|
||||||
@ -102,7 +104,7 @@
|
|||||||
|
|
||||||
<div class="flex-1 mt-1 lg:mt-0 overflow-y-scroll">
|
<div class="flex-1 mt-1 lg:mt-0 overflow-y-scroll">
|
||||||
{#if selectedTab === 'overview'}
|
{#if selectedTab === 'overview'}
|
||||||
<UserList {users} />
|
<UserList {totalUsers}/>
|
||||||
{:else if selectedTab === 'groups'}
|
{:else if selectedTab === 'groups'}
|
||||||
<Groups {users} />
|
<Groups {users} />
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -33,7 +33,8 @@
|
|||||||
|
|
||||||
const i18n = getContext('i18n');
|
const i18n = getContext('i18n');
|
||||||
|
|
||||||
export let users = [];
|
export let totalUsers = 0;
|
||||||
|
let users = []
|
||||||
|
|
||||||
let search = '';
|
let search = '';
|
||||||
let selectedUser = null;
|
let selectedUser = null;
|
||||||
@ -67,6 +68,13 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchUserPage = async () => {
|
||||||
|
try {
|
||||||
|
users = await getUsers(localStorage.token, page);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error fetching users: " + err);
|
||||||
|
}
|
||||||
|
};
|
||||||
let sortKey = 'created_at'; // default sort key
|
let sortKey = 'created_at'; // default sort key
|
||||||
let sortOrder = 'asc'; // default sort order
|
let sortOrder = 'asc'; // default sort order
|
||||||
|
|
||||||
@ -79,25 +87,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let filteredUsers;
|
const queryUser = async (q) => {
|
||||||
|
try {
|
||||||
$: filteredUsers = users
|
const result = await getUsers(localStorage.token, undefined, 10, q);
|
||||||
.filter((user) => {
|
filteredUsers = result.slice((page - 1) * 10, page * 10);
|
||||||
if (search === '') {
|
} catch (err) {
|
||||||
return true;
|
console.error(err);
|
||||||
} else {
|
|
||||||
let name = user.name.toLowerCase();
|
|
||||||
let email = user.email.toLowerCase();
|
|
||||||
const query = search.toLowerCase();
|
|
||||||
return name.includes(query) || email.includes(query);
|
|
||||||
}
|
}
|
||||||
})
|
};
|
||||||
|
|
||||||
|
let filteredUsers;
|
||||||
|
$: if (search.trim() === '') {
|
||||||
|
filteredUsers = users
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (a[sortKey] < b[sortKey]) return sortOrder === 'asc' ? -1 : 1;
|
if (a[sortKey] < b[sortKey]) return sortOrder === 'asc' ? -1 : 1;
|
||||||
if (a[sortKey] > b[sortKey]) return sortOrder === 'asc' ? 1 : -1;
|
if (a[sortKey] > b[sortKey]) return sortOrder === 'asc' ? 1 : -1;
|
||||||
return 0;
|
return 0;
|
||||||
})
|
})
|
||||||
.slice((page - 1) * 20, page * 20);
|
} else {
|
||||||
|
queryUser(search);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
@ -486,10 +495,10 @@
|
|||||||
ⓘ {$i18n.t("Click on the user role button to change a user's role.")}
|
ⓘ {$i18n.t("Click on the user role button to change a user's role.")}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Pagination bind:page count={users.length} />
|
<Pagination bind:page count={totalUsers} perPage={10}/>
|
||||||
|
|
||||||
{#if !$config?.license_metadata}
|
{#if !$config?.license_metadata}
|
||||||
{#if users.length > 50}
|
{#if totalUsers > 50}
|
||||||
<div class="text-sm">
|
<div class="text-sm">
|
||||||
<Markdown
|
<Markdown
|
||||||
content={`
|
content={`
|
||||||
|
Loading…
Reference in New Issue
Block a user