mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-10-01 23:56:39 +08:00
commit
c9b3ef8b94
@ -95,7 +95,16 @@ async function handle(
|
|||||||
if (method === "PUT") {
|
if (method === "PUT") {
|
||||||
try {
|
try {
|
||||||
const userId = params.path[0];
|
const userId = params.path[0];
|
||||||
return await changeUserInfo(userId, await req.json());
|
let new_user_info: Partial<User> = Object.entries(
|
||||||
|
await req.json(),
|
||||||
|
).reduce((acc, [key, value]) => {
|
||||||
|
if (value !== null) {
|
||||||
|
// @ts-ignore
|
||||||
|
acc[key] = value;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
return await changeUserInfo(userId, new_user_info);
|
||||||
} catch {
|
} catch {
|
||||||
return NextResponse.json({ error: "未知错误" }, { status: 500 });
|
return NextResponse.json({ error: "未知错误" }, { status: 500 });
|
||||||
}
|
}
|
||||||
@ -103,16 +112,18 @@ async function handle(
|
|||||||
return NextResponse.json({ error: "当前方法不支持" }, { status: 405 });
|
return NextResponse.json({ error: "当前方法不支持" }, { status: 405 });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function changeUserInfo(id: string, info: User) {
|
async function changeUserInfo(id: string, info: Partial<User>) {
|
||||||
const hashDPassword = hashPassword(info?.password ?? "");
|
if (info.password) {
|
||||||
console.log("-----------", id, info, hashDPassword);
|
info["password"] = hashPassword(info.password);
|
||||||
if (hashDPassword) {
|
}
|
||||||
|
// console.log("-----------", id, info, hashDPassword);
|
||||||
|
if (info) {
|
||||||
await prisma.user.update({
|
await prisma.user.update({
|
||||||
where: {
|
where: {
|
||||||
id: id,
|
id: id,
|
||||||
},
|
},
|
||||||
data: {
|
data: {
|
||||||
password: hashDPassword,
|
...info,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
return NextResponse.json({ result: "ok" });
|
return NextResponse.json({ result: "ok" });
|
||||||
|
10
app/api/test/route.ts
Normal file
10
app/api/test/route.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
|
async function handle(
|
||||||
|
req: NextRequest,
|
||||||
|
{ params }: { params: { path: string[] } },
|
||||||
|
) {
|
||||||
|
return NextResponse.json({});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GET = handle;
|
@ -20,13 +20,13 @@ function SettingForm() {
|
|||||||
const [form] = Form.useForm();
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
const [setting, setSetting] = useState<Setting[]>([]);
|
const [setting, setSetting] = useState<Setting[]>([]);
|
||||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
// const [isModalVisible, setIsModalVisible] = useState(false);
|
||||||
const openModal = () => setIsModalVisible(true);
|
// const openModal = () => setIsModalVisible(true);
|
||||||
const closeModal = () => setIsModalVisible(false);
|
// const closeModal = () => setIsModalVisible(false);
|
||||||
|
//
|
||||||
const handleFormSubmit = async (record: Setting) => {
|
// const handleFormSubmit = async (record: Setting) => {
|
||||||
console.log("-------", record);
|
// console.log("-------", record);
|
||||||
};
|
// };
|
||||||
|
|
||||||
const handelDel = (record: Setting) => {
|
const handelDel = (record: Setting) => {
|
||||||
fetch(`/api/admin/setting/${record.key}`, {
|
fetch(`/api/admin/setting/${record.key}`, {
|
||||||
|
@ -5,20 +5,21 @@ import { User } from "@prisma/client";
|
|||||||
import {
|
import {
|
||||||
Space,
|
Space,
|
||||||
Table,
|
Table,
|
||||||
Tag,
|
|
||||||
Input,
|
Input,
|
||||||
Button,
|
Button,
|
||||||
notification,
|
notification as notificationModule,
|
||||||
Popconfirm,
|
|
||||||
Checkbox,
|
Checkbox,
|
||||||
|
Modal,
|
||||||
|
Form,
|
||||||
} from "antd";
|
} from "antd";
|
||||||
import type { GetRef, TableColumnsType } from "antd";
|
import type { GetRef, TableColumnsType } from "antd";
|
||||||
|
import { LockOutlined } from "@ant-design/icons";
|
||||||
// import { headers } from 'next/headers'
|
// import { headers } from 'next/headers'
|
||||||
import type { NotificationArgsProps } from "antd";
|
import type { NotificationArgsProps } from "antd";
|
||||||
|
|
||||||
import Highlighter from "react-highlight-words";
|
// import Highlighter from "react-highlight-words";
|
||||||
// 后期考虑删除该依赖
|
// 后期考虑删除该依赖
|
||||||
type NotificationPlacement = NotificationArgsProps["placement"];
|
// type NotificationPlacement = NotificationArgsProps["placement"];
|
||||||
|
|
||||||
import type { SearchProps } from "antd/es/input/Search";
|
import type { SearchProps } from "antd/es/input/Search";
|
||||||
|
|
||||||
@ -37,9 +38,6 @@ interface SearchTextProps {
|
|||||||
setSearchText: Dispatch<SetStateAction<string>>;
|
setSearchText: Dispatch<SetStateAction<string>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
type DataIndex = keyof User;
|
|
||||||
type InputRef = GetRef<typeof Input>;
|
|
||||||
|
|
||||||
function UserTableSearchInput({ users, setUsers, setLoading }: UserInterface) {
|
function UserTableSearchInput({ users, setUsers, setLoading }: UserInterface) {
|
||||||
const [searchText, setSearchText] = useState("");
|
const [searchText, setSearchText] = useState("");
|
||||||
// 这里直接搜索,并获取数据不传递搜索的值给表格了。
|
// 这里直接搜索,并获取数据不传递搜索的值给表格了。
|
||||||
@ -82,63 +80,114 @@ function UserTableSearchInput({ users, setUsers, setLoading }: UserInterface) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function UsersTable({ users, setUsers, loading }: UserInterface) {
|
function UsersTable({ users, setUsers, loading }: UserInterface) {
|
||||||
const [api, contextHolder] = notification.useNotification();
|
const [notification, notificationContextHolder] =
|
||||||
|
notificationModule.useNotification();
|
||||||
|
const [editUserModal, editUserModalContextHolder] = Modal.useModal();
|
||||||
|
const [editUserForm] = Form.useForm();
|
||||||
|
|
||||||
const [newPassword, setNewPassword] = useState("");
|
const handleUserEdit = (method: "POST" | "PUT", record: User | undefined) => {
|
||||||
|
editUserModal.confirm({
|
||||||
|
title: "编辑用户",
|
||||||
|
content: (
|
||||||
|
<Form
|
||||||
|
form={editUserForm}
|
||||||
|
labelCol={{ span: 7 }}
|
||||||
|
wrapperCol={{ span: 28 }}
|
||||||
|
layout="horizontal"
|
||||||
|
autoComplete="off"
|
||||||
|
initialValues={record}
|
||||||
|
preserve={false}
|
||||||
|
>
|
||||||
|
<Form.Item name="id" label="id" rules={[{ required: true }]}>
|
||||||
|
<Input disabled />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="name" label="name">
|
||||||
|
<Input disabled />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="username" label="username">
|
||||||
|
<Input autoComplete="off" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="gh_username" label="gh_username">
|
||||||
|
<Input disabled />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="email" label="email">
|
||||||
|
<Input />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="emailVerified" label="emailVerified">
|
||||||
|
<Input disabled />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="createdAt" label="createdAt">
|
||||||
|
<Input disabled />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="updatedAt" label="updatedAt">
|
||||||
|
<Input disabled />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="allowToLogin"
|
||||||
|
label="allowToLogin"
|
||||||
|
valuePropName="checked"
|
||||||
|
>
|
||||||
|
<Checkbox />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item name="isAdmin" label="isAdmin" valuePropName="checked">
|
||||||
|
<Checkbox />
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
const newPasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
<Form.Item name="password" label="password">
|
||||||
// if ((e.nativeEvent as InputEvent).isComposing) {
|
<Input
|
||||||
// return;
|
prefix={<LockOutlined className="site-form-item-icon" />}
|
||||||
// }
|
type="password"
|
||||||
setNewPassword(e.target.value.trim());
|
placeholder="Password"
|
||||||
};
|
autoComplete="new-password"
|
||||||
|
/>
|
||||||
const confirmPassword = async (id: string) => {
|
</Form.Item>
|
||||||
console.log("-----", newPassword, id);
|
</Form>
|
||||||
try {
|
),
|
||||||
fetch(`/api/admin/users/${id}`, {
|
onOk: () => {
|
||||||
method: "put",
|
const setting_key = method === "PUT" ? record?.id : "";
|
||||||
headers: {
|
editUserForm.validateFields().then((values) => {
|
||||||
"Content-Type": "application/json",
|
const dataToSubmit = {
|
||||||
},
|
username: values.username ?? null,
|
||||||
body: JSON.stringify({
|
email: values.email ?? null,
|
||||||
password: newPassword,
|
allowToLogin: values.allowToLogin ?? true,
|
||||||
}),
|
isAdmin: values.isAdmin ?? false,
|
||||||
credentials: "include",
|
password: values.password ?? null,
|
||||||
})
|
};
|
||||||
.then((response) => response.json())
|
fetch(`/api/admin/users/${values.id}`, {
|
||||||
.then((res) => {
|
method: method,
|
||||||
if (res["result"] == "ok") {
|
credentials: "include",
|
||||||
openNotification("info", {
|
headers: { "Content-Type": "application/json" },
|
||||||
message: "修改密码",
|
body: JSON.stringify(dataToSubmit),
|
||||||
description: `${id} 密码修改成功`,
|
})
|
||||||
|
.then((response) => response.json())
|
||||||
|
.then((result) => {
|
||||||
|
if (result["result"] == "ok") {
|
||||||
|
openNotification("info", {
|
||||||
|
message: "修改信息",
|
||||||
|
description: `${values.id} 信息修改成功`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log("e", error);
|
||||||
|
openNotification("error", {
|
||||||
|
message: "修改信息",
|
||||||
|
description: `${values.id} 信息修改失败`,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.log("e", error);
|
|
||||||
openNotification("error", {
|
|
||||||
message: "修改密码",
|
|
||||||
description: `${id} 密码修改失败`,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
} catch {
|
},
|
||||||
openNotification("error", {
|
});
|
||||||
message: "修改密码",
|
|
||||||
description: `${id} 密码修改失败`,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setNewPassword("");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const openNotification = (level: string, arms: NotificationArgsProps) => {
|
const openNotification = (level: string, arms: NotificationArgsProps) => {
|
||||||
if (level === "error") {
|
if (level === "error") {
|
||||||
api.error({
|
notification.error({
|
||||||
...arms,
|
...arms,
|
||||||
placement: "topRight",
|
placement: "topRight",
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
api.info({
|
notification.info({
|
||||||
...arms,
|
...arms,
|
||||||
placement: "topRight",
|
placement: "topRight",
|
||||||
});
|
});
|
||||||
@ -189,7 +238,7 @@ function UsersTable({ users, setUsers, loading }: UserInterface) {
|
|||||||
render: (value) => getCurrentTime(new Date(value)),
|
render: (value) => getCurrentTime(new Date(value)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "isAdmin",
|
title: "管理员",
|
||||||
dataIndex: "isAdmin",
|
dataIndex: "isAdmin",
|
||||||
width: 80,
|
width: 80,
|
||||||
render: (value) => {
|
render: (value) => {
|
||||||
@ -201,9 +250,9 @@ function UsersTable({ users, setUsers, loading }: UserInterface) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "allowToLogin",
|
title: "允许登录",
|
||||||
dataIndex: "allowToLogin",
|
dataIndex: "allowToLogin",
|
||||||
width: 120,
|
width: 80,
|
||||||
render: (value) => {
|
render: (value) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -217,29 +266,13 @@ function UsersTable({ users, setUsers, loading }: UserInterface) {
|
|||||||
dataIndex: "",
|
dataIndex: "",
|
||||||
key: "id",
|
key: "id",
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Space size="middle">
|
<Space size="small">
|
||||||
{contextHolder}
|
<Button type="link" onClick={() => handleUserEdit("PUT", record)}>
|
||||||
<Popconfirm
|
编辑
|
||||||
id="user-admin-table-pop_confirm"
|
</Button>
|
||||||
title="设置密码"
|
<Button type="link" onClick={() => handleDeleteUser(record)}>
|
||||||
description={
|
删除
|
||||||
<>
|
</Button>
|
||||||
<Input.Password
|
|
||||||
autoComplete="new-password"
|
|
||||||
value={newPassword}
|
|
||||||
onCompositionStart={(e) => e.preventDefault()}
|
|
||||||
onChange={newPasswordChange}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
onConfirm={() => confirmPassword(record.id)}
|
|
||||||
onOpenChange={() => console.log("open change")}
|
|
||||||
>
|
|
||||||
<Button type="primary" size="small">
|
|
||||||
设置密码
|
|
||||||
</Button>
|
|
||||||
</Popconfirm>
|
|
||||||
<a onClick={() => handleDeleteUser(record)}>删除</a>
|
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@ -247,16 +280,20 @@ function UsersTable({ users, setUsers, loading }: UserInterface) {
|
|||||||
// console.log(users, "users2");
|
// console.log(users, "users2");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table
|
<>
|
||||||
dataSource={users}
|
{notificationContextHolder}
|
||||||
rowKey="id"
|
{editUserModalContextHolder}
|
||||||
columns={columns}
|
<Table
|
||||||
loading={loading as boolean}
|
dataSource={users}
|
||||||
scroll={{
|
rowKey="id"
|
||||||
scrollToFirstRowOnChange: true,
|
columns={columns}
|
||||||
y: 1080,
|
loading={loading as boolean}
|
||||||
}}
|
scroll={{
|
||||||
/>
|
scrollToFirstRowOnChange: true,
|
||||||
|
y: 1080,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import {createTransport} from "nodemailer";
|
|||||||
import { comparePassword, hashPassword } from "@/lib/utils";
|
import { comparePassword, hashPassword } from "@/lib/utils";
|
||||||
import {getCurStartEnd} from "@/app/utils/custom";
|
import {getCurStartEnd} from "@/app/utils/custom";
|
||||||
const SECURE_COOKIES:boolean = !!process.env.SECURE_COOKIES;
|
const SECURE_COOKIES:boolean = !!process.env.SECURE_COOKIES;
|
||||||
type PartialUser = Partial<User>;
|
|
||||||
|
|
||||||
|
|
||||||
export const authOptions: NextAuthOptions = {
|
export const authOptions: NextAuthOptions = {
|
||||||
@ -89,7 +89,7 @@ export const authOptions: NextAuthOptions = {
|
|||||||
// 判断姓名格式是否符合要求,不符合则拒绝
|
// 判断姓名格式是否符合要求,不符合则拒绝
|
||||||
if (username && isName(username)) {
|
if (username && isName(username)) {
|
||||||
// Any object returned will be saved in `user` property of the JWT
|
// Any object returned will be saved in `user` property of the JWT
|
||||||
let user: PartialUser = {}
|
let user: Partial<User> = {}
|
||||||
if (isEmail(username)) {
|
if (isEmail(username)) {
|
||||||
user['email'] = username;
|
user['email'] = username;
|
||||||
} else {
|
} else {
|
||||||
@ -228,7 +228,7 @@ async function getSetting(key: string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function existUser(user: PartialUser ) {
|
async function existUser(user: Partial<User> ) {
|
||||||
const conditions = [];
|
const conditions = [];
|
||||||
if (user?.name) {
|
if (user?.name) {
|
||||||
conditions.push({ name: user.name });
|
conditions.push({ name: user.name });
|
||||||
@ -243,7 +243,7 @@ async function existUser(user: PartialUser ) {
|
|||||||
}) : null
|
}) : null
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function insertUser(user: PartialUser ) {
|
export async function insertUser(user: Partial<User> ) {
|
||||||
console.log('------------', user)
|
console.log('------------', user)
|
||||||
try {
|
try {
|
||||||
return await prisma.user.create({
|
return await prisma.user.create({
|
||||||
|
Loading…
Reference in New Issue
Block a user