update login

This commit is contained in:
sijinhui 2024-04-18 16:56:09 +08:00
parent 714ad21b74
commit ef5638e427
6 changed files with 267 additions and 134 deletions

View File

@ -1,6 +1,5 @@
import { NextRequest, NextResponse } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { insertUser } from "@/lib/auth";
import { getTokenLength } from "@/lib/utils"; import { getTokenLength } from "@/lib/utils";
async function handle( async function handle(

View File

@ -1,9 +0,0 @@
input {
text-align: left !important
}
.ant-input {
text-align: left !important
}

View File

@ -1,6 +1,5 @@
"use client"; "use client";
import "./users-table.modules.scss";
import React, { Dispatch, SetStateAction, useEffect, useState } from "react"; import React, { Dispatch, SetStateAction, useEffect, useState } from "react";
import { User } from "@prisma/client"; import { User } from "@prisma/client";
import { import {

View File

@ -3,17 +3,79 @@
import { signIn } from "next-auth/react"; import { signIn } from "next-auth/react";
import React, { useState, useEffect, useRef, use } from "react"; import React, { useState, useEffect, useRef, use } from "react";
import { isName } from "@/lib/auth_list"; import { isName } from "@/lib/auth_list";
import { Form, Input, InputRef } from "antd";
import { UserOutlined, MailOutlined } from "@ant-design/icons";
import type { FormProps } from "antd";
import { SignInOptions } from "next-auth/react";
export default function UserLoginButton() { export default function UserLoginButton() {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [loginForm] = Form.useForm();
const nameInput = useRef<HTMLInputElement>(null); const nameInput = useRef<InputRef>(null);
const passwordInput = useRef<HTMLInputElement>(null); const passwordInput = useRef<InputRef>(null);
const emailInput = useRef<HTMLInputElement>(null); const emailInput = useRef<HTMLInputElement>(null);
const [username, setUsername] = useState(""); const [username, setUsername] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [error, setError] = useState(false); const [error, setError] = useState(false);
type FieldType = {
username?: string;
password?: string;
email?: string;
};
const onFinish: FormProps<FieldType>["onFinish"] = (values) => {
setLoading(true);
let signInOptions: SignInOptions = {
redirect: false,
};
let loginProvider = "";
if (values.email) {
loginProvider = "email";
signInOptions = { ...signInOptions, email: values.email };
} else {
loginProvider = "credentials";
signInOptions = {
...signInOptions,
username: values.username,
password: values.password ?? "",
};
}
signIn(loginProvider, signInOptions).then((result) => {
setLoading(false);
if (!result?.error) {
window.location.href =
result?.url && result.url.includes("verify") ? result.url : "/";
} else {
const errorParts = result.error.split(",");
if (errorParts.length > 1) {
loginForm.setFields([
{
name: errorParts[0],
errors: [errorParts[1]],
},
]);
} else {
loginForm.setFields([
{
name: "password",
errors: [result.error],
},
]);
}
}
console.log("response,", result);
});
setLoading(false);
console.log("Success:", values);
};
const onFinishFailed: FormProps<FieldType>["onFinishFailed"] = (
errorInfo,
) => {
console.log("Failed:", errorInfo);
};
const handleNameComposition = ( const handleNameComposition = (
e: React.CompositionEvent<HTMLInputElement>, e: React.CompositionEvent<HTMLInputElement>,
@ -68,10 +130,11 @@ export default function UserLoginButton() {
if (nameInput.current) { if (nameInput.current) {
if (!isName(username)) { if (!isName(username)) {
setError(true); setError(true);
nameInput.current.setCustomValidity("用户名校验失败"); // nameInput
// nameInput.current.setCustomValidity("用户名校验失败");
} else { } else {
setError(false); setError(false);
nameInput.current.setCustomValidity(""); // nameInput.current.setCustomValidity("");
} }
} }
// console.log("username:", username); // console.log("username:", username);
@ -79,102 +142,176 @@ export default function UserLoginButton() {
return ( return (
<> <>
{/* <div className="mt-6 sm:mx-auto sm:w-full sm:max-w-sm">
This example requires updating your template: <Form
```
<html class="h-full bg-white">
<body class="h-full">
```
*/}
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<form
className="space-y-6" className="space-y-6"
action="#" // action="#"
method="POST" // method="POST"
autoComplete="off" autoComplete="off"
onSubmit={onSubmitHandler} onFinish={onFinish}
onFinishFailed={onFinishFailed}
size="large"
form={loginForm}
id="login-form"
// onSubmit={onSubmitHandler}
> >
<div> <div>
<div className="mt-2"> <Form.Item
<input name="username"
id="username" rules={[
name="username" {
type="username" validator: async (_, value) => {
ref={nameInput} if (value && !isName(value)) {
// value={username} return Promise.reject(
onCompositionStart={(e) => e.preventDefault()} new Error("Invalid username format!"),
onCompositionEnd={handleNameComposition} );
onChange={onNameChange} }
// required const email_value = loginForm.getFieldValue("email");
placeholder="输入姓名、拼音或邮箱" if (!value && !email_value) {
className={`${ return Promise.reject(
loading new Error("Please input your username!"),
? "cursor-not-allowed bg-stone-50 dark:bg-stone-800" );
: "bg-white hover:bg-stone-50 active:bg-stone-100 dark:bg-black dark:hover:border-white dark:hover:bg-black" }
} group my-2 flex h-10 w-full items-center justify-center space-x-2 rounded-md border border-stone-200 transition-colors duration-75 focus:outline-none dark:border-stone-700 if (value && email_value) {
${ return Promise.reject(new Error("Field must be unique!"));
error }
? "focus:invalid:border-red-500 focus:invalid:ring-red-500" const password_value = loginForm.getFieldValue("password");
: "" if (!value && password_value) {
} return Promise.reject(
`} new Error("Please input your username!"),
/> );
<input }
id="password" },
name="password" },
type="password" ]}
ref={passwordInput} >
value={password} <Input
// id="basic_username"
// name="username"
// type="username"
// ref={nameInput}
// // value={username}
// onCompositionStart={(e) => e.preventDefault()} // onCompositionStart={(e) => e.preventDefault()}
// onCompositionEnd={handleComposition} // onCompositionEnd={handleNameComposition}
onChange={onPasswordChange} // onChange={onNameChange}
// required // required
placeholder="密码验证,测试阶段" autoComplete="off"
className={`${ prefix={<UserOutlined style={{ color: "rgba(0,0,0,.25)" }} />}
loading placeholder="输入姓名、拼音或邮箱"
? "cursor-not-allowed bg-stone-50 dark:bg-stone-800" className={
: "bg-white hover:bg-stone-50 active:bg-stone-100 dark:bg-black dark:hover:border-white dark:hover:bg-black" "text-sm font-medium text-stone-600 dark:text-stone-400"
} group my-2 flex h-10 w-full items-center justify-center space-x-2 rounded-md border border-stone-200 transition-colors duration-75 focus:outline-none dark:border-stone-700 }
${ // className={`${
error // loading
? "focus:invalid:border-red-500 focus:invalid:ring-red-500" // ? "cursor-not-allowed bg-stone-50 dark:bg-stone-800"
: "" // : "bg-white hover:bg-stone-50 active:bg-stone-100 dark:bg-black dark:hover:border-white dark:hover:bg-black"
} // } group my-2 flex h-10 w-full items-center justify-center space-x-2 rounded-md border border-stone-200 transition-colors duration-75 focus:outline-none dark:border-stone-700
`} // ${
// error
// ? "focus:invalid:border-red-500 focus:invalid:ring-red-500"
// : ""
// }
// `}
/> />
<input </Form.Item>
id="email"
name="email" <Form.Item<FieldType>
type="email" // label="Password"
ref={emailInput} name="password"
rules={[
{
validator: async (_, value) => {
if (value) {
if (value.length < 6) {
return Promise.reject(
new Error("Password must be at least 6 characters!"),
);
}
}
},
},
]}
>
<Input.Password
// id="basic_password"
// name="password"
// type="password"
// status={error ? "error" : ""}
// ref={passwordInput}
// value={password}
// // onCompositionStart={(e) => e.preventDefault()}
// // onCompositionEnd={handleComposition}
// onChange={onPasswordChange}
autoComplete="off"
// // required
placeholder="密码验证,测试阶段"
className={
"text-sm font-medium text-stone-600 dark:text-stone-400"
}
// className={`${
// loading
// ? "cursor-not-allowed bg-stone-50 dark:bg-stone-800"
// : "bg-white hover:bg-stone-50 active:bg-stone-100 dark:bg-black dark:hover:border-white dark:hover:bg-black"
// } group my-2 flex h-10 w-full items-center justify-center space-x-2 rounded-md border border-stone-200 transition-colors duration-75 focus:outline-none dark:border-stone-700
// ${
// error
// ? "focus:invalid:border-red-500 focus:invalid:ring-red-500"
// : ""
// }
// `}
/>
</Form.Item>
<Form.Item
name="email"
rules={[
{
type: "email",
message: "The input is not valid E-mail!",
},
{
validator: async (_, value) => {
const username_value = loginForm.getFieldValue("username");
if (value && username_value) {
return Promise.reject(new Error("Field must be unique!"));
}
},
},
]}
>
<Input
// id="email"
// name="email"
// type="email"
// ref={emailInput}
// value={username} // value={username}
onCompositionStart={(e) => e.preventDefault()} // onCompositionStart={(e) => e.preventDefault()}
// onCompositionEnd={handleComposition} // onCompositionEnd={handleComposition}
// onChange={onNameChange} // onChange={onNameChange}
// required // required
prefix={<MailOutlined style={{ color: "rgba(0,0,0,.25)" }} />}
placeholder="邮箱验证,测试阶段" placeholder="邮箱验证,测试阶段"
className={`${ className={
loading "text-sm font-medium text-stone-600 dark:text-stone-400"
? "cursor-not-allowed bg-stone-50 dark:bg-stone-800" }
: "bg-white hover:bg-stone-50 active:bg-stone-100 dark:bg-black dark:hover:border-white dark:hover:bg-black" // className={`${
} group my-2 flex h-10 w-full items-center justify-center space-x-2 rounded-md border border-stone-200 transition-colors duration-75 focus:outline-none dark:border-stone-700 // loading
${ // ? "cursor-not-allowed bg-stone-50 dark:bg-stone-800"
error // : "bg-white hover:bg-stone-50 active:bg-stone-100 dark:bg-black dark:hover:border-white dark:hover:bg-black"
? "focus:invalid:border-red-500 focus:invalid:ring-red-500" // } group my-2 flex h-10 w-full items-center justify-center space-x-2 rounded-md border border-stone-200 transition-colors duration-75 focus:outline-none dark:border-stone-700
: "" // ${
} // error
`} // ? "focus:invalid:border-red-500 focus:invalid:ring-red-500"
// : ""
// }
// `}
/> />
{/*{error && <p className="mt-2 text-pink-600 text-sm">{error}</p>}*/} </Form.Item>
</div>
</div> </div>
<div> <Form.Item>
<button <button
disabled={loading} disabled={loading}
// onClick={(e) => handleSubmit(e)} // onClick={() => loginForm.submit()}
type="submit" type="submit"
className={`${ className={`${
loading loading
@ -184,8 +321,8 @@ export default function UserLoginButton() {
> >
Sign in Sign in
</button> </button>
</div> </Form.Item>
</form> </Form>
</div> </div>
{/*</div>*/} {/*</div>*/}
</> </>

View File

@ -14,7 +14,7 @@
border: 0; border: 0;
border-top: 1px solid var(--color-separator); border-top: 1px solid var(--color-separator);
display: block; display: block;
margin: 2rem auto 1rem; margin: 1rem auto 1rem;
overflow: visible overflow: visible
} }
@ -47,7 +47,7 @@
} }
.signin form input[type],.signin>div input[type] { .signin form input[type],.signin>div input[type] {
margin-bottom: .5rem //margin-bottom: .5rem
} }
.signin form button,.signin>div button { .signin form button,.signin>div button {
@ -73,7 +73,7 @@ html {
.login-form { .login-form {
background-color: #fff; background-color: #fff;
width: 350px; width: 350px;
height: 500px; height: 550px;
border-radius: 15px; border-radius: 15px;
/* 定位到中心 */ /* 定位到中心 */
@ -86,4 +86,12 @@ html {
/* 毛玻璃 */ /* 毛玻璃 */
backdrop-filter: blur(10px); /* 应用模糊效果 */ backdrop-filter: blur(10px); /* 应用模糊效果 */
background-color: rgba(255, 255, 255, 0.5); /* 半透明的白色背景 */ background-color: rgba(255, 255, 255, 0.5); /* 半透明的白色背景 */
} }
input {
text-align: left !important
}
#login-form input:-webkit-autofill {
transition: background-color 5000s ease-in-out 0s;
}

View File

@ -80,7 +80,7 @@ export const authOptions: NextAuthOptions = {
const username = cleanUpString(`${credential?.username}`); const username = cleanUpString(`${credential?.username}`);
const password = cleanPassword(`${credential?.password}`); const password = cleanPassword(`${credential?.password}`);
// 验证用户名 // 验证用户名
console.log(credential, 'p', password, '==============3') // console.log(credential, 'p', password, '==============3')
// 判断姓名格式是否符合要求,不符合则拒绝 // 判断姓名格式是否符合要求,不符合则拒绝
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
@ -90,17 +90,21 @@ export const authOptions: NextAuthOptions = {
} else { } else {
user['name'] = username; user['name'] = username;
} }
if (password) { // 目前用户不存在,则会创建新用户。
user['password'] = password; let existingUser = await existUser(user); // await insertUser(user)
// 如果有密码,则启用密码验证,查询数据库,否则失败 if (!existingUser) {
return await validatePassword(user); // 如果不存在,则报错
// throw new Error("用户查询失败")
// 如果不存在,则创建
existingUser = await insertUser(user);
} }
// 有密码就校验密码,没有就直接返回用户
return await insertUser(user) ?? user password && validatePassword(password, existingUser.password);
return existingUser;
} else { } else {
// If you return null then an error will be displayed advising the user to check their details. // If you return null then an error will be displayed advising the user to check their details.
// return null // return null
throw new Error("用户名校验失败") throw new Error("username,用户名校验失败")
// You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter // You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter
} }
} }
@ -190,21 +194,15 @@ export async function VerifiedAdminUser() {
return !!(name && ADMIN_LIST.includes(name)); return !!(name && ADMIN_LIST.includes(name));
} }
export async function validatePassword(user: {[key: string]: string}): Promise<User | void> { export function validatePassword(password: string, hashPassword: string | null | undefined ): boolean | void {
if (!hashPassword) {
const existingUser = await existUser(user); throw new Error("password,未设置密码");
console.log('------', 'existUser', existUser)
if (!existingUser) {
throw new Error("用户名或密码不正确");
} }
if (existingUser.password == null) {
throw new Error("未设置密码"); if (!comparePassword(password, hashPassword)) {
} throw new Error("password,用户名或密码不正确")
if (!comparePassword(user.passowrd, existingUser.password)) {
throw new Error("用户名或密码不正确")
} else { } else {
return existingUser; return true;
} }
} }
@ -225,19 +223,20 @@ async function existUser(user: {[key: string]: string} | User ) {
export async function insertUser(user: {[key: string]: string}) { export async function insertUser(user: {[key: string]: string}) {
try { try {
const existingUser = await existUser(user); return await prisma.user.create({
// console.log('[LOG]', existingUser, user, '=======') data: user
if (!existingUser) { })
return await prisma.user.create({ // const existingUser = await existUser(user);
data: user // // console.log('[LOG]', existingUser, user, '=======')
}) // if (!existingUser) {
} else { //
// console.log('user==========', existingUser) // } else {
return existingUser; // // console.log('user==========', existingUser)
} // return existingUser;
// }
} catch (e) { } catch (e) {
console.log('[Prisma Error]', e); throw new Error("username,用户创建失败");
return false; // return false;
} }
} }