From ef5638e427903827b8005dcfe9a70408f3a33961 Mon Sep 17 00:00:00 2001 From: sijinhui Date: Thu, 18 Apr 2024 16:56:09 +0800 Subject: [PATCH] update login --- app/api/logs/[[...path]]/route.ts | 1 - .../components/users-table.modules.scss | 9 - app/app/(admin)/components/users-table.tsx | 1 - app/app/(auth)/login/user-login-button.tsx | 309 +++++++++++++----- app/app/login.scss | 16 +- lib/auth.ts | 65 ++-- 6 files changed, 267 insertions(+), 134 deletions(-) delete mode 100644 app/app/(admin)/components/users-table.modules.scss diff --git a/app/api/logs/[[...path]]/route.ts b/app/api/logs/[[...path]]/route.ts index 900b6d680..a3473a67f 100644 --- a/app/api/logs/[[...path]]/route.ts +++ b/app/api/logs/[[...path]]/route.ts @@ -1,6 +1,5 @@ import { NextRequest, NextResponse } from "next/server"; import prisma from "@/lib/prisma"; -import { insertUser } from "@/lib/auth"; import { getTokenLength } from "@/lib/utils"; async function handle( diff --git a/app/app/(admin)/components/users-table.modules.scss b/app/app/(admin)/components/users-table.modules.scss deleted file mode 100644 index 1820de0d1..000000000 --- a/app/app/(admin)/components/users-table.modules.scss +++ /dev/null @@ -1,9 +0,0 @@ - -input { - text-align: left !important -} - - -.ant-input { - text-align: left !important -} \ No newline at end of file diff --git a/app/app/(admin)/components/users-table.tsx b/app/app/(admin)/components/users-table.tsx index 98d2e5882..203185294 100644 --- a/app/app/(admin)/components/users-table.tsx +++ b/app/app/(admin)/components/users-table.tsx @@ -1,6 +1,5 @@ "use client"; -import "./users-table.modules.scss"; import React, { Dispatch, SetStateAction, useEffect, useState } from "react"; import { User } from "@prisma/client"; import { diff --git a/app/app/(auth)/login/user-login-button.tsx b/app/app/(auth)/login/user-login-button.tsx index d54b0a04b..7b8b258bc 100644 --- a/app/app/(auth)/login/user-login-button.tsx +++ b/app/app/(auth)/login/user-login-button.tsx @@ -3,17 +3,79 @@ import { signIn } from "next-auth/react"; import React, { useState, useEffect, useRef, use } from "react"; 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() { const [loading, setLoading] = useState(false); - - const nameInput = useRef(null); - const passwordInput = useRef(null); + const [loginForm] = Form.useForm(); + const nameInput = useRef(null); + const passwordInput = useRef(null); const emailInput = useRef(null); const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(false); + type FieldType = { + username?: string; + password?: string; + email?: string; + }; + const onFinish: FormProps["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["onFinishFailed"] = ( + errorInfo, + ) => { + console.log("Failed:", errorInfo); + }; const handleNameComposition = ( e: React.CompositionEvent, @@ -68,10 +130,11 @@ export default function UserLoginButton() { if (nameInput.current) { if (!isName(username)) { setError(true); - nameInput.current.setCustomValidity("用户名校验失败"); + // nameInput + // nameInput.current.setCustomValidity("用户名校验失败"); } else { setError(false); - nameInput.current.setCustomValidity(""); + // nameInput.current.setCustomValidity(""); } } // console.log("username:", username); @@ -79,102 +142,176 @@ export default function UserLoginButton() { return ( <> - {/* - This example requires updating your template: - - ``` - - - ``` - */} - -
-
+
-
- e.preventDefault()} - onCompositionEnd={handleNameComposition} - onChange={onNameChange} - // required - placeholder="输入姓名、拼音或邮箱" - 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" - : "" - } - `} - /> - { + if (value && !isName(value)) { + return Promise.reject( + new Error("Invalid username format!"), + ); + } + const email_value = loginForm.getFieldValue("email"); + if (!value && !email_value) { + return Promise.reject( + new Error("Please input your username!"), + ); + } + if (value && email_value) { + return Promise.reject(new Error("Field must be unique!")); + } + const password_value = loginForm.getFieldValue("password"); + if (!value && password_value) { + return Promise.reject( + new Error("Please input your username!"), + ); + } + }, + }, + ]} + > + e.preventDefault()} - // onCompositionEnd={handleComposition} - onChange={onPasswordChange} + // onCompositionEnd={handleNameComposition} + // onChange={onNameChange} // required - placeholder="密码验证,测试阶段" - 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" - : "" - } - `} + autoComplete="off" + prefix={} + 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" + // : "" + // } + // `} /> - + + + // label="Password" + name="password" + rules={[ + { + validator: async (_, value) => { + if (value) { + if (value.length < 6) { + return Promise.reject( + new Error("Password must be at least 6 characters!"), + ); + } + } + }, + }, + ]} + > + 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" + // : "" + // } + // `} + /> + + { + const username_value = loginForm.getFieldValue("username"); + if (value && username_value) { + return Promise.reject(new Error("Field must be unique!")); + } + }, + }, + ]} + > + e.preventDefault()} + // onCompositionStart={(e) => e.preventDefault()} // onCompositionEnd={handleComposition} // onChange={onNameChange} // required + prefix={} placeholder="邮箱验证,测试阶段" - 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" - : "" - } - `} + 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" + // : "" + // } + // `} /> - {/*{error &&

{error}

}*/} -
+
-
+ -
-
+ +
{/**/} diff --git a/app/app/login.scss b/app/app/login.scss index a91bef414..5fb5c9a00 100644 --- a/app/app/login.scss +++ b/app/app/login.scss @@ -14,7 +14,7 @@ border: 0; border-top: 1px solid var(--color-separator); display: block; - margin: 2rem auto 1rem; + margin: 1rem auto 1rem; overflow: visible } @@ -47,7 +47,7 @@ } .signin form input[type],.signin>div input[type] { - margin-bottom: .5rem + //margin-bottom: .5rem } .signin form button,.signin>div button { @@ -73,7 +73,7 @@ html { .login-form { background-color: #fff; width: 350px; - height: 500px; + height: 550px; border-radius: 15px; /* 定位到中心 */ @@ -86,4 +86,12 @@ html { /* 毛玻璃 */ backdrop-filter: blur(10px); /* 应用模糊效果 */ background-color: rgba(255, 255, 255, 0.5); /* 半透明的白色背景 */ -} \ No newline at end of file +} + +input { + text-align: left !important +} + +#login-form input:-webkit-autofill { + transition: background-color 5000s ease-in-out 0s; +} diff --git a/lib/auth.ts b/lib/auth.ts index ba0d4b4c6..828498e3a 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -80,7 +80,7 @@ export const authOptions: NextAuthOptions = { const username = cleanUpString(`${credential?.username}`); const password = cleanPassword(`${credential?.password}`); // 验证用户名 - console.log(credential, 'p', password, '==============3') + // console.log(credential, 'p', password, '==============3') // 判断姓名格式是否符合要求,不符合则拒绝 if (username && isName(username)) { // Any object returned will be saved in `user` property of the JWT @@ -90,17 +90,21 @@ export const authOptions: NextAuthOptions = { } else { user['name'] = username; } - if (password) { - user['password'] = password; - // 如果有密码,则启用密码验证,查询数据库,否则失败 - return await validatePassword(user); + // 目前用户不存在,则会创建新用户。 + let existingUser = await existUser(user); // await insertUser(user) + if (!existingUser) { + // 如果不存在,则报错 + // throw new Error("用户查询失败") + // 如果不存在,则创建 + existingUser = await insertUser(user); } - - return await insertUser(user) ?? user + // 有密码就校验密码,没有就直接返回用户 + password && validatePassword(password, existingUser.password); + return existingUser; } else { // If you return null then an error will be displayed advising the user to check their details. // 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 } } @@ -190,21 +194,15 @@ export async function VerifiedAdminUser() { return !!(name && ADMIN_LIST.includes(name)); } -export async function validatePassword(user: {[key: string]: string}): Promise { - - const existingUser = await existUser(user); - console.log('------', 'existUser', existUser) - - if (!existingUser) { - throw new Error("用户名或密码不正确"); +export function validatePassword(password: string, hashPassword: string | null | undefined ): boolean | void { + if (!hashPassword) { + throw new Error("password,未设置密码"); } - if (existingUser.password == null) { - throw new Error("未设置密码"); - } - if (!comparePassword(user.passowrd, existingUser.password)) { - throw new Error("用户名或密码不正确") + + if (!comparePassword(password, hashPassword)) { + throw new Error("password,用户名或密码不正确") } 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}) { try { - const existingUser = await existUser(user); - // console.log('[LOG]', existingUser, user, '=======') - if (!existingUser) { - return await prisma.user.create({ - data: user - }) - } else { - // console.log('user==========', existingUser) - return existingUser; - } + return await prisma.user.create({ + data: user + }) + // const existingUser = await existUser(user); + // // console.log('[LOG]', existingUser, user, '=======') + // if (!existingUser) { + // + // } else { + // // console.log('user==========', existingUser) + // return existingUser; + // } } catch (e) { - console.log('[Prisma Error]', e); - return false; + throw new Error("username,用户创建失败"); + // return false; } }