diff --git a/app/api/admin/users/[...path]/route.ts b/app/api/admin/users/[...path]/route.ts deleted file mode 100644 index eafb0b642..000000000 --- a/app/api/admin/users/[...path]/route.ts +++ /dev/null @@ -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; diff --git a/app/api/admin/users/route.ts b/app/api/admin/users/[[...path]]/route.ts similarity index 63% rename from app/api/admin/users/route.ts rename to app/api/admin/users/[[...path]]/route.ts index 42999d237..b84667a1e 100644 --- a/app/api/admin/users/route.ts +++ b/app/api/admin/users/[[...path]]/route.ts @@ -1,16 +1,24 @@ import { NextRequest, NextResponse } from "next/server"; import prisma from "@/lib/prisma"; +import { getSessionName } from "@/lib/auth"; +import { ADMIN_LIST } from "@/lib/auth_list"; async function handle( req: NextRequest, { 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 url = req.url; const { pathname, searchParams } = new URL(req.url); const searchText = searchParams.get("search"); - // console.log(req) + // console.log(req, '2', params.path) if (method === "GET") { // 是否有查询 @@ -51,8 +59,27 @@ async function handle( 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 POST = handle; +export const DELETE = handle; diff --git a/app/app/(admin)/components/users-table.tsx b/app/app/(admin)/components/users-table.tsx index 756b4bcb8..d6f15c499 100644 --- a/app/app/(admin)/components/users-table.tsx +++ b/app/app/(admin)/components/users-table.tsx @@ -8,12 +8,14 @@ import React, { useState, } from "react"; import { User } from "@prisma/client"; -import { Space, Table, Tag, Input, Button } from "antd"; -import { SearchOutlined } from "@ant-design/icons"; -import type { FilterDropdownProps } from "antd/es/table/interface"; -import type { GetRef, TableColumnsType, TableColumnType } from "antd"; +import { Space, Table, Tag, Input, Button, notification } from "antd"; +import type { GetRef, TableColumnsType } from "antd"; + +import type { NotificationArgsProps } from "antd"; + import Highlighter from "react-highlight-words"; // 后期考虑删除该依赖 +type NotificationPlacement = NotificationArgsProps["placement"]; import type { SearchProps } from "antd/es/input/Search"; @@ -77,122 +79,44 @@ function UserTableSearchInput({ users, setUsers, setLoading }: UserInterface) { } function UsersTable({ users, setUsers, loading }: UserInterface) { - // const [searchText, setSearchText] = useState(""); - // const [searchedColumn, setSearchedColumn] = useState(""); - // const searchInput = useRef(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 => ({ - // filterDropdown: ({ - // setSelectedKeys, - // selectedKeys, - // confirm, - // clearFilters, - // close, - // }) => ( - //
e.stopPropagation()}> - // - // setSelectedKeys(e.target.value ? [e.target.value] : []) - // } - // onPressEnter={() => - // handleSearch(selectedKeys as string[], confirm, dataIndex) - // } - // style={{ marginBottom: 8, display: "block" }} - // /> - // - // - // - // - // - // - //
- // ), - // filterIcon: (filtered: boolean) => ( - // - // ), - // 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 - // - // ) : ( - // text - // ), - // }); + const [api, contextHolder] = notification.useNotification(); + 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 = [ { title: "Name", dataIndex: "name" }, { @@ -218,15 +142,16 @@ function UsersTable({ users, setUsers, loading }: UserInterface) { title: "Action", dataIndex: "", key: "id", - render: () => ( + render: (_, record) => ( + {contextHolder} 编辑 - 删除 + handleDeleteUser(record)}>删除 ), }, ]; - console.log(users, "users2"); + // console.log(users, "users2"); return ( ); } diff --git a/lib/auth.ts b/lib/auth.ts index 4f27aa9b3..2493aa639 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -123,7 +123,7 @@ export const authOptions: NextAuthOptions = { callbacks: { jwt: async ({ token, user }) => { // const current_time = Math.floor(Date.now() / 1000); - console.log('=============', token, user,) + // console.log('=============', token, user,) if (user) { token.user = user; } @@ -137,7 +137,7 @@ export const authOptions: NextAuthOptions = { // @ts-expect-error username: token?.user?.username || token?.user?.gh_username, }; - console.log('555555555,', session, token) + // console.log('555555555,', session, token) return session; }, }, diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 741ac3512..d75ccb125 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -74,7 +74,7 @@ model LogEntry { createdAt DateTime @default(now()) // logEntry String? @db.Text logToken Int? @default(0) - user User? @relation(fields: [userID], references: [id], onDelete: NoAction) + user User? @relation(fields: [userID], references: [id], onDelete: SetNull) } model VerificationToken {