增加清理用户功能

This commit is contained in:
sijinhui 2024-03-31 14:47:14 +08:00
parent f2d6a6dfbe
commit 95fc45486b
5 changed files with 83 additions and 150 deletions

View File

@ -1,23 +0,0 @@
import { NextRequest, NextResponse } from "next/server";
import prisma from "@/lib/prisma";
async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
// 判断网址和请求方法
// const method = req.method;
// // const url = req.url;
// const { pathname } = new URL(req.url);
//
// console.log('123', method, pathname,)
// const result = await prisma.user.findMany({
// orderBy: {
// createdAt: "desc",
// },
// });
return NextResponse.json({ error: "暂未开发" }, { status: 400 });
}
export const GET = handle;
export const POST = handle;

View File

@ -1,16 +1,24 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { getSessionName } from "@/lib/auth";
import { ADMIN_LIST } from "@/lib/auth_list";
async function handle( async function handle(
req: NextRequest, req: NextRequest,
{ params }: { params: { path: string[] } }, { params }: { params: { path: string[] } },
) { ) {
// 认证,管理员权限
const { name } = await getSessionName();
if (!(name && ADMIN_LIST.includes(name))) {
return NextResponse.json({ error: "无权限" }, { status: 401 });
}
// 判断网址和请求方法 // 判断网址和请求方法
const method = req.method; const method = req.method;
// const url = req.url; // const url = req.url;
const { pathname, searchParams } = new URL(req.url); const { pathname, searchParams } = new URL(req.url);
const searchText = searchParams.get("search"); const searchText = searchParams.get("search");
// console.log(req) // console.log(req, '2', params.path)
if (method === "GET") { if (method === "GET") {
// 是否有查询 // 是否有查询
@ -51,8 +59,27 @@ async function handle(
return NextResponse.json({ count: count, results: result }); return NextResponse.json({ count: count, results: result });
} }
return NextResponse.json({ error: "当前方法不支持" }, { status: 400 }); if (method === "DELETE") {
if (!params.path) {
return NextResponse.json({ error: "未输入用户ID" }, { status: 400 });
}
try {
const userId = params.path[0];
const user = await prisma.user.delete({
where: {
id: userId,
},
});
// console.log('user', user)
} catch (e) {
console.log("[delete user]", e);
return NextResponse.json({ error: "无法删除用户" }, { status: 400 });
}
return NextResponse.json({ result: "删除用户成功" });
}
return NextResponse.json({ error: "当前方法不支持" }, { status: 405 });
} }
export const GET = handle; export const GET = handle;
export const POST = handle; export const POST = handle;
export const DELETE = handle;

View File

@ -8,12 +8,14 @@ import React, {
useState, useState,
} from "react"; } from "react";
import { User } from "@prisma/client"; import { User } from "@prisma/client";
import { Space, Table, Tag, Input, Button } from "antd"; import { Space, Table, Tag, Input, Button, notification } from "antd";
import { SearchOutlined } from "@ant-design/icons"; import type { GetRef, TableColumnsType } from "antd";
import type { FilterDropdownProps } from "antd/es/table/interface";
import type { GetRef, TableColumnsType, TableColumnType } from "antd"; import type { NotificationArgsProps } from "antd";
import Highlighter from "react-highlight-words"; import Highlighter from "react-highlight-words";
// 后期考虑删除该依赖 // 后期考虑删除该依赖
type NotificationPlacement = NotificationArgsProps["placement"];
import type { SearchProps } from "antd/es/input/Search"; import type { SearchProps } from "antd/es/input/Search";
@ -77,122 +79,44 @@ function UserTableSearchInput({ users, setUsers, setLoading }: UserInterface) {
} }
function UsersTable({ users, setUsers, loading }: UserInterface) { function UsersTable({ users, setUsers, loading }: UserInterface) {
// const [searchText, setSearchText] = useState(""); const [api, contextHolder] = notification.useNotification();
// const [searchedColumn, setSearchedColumn] = useState("");
// const searchInput = useRef<InputRef>(null);
// const handleSearch = (
// selectedKeys: string[],
// confirm: FilterDropdownProps["confirm"],
// dataIndex: DataIndex,
// ) => {
// confirm();
// setSearchText(selectedKeys[0]);
// setSearchedColumn(dataIndex);
// };
// const handleReset = (clearFilters: () => void) => {
// clearFilters();
// setSearchText("");
// };
// const getColumnSearchProps = (
// dataIndex: DataIndex,
// ): TableColumnType<User> => ({
// filterDropdown: ({
// setSelectedKeys,
// selectedKeys,
// confirm,
// clearFilters,
// close,
// }) => (
// <div style={{ padding: 8 }} onKeyDown={(e) => e.stopPropagation()}>
// <Input
// ref={searchInput}
// placeholder={`Search ${dataIndex}`}
// value={selectedKeys[0]}
// onChange={(e) =>
// setSelectedKeys(e.target.value ? [e.target.value] : [])
// }
// onPressEnter={() =>
// handleSearch(selectedKeys as string[], confirm, dataIndex)
// }
// style={{ marginBottom: 8, display: "block" }}
// />
// <Space>
// <Button
// type="primary"
// onClick={() =>
// handleSearch(selectedKeys as string[], confirm, dataIndex)
// }
// icon={<SearchOutlined />}
// size="small"
// style={{ width: 90 }}
// >
// Search
// </Button>
// <Button
// onClick={() => clearFilters && handleReset(clearFilters)}
// size="small"
// style={{ width: 90 }}
// >
// Reset
// </Button>
// <Button
// type="link"
// size="small"
// onClick={() => {
// confirm({ closeDropdown: false });
// setSearchText((selectedKeys as string[])[0]);
// setSearchedColumn(dataIndex);
// }}
// >
// Filter
// </Button>
// <Button
// type="link"
// size="small"
// onClick={() => {
// close();
// }}
// >
// close
// </Button>
// </Space>
// </div>
// ),
// filterIcon: (filtered: boolean) => (
// <SearchOutlined style={{ color: filtered ? "#1677ff" : undefined }} />
// ),
// onFilter: (value, record: User) => {
// let result = record?.[dataIndex];
// if (result) {
// return result
// .toString()
// .toLowerCase()
// .includes((value as string).toLowerCase());
// }
// return false;
// },
// onFilterDropdownOpenChange: (visible) => {
// if (visible) {
// // @ts-ignore
// setTimeout(() => searchInput.current?.select(), 100);
// }
// },
// render: (text) =>
// searchedColumn === dataIndex ? (
// // @ts-ignore
// <Highlighter
// highlightStyle={{ backgroundColor: "#ffc069", padding: 0 }}
// searchWords={[searchText]}
// autoEscape
// textToHighlight={text ? text.toString() : ""}
// />
// ) : (
// text
// ),
// });
const openNotification = (level: string, arms: NotificationArgsProps) => {
if (level === "error") {
api.error({
...arms,
placement: "topRight",
});
} else {
api.info({
...arms,
placement: "topRight",
});
}
};
const handleDeleteUser = (record: User) => {
fetch(`/api/admin/users/${record.id}`, { method: "delete" })
.then((response) => {
console.log("delete, ", record);
if (response.ok) {
openNotification("info", {
message: "删除用户",
description: `${record.email || record.name} 删除成功`,
});
} else {
openNotification("error", {
message: "删除用户",
description: `${record.email || record.name} 删除失败`,
});
}
})
.catch((reason) => {
openNotification("error", {
message: "删除用户",
description: `${record.email || record.name} 删除失败\n${reason}`,
});
});
};
const columns: TableColumnsType<User> = [ const columns: TableColumnsType<User> = [
{ title: "Name", dataIndex: "name" }, { title: "Name", dataIndex: "name" },
{ {
@ -218,15 +142,16 @@ function UsersTable({ users, setUsers, loading }: UserInterface) {
title: "Action", title: "Action",
dataIndex: "", dataIndex: "",
key: "id", key: "id",
render: () => ( render: (_, record) => (
<Space size="middle"> <Space size="middle">
{contextHolder}
<a></a> <a></a>
<a></a> <a onClick={() => handleDeleteUser(record)}></a>
</Space> </Space>
), ),
}, },
]; ];
console.log(users, "users2"); // console.log(users, "users2");
return ( return (
<Table <Table
@ -234,6 +159,10 @@ function UsersTable({ users, setUsers, loading }: UserInterface) {
rowKey="id" rowKey="id"
columns={columns} columns={columns}
loading={loading as boolean} loading={loading as boolean}
scroll={{
scrollToFirstRowOnChange: true,
y: 1080,
}}
/> />
); );
} }

View File

@ -123,7 +123,7 @@ export const authOptions: NextAuthOptions = {
callbacks: { callbacks: {
jwt: async ({ token, user }) => { jwt: async ({ token, user }) => {
// const current_time = Math.floor(Date.now() / 1000); // const current_time = Math.floor(Date.now() / 1000);
console.log('=============', token, user,) // console.log('=============', token, user,)
if (user) { if (user) {
token.user = user; token.user = user;
} }
@ -137,7 +137,7 @@ export const authOptions: NextAuthOptions = {
// @ts-expect-error // @ts-expect-error
username: token?.user?.username || token?.user?.gh_username, username: token?.user?.username || token?.user?.gh_username,
}; };
console.log('555555555,', session, token) // console.log('555555555,', session, token)
return session; return session;
}, },
}, },

View File

@ -74,7 +74,7 @@ model LogEntry {
createdAt DateTime @default(now()) createdAt DateTime @default(now())
// logEntry String? @db.Text // logEntry String? @db.Text
logToken Int? @default(0) logToken Int? @default(0)
user User? @relation(fields: [userID], references: [id], onDelete: NoAction) user User? @relation(fields: [userID], references: [id], onDelete: SetNull)
} }
model VerificationToken { model VerificationToken {