mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-06-27 16:10:30 +08:00
Supporting user pagination on the web admin view
This commit is contained in:
@@ -19,14 +19,14 @@ router = APIRouter()
|
||||
def get_users(
|
||||
api_client_id: Optional[UUID] = None,
|
||||
max_count: Optional[int] = Query(100, gt=0, le=10000),
|
||||
gte: Optional[str] = None,
|
||||
gt: Optional[str] = None,
|
||||
lt: Optional[str] = None,
|
||||
auth_method: Optional[str] = None,
|
||||
api_client: ApiClient = Depends(deps.get_api_client),
|
||||
db: Session = Depends(deps.get_db),
|
||||
):
|
||||
ur = UserRepository(db, api_client)
|
||||
users = ur.query_users(api_client_id=api_client_id, limit=max_count, gte=gte, lt=lt, auth_method=auth_method)
|
||||
users = ur.query_users(api_client_id=api_client_id, limit=max_count, gt=gt, lt=lt, auth_method=auth_method)
|
||||
return [u.to_protocol_frontend_user() for u in users]
|
||||
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ class UserRepository:
|
||||
self,
|
||||
api_client_id: Optional[UUID] = None,
|
||||
limit: Optional[int] = 20,
|
||||
gte: Optional[str] = None,
|
||||
gt: Optional[str] = None,
|
||||
lt: Optional[str] = None,
|
||||
auth_method: Optional[str] = None,
|
||||
) -> list[User]:
|
||||
@@ -183,8 +183,8 @@ class UserRepository:
|
||||
|
||||
users = users.order_by(User.display_name)
|
||||
|
||||
if gte:
|
||||
users = users.filter(User.display_name >= gte)
|
||||
if gt:
|
||||
users = users.filter(User.display_name > gt)
|
||||
|
||||
if lt:
|
||||
users = users.filter(User.display_name < lt)
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Th,
|
||||
Thead,
|
||||
Tr,
|
||||
useToast,
|
||||
} from "@chakra-ui/react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
@@ -18,26 +19,60 @@ import { get } from "src/lib/api";
|
||||
import type { User } from "src/types/Users";
|
||||
import useSWR from "swr";
|
||||
|
||||
interface Pagination {
|
||||
/**
|
||||
* The user's `display_name` used for pagination.
|
||||
*/
|
||||
cursor: string;
|
||||
|
||||
/**
|
||||
* The pagination direction.
|
||||
*/
|
||||
direction: "forward" | "back";
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches users from the users api route and then presents them in a simple Chakra table.
|
||||
*/
|
||||
const UsersCell = () => {
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
const toast = useToast();
|
||||
const [pagination, setPagination] = useState<Pagination>({ cursor: "", direction: "forward" });
|
||||
const [users, setUsers] = useState<User[]>([]);
|
||||
|
||||
// Fetch and save the users.
|
||||
// This follows useSWR's recommendation for simple pagination:
|
||||
// https://swr.vercel.app/docs/pagination#when-to-use-useswr
|
||||
useSWR(`/api/admin/users?pageIndex=${pageIndex}`, get, {
|
||||
onSuccess: setUsers,
|
||||
useSWR(`/api/admin/users?direction=${pagination.direction}&cursor=${pagination.cursor}`, get, {
|
||||
onSuccess: (data) => {
|
||||
// When no more users can be found, trigger a toast to indicate why no
|
||||
// changes have taken place. We have to maintain a non-empty set of
|
||||
// users otherwise we can't paginate using a cursor (since we've lost the
|
||||
// cursor).
|
||||
if (data.length === 0) {
|
||||
toast({
|
||||
title: "No more users",
|
||||
status: "warning",
|
||||
duration: 1000,
|
||||
isClosable: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
setUsers(data);
|
||||
},
|
||||
});
|
||||
|
||||
const toPreviousPage = () => {
|
||||
setPageIndex(Math.max(0, pageIndex - 1));
|
||||
setPagination({
|
||||
cursor: users[0].display_name,
|
||||
direction: "back",
|
||||
});
|
||||
};
|
||||
|
||||
const toNextPage = () => {
|
||||
setPageIndex(pageIndex + 1);
|
||||
setPagination({
|
||||
cursor: users[users.length - 1].display_name,
|
||||
direction: "forward",
|
||||
});
|
||||
};
|
||||
|
||||
// Present users in a naive table.
|
||||
|
||||
@@ -157,10 +157,27 @@ export class OasstApiClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the `max_count` `BackendUser`s stored by the backend.
|
||||
* Returns the set of `BackendUser`s stored by the backend.
|
||||
*
|
||||
* @param {number} max_count - The maximum number of users to fetch.
|
||||
* @param {string} cursor - The user's `display_name` to use when paginating.
|
||||
* @param {boolean} isForward - If true and `cursor` is not empty, pages
|
||||
* forward. If false and `cursor` is not empty, pages backwards.
|
||||
* @returns {Promise<BackendUser[]>} A Promise that returns an array of `BackendUser` objects.
|
||||
*/
|
||||
async fetch_users(max_count: number): Promise<BackendUser[]> {
|
||||
return this.get(`/api/v1/frontend_users/?max_count=${max_count}`);
|
||||
async fetch_users(max_count: number, cursor: string, isForward: boolean): Promise<BackendUser[]> {
|
||||
const params = new URLSearchParams();
|
||||
params.append("max_count", max_count.toString());
|
||||
|
||||
// The backend API uses different query paramters depending on the
|
||||
// pagination direction but they both take the same cursor value.
|
||||
// Depending on direction, pick the right query param.
|
||||
if (cursor !== "") {
|
||||
params.append(isForward ? "gt" : "lt", cursor);
|
||||
}
|
||||
const BASE_URL = `/api/v1/frontend_users`;
|
||||
const url = `${BASE_URL}/?${params.toString()}`;
|
||||
return this.get(url);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,17 +2,27 @@ import { withRole } from "src/lib/auth";
|
||||
import { oasstApiClient } from "src/lib/oasst_api_client";
|
||||
import prisma from "src/lib/prismadb";
|
||||
|
||||
/**
|
||||
* The number of users to fetch in a single request. Could later be a query parameter.
|
||||
*/
|
||||
const PAGE_SIZE = 20;
|
||||
|
||||
/**
|
||||
* Returns a list of user results from the database when the requesting user is
|
||||
* a logged in admin.
|
||||
*
|
||||
* This takes two query params:
|
||||
* - `cursor`: A string representing a user's `display_name`.
|
||||
* - `direction`: Either "forward" or "backward" representing the pagination
|
||||
* direction.
|
||||
*/
|
||||
const handler = withRole("admin", async (req, res) => {
|
||||
// TODO(#673): Update this to support pagination.
|
||||
const { cursor, direction } = req.query;
|
||||
|
||||
// First, get all the users according to the backend.
|
||||
const all_users = await oasstApiClient.fetch_users(20);
|
||||
const all_users = await oasstApiClient.fetch_users(PAGE_SIZE, cursor as string, direction === "forward");
|
||||
|
||||
// Next, get all the users stored in the web's auth datbase to fetch their role.
|
||||
// Next, get all the users stored in the web's auth database to fetch their role.
|
||||
const local_user_ids = all_users.map(({ id }) => id);
|
||||
const local_users = await prisma.user.findMany({
|
||||
where: {
|
||||
|
||||
Reference in New Issue
Block a user