mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-09-10 11:41:04 +08:00
Merge pull request #521 from LAION-AI/237-manage-user-page
Adding an admin page to manage a specific user's status
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Table, TableCaption, TableContainer, Tbody, Td, Th, Thead, Tr } from "@chakra-ui/react";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import fetcher from "src/lib/fetcher";
|
||||
import useSWR from "swr";
|
||||
@@ -24,6 +25,7 @@ const UsersCell = () => {
|
||||
<Th>Email</Th>
|
||||
<Th>Name</Th>
|
||||
<Th>Role</Th>
|
||||
<Th>Update</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
@@ -33,6 +35,9 @@ const UsersCell = () => {
|
||||
<Td>{user.email}</Td>
|
||||
<Td>{user.name}</Td>
|
||||
<Td>{user.role}</Td>
|
||||
<Td>
|
||||
<Link href={`/admin/manage_user/${user.id}`}>Manage</Link>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { getToken } from "next-auth/jwt";
|
||||
|
||||
/**
|
||||
* Wraps any API Route handler and verifies that the user has the appropriate
|
||||
* role before running the handler. Returns a 403 otherwise.
|
||||
*/
|
||||
const withRole = (role: string, handler: (arg0: NextApiRequest, arg1: NextApiResponse<any>) => any) => {
|
||||
return async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const token = await getToken({ req });
|
||||
if (!token || token.role !== role) {
|
||||
res.status(403).end();
|
||||
return;
|
||||
}
|
||||
return handler(req, res);
|
||||
};
|
||||
};
|
||||
|
||||
export default withRole;
|
||||
@@ -28,8 +28,6 @@ const AdminIndex = () => {
|
||||
router.push("/");
|
||||
}, [session, status]);
|
||||
|
||||
// Show the final page.
|
||||
// TODO(#237): Display a component that fetches actual user data.
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
import { Box, Button, Container, Flex, FormControl, FormLabel, Input, Select, useToast } from "@chakra-ui/react";
|
||||
import { Field, Form, Formik } from "formik";
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/router";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useEffect } from "react";
|
||||
import { getAdminLayout } from "src/components/Layout";
|
||||
import poster from "src/lib/poster";
|
||||
import prisma from "src/lib/prismadb";
|
||||
import useSWRMutation from "swr/mutation";
|
||||
|
||||
const ManageUser = ({ user }) => {
|
||||
const toast = useToast();
|
||||
const router = useRouter();
|
||||
const { data: session, status } = useSession();
|
||||
|
||||
// Check when the user session is loaded and re-route if the user is not an
|
||||
// admin. This follows the suggestion by NextJS for handling private pages:
|
||||
// https://nextjs.org/docs/api-reference/next/router#usage
|
||||
//
|
||||
// All admin pages should use the same check and routing steps.
|
||||
useEffect(() => {
|
||||
if (status === "loading") {
|
||||
return;
|
||||
}
|
||||
if (session?.user?.role === "admin") {
|
||||
return;
|
||||
}
|
||||
router.push("/");
|
||||
}, [session, status]);
|
||||
|
||||
// Trigger to let us update the user's role. Triggers a toast when complete.
|
||||
const { trigger } = useSWRMutation("/api/admin/update_user", poster, {
|
||||
onSuccess: () => {
|
||||
toast({
|
||||
title: "User Role Updated",
|
||||
status: "success",
|
||||
duration: 1000,
|
||||
isClosable: true,
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
toast({
|
||||
title: "User Role update failed",
|
||||
status: "error",
|
||||
duration: 1000,
|
||||
isClosable: true,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Manage Users - Open Assistant</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Conversational AI for everyone. An open source project to create a chat enabled GPT LLM run by LAION and contributors around the world."
|
||||
/>
|
||||
</Head>
|
||||
<Container className="oa-basic-theme">
|
||||
<Formik
|
||||
initialValues={user}
|
||||
onSubmit={(values) => {
|
||||
trigger(values);
|
||||
}}
|
||||
>
|
||||
<Form>
|
||||
<Field name="id" type="hidden" />
|
||||
<Field name="name">
|
||||
{({ field }) => (
|
||||
<FormControl>
|
||||
<FormLabel>Username</FormLabel>
|
||||
<Input {...field} isDisabled />
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="email">
|
||||
{({ field }) => (
|
||||
<FormControl>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<Input {...field} isDisabled />
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
|
||||
<Field name="role">
|
||||
{({ field }) => (
|
||||
<FormControl>
|
||||
<FormLabel>Role</FormLabel>
|
||||
<Select {...field}>
|
||||
<option value="banned">Banned</option>
|
||||
<option value="general">General</option>
|
||||
<option value="admin">Admin</option>
|
||||
</Select>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
<Button mt={4} type="submit">
|
||||
Update
|
||||
</Button>
|
||||
</Form>
|
||||
</Formik>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch the user's data on the server side when rendering.
|
||||
*/
|
||||
export async function getServerSideProps({ query }) {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { id: query.id },
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
email: true,
|
||||
role: true,
|
||||
},
|
||||
});
|
||||
return {
|
||||
props: {
|
||||
user,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
ManageUser.getLayout = getAdminLayout;
|
||||
|
||||
export default ManageUser;
|
||||
@@ -0,0 +1,23 @@
|
||||
import { getToken } from "next-auth/jwt";
|
||||
import withRole from "src/lib/auth";
|
||||
import prisma from "src/lib/prismadb";
|
||||
|
||||
/**
|
||||
* Update's the user's data in the database. Accessible only to admins.
|
||||
*/
|
||||
const handler = withRole("admin", async (req, res) => {
|
||||
const { id, role } = JSON.parse(req.body);
|
||||
|
||||
await prisma.user.update({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
data: {
|
||||
role,
|
||||
},
|
||||
});
|
||||
|
||||
res.status(200).end();
|
||||
});
|
||||
|
||||
export default handler;
|
||||
@@ -1,21 +1,14 @@
|
||||
import { getToken } from "next-auth/jwt";
|
||||
import client from "src/lib/prismadb";
|
||||
import withRole from "src/lib/auth";
|
||||
import prisma from "src/lib/prismadb";
|
||||
|
||||
/**
|
||||
* Returns a list of user results from the database when the requesting user is
|
||||
* a logged in admin.
|
||||
*/
|
||||
const handler = async (req, res) => {
|
||||
const token = await getToken({ req });
|
||||
|
||||
// Return nothing if the user isn't registered or if the user isn't an admin.
|
||||
if (!token || token.role !== "admin") {
|
||||
res.status(403).end();
|
||||
return;
|
||||
}
|
||||
|
||||
const handler = withRole("admin", async (req, res) => {
|
||||
// Fetch 20 users.
|
||||
const users = await client.user.findMany({
|
||||
const users = await prisma.user.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
role: true,
|
||||
@@ -26,6 +19,6 @@ const handler = async (req, res) => {
|
||||
});
|
||||
|
||||
res.status(200).json(users);
|
||||
};
|
||||
});
|
||||
|
||||
export default handler;
|
||||
|
||||
Reference in New Issue
Block a user