mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-10-01 23:56:39 +08:00
测试增加邮件认证
This commit is contained in:
parent
8efbed2c28
commit
c84e223ca5
@ -7,7 +7,8 @@ import { Flex } from "antd";
|
|||||||
|
|
||||||
export default async function AdminPage() {
|
export default async function AdminPage() {
|
||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
if (!(session?.user?.name && ADMIN_LIST.includes(session.user.name))) {
|
const name = session?.user?.email || session?.user?.name;
|
||||||
|
if (!(name && ADMIN_LIST.includes(name))) {
|
||||||
// Replace '/dashboard' with the desired redirect path
|
// Replace '/dashboard' with the desired redirect path
|
||||||
redirect("/");
|
redirect("/");
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,8 @@ export default async function AuthLayout({
|
|||||||
}) {
|
}) {
|
||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
// If the user is already authenticated, redirect them to home
|
// If the user is already authenticated, redirect them to home
|
||||||
if (session?.user?.name && isName(session.user.name)) {
|
const name = session?.user?.email || session?.user?.name;
|
||||||
|
if (name && isName(name)) {
|
||||||
// Replace '/dashboard' with the desired redirect path
|
// Replace '/dashboard' with the desired redirect path
|
||||||
redirect("/");
|
redirect("/");
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { signIn } from "next-auth/react";
|
import { signIn } from "next-auth/react";
|
||||||
import React, { useState, useEffect, useRef } from "react";
|
import React, { useState, useEffect, useRef, use } from "react";
|
||||||
import { isName } from "@/lib/auth_list";
|
import { isName } from "@/lib/auth_list";
|
||||||
|
|
||||||
export default function UserLoginButton() {
|
export default function UserLoginButton() {
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const nameInput = useRef<HTMLInputElement>(null);
|
const nameInput = useRef<HTMLInputElement>(null);
|
||||||
|
const emailInput = useRef<HTMLInputElement>(null);
|
||||||
const [username, setUsername] = useState("");
|
const [username, setUsername] = useState("");
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
|
|
||||||
@ -27,18 +28,31 @@ export default function UserLoginButton() {
|
|||||||
setLoading(true);
|
setLoading(true);
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
console.log("current,username2", username);
|
let result: { error: any; url: string | null } | undefined = {
|
||||||
const result = await signIn("credentials", {
|
error: null,
|
||||||
username: username,
|
url: null,
|
||||||
redirect: false,
|
};
|
||||||
});
|
if (emailInput.current && emailInput.current.value) {
|
||||||
|
result = await signIn("email", {
|
||||||
|
email: emailInput.current.value,
|
||||||
|
redirect: false,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result = await signIn("credentials", {
|
||||||
|
username: username,
|
||||||
|
redirect: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
if (!result?.error) {
|
console.log("------1", result);
|
||||||
window.location.href = "/";
|
// if (!result?.error) {
|
||||||
} else setError(true);
|
// console.log('------2',result)
|
||||||
|
// window.location.href = result?.url || "/";
|
||||||
|
// } else setError(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!username) return;
|
||||||
if (nameInput.current) {
|
if (nameInput.current) {
|
||||||
if (!isName(username)) {
|
if (!isName(username)) {
|
||||||
setError(true);
|
setError(true);
|
||||||
@ -81,7 +95,7 @@ export default function UserLoginButton() {
|
|||||||
onCompositionStart={(e) => e.preventDefault()}
|
onCompositionStart={(e) => e.preventDefault()}
|
||||||
onCompositionEnd={handleComposition}
|
onCompositionEnd={handleComposition}
|
||||||
onChange={onNameChange}
|
onChange={onNameChange}
|
||||||
required
|
// required
|
||||||
placeholder="输入姓名、拼音或邮箱"
|
placeholder="输入姓名、拼音或邮箱"
|
||||||
className={`${
|
className={`${
|
||||||
loading
|
loading
|
||||||
@ -95,6 +109,29 @@ export default function UserLoginButton() {
|
|||||||
}
|
}
|
||||||
`}
|
`}
|
||||||
/>
|
/>
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
type="email"
|
||||||
|
ref={emailInput}
|
||||||
|
// value={username}
|
||||||
|
onCompositionStart={(e) => e.preventDefault()}
|
||||||
|
// onCompositionEnd={handleComposition}
|
||||||
|
// 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"
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
`}
|
||||||
|
/>
|
||||||
{/*{error && <p className="mt-2 text-pink-600 text-sm">{error}</p>}*/}
|
{/*{error && <p className="mt-2 text-pink-600 text-sm">{error}</p>}*/}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,7 +12,8 @@ const serverConfig = getServerSideConfig();
|
|||||||
|
|
||||||
export default async function App() {
|
export default async function App() {
|
||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
if (!session || !(session?.user?.name && isName(session.user.name))) {
|
const name = session?.user?.email || session?.user?.name;
|
||||||
|
if (!(name && isName(name))) {
|
||||||
redirect("/login");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
16
lib/auth.ts
16
lib/auth.ts
@ -1,5 +1,6 @@
|
|||||||
import { getServerSession, type NextAuthOptions } from "next-auth";
|
import { getServerSession, type NextAuthOptions } from "next-auth";
|
||||||
import GitHubProvider from "next-auth/providers/github";
|
import GitHubProvider from "next-auth/providers/github";
|
||||||
|
import EmailProvider from "next-auth/providers/email";
|
||||||
import CredentialsProvider from "next-auth/providers/credentials";
|
import CredentialsProvider from "next-auth/providers/credentials";
|
||||||
import { PrismaAdapter } from "@next-auth/prisma-adapter";
|
import { PrismaAdapter } from "@next-auth/prisma-adapter";
|
||||||
import prisma from "@/lib/prisma";
|
import prisma from "@/lib/prisma";
|
||||||
@ -27,6 +28,17 @@ export const authOptions: NextAuthOptions = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
EmailProvider({
|
||||||
|
server: {
|
||||||
|
host: process.env.EMAIL_SERVER_HOST,
|
||||||
|
port: process.env.EMAIL_SERVER_PORT,
|
||||||
|
auth: {
|
||||||
|
user: process.env.EMAIL_SERVER_USER,
|
||||||
|
pass: process.env.EMAIL_SERVER_PASSWORD,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
from: process.env.EMAIL_FROM,
|
||||||
|
}),
|
||||||
CredentialsProvider({
|
CredentialsProvider({
|
||||||
// The name to display on the sign in form (e.g. "Sign in with...")
|
// The name to display on the sign in form (e.g. "Sign in with...")
|
||||||
name: "Credentials",
|
name: "Credentials",
|
||||||
@ -66,7 +78,7 @@ export const authOptions: NextAuthOptions = {
|
|||||||
],
|
],
|
||||||
pages: {
|
pages: {
|
||||||
signIn: `/login`,
|
signIn: `/login`,
|
||||||
verifyRequest: `/login`,
|
// verifyRequest: `/login`,
|
||||||
error: "/login", // Error code passed in query string as ?error=
|
error: "/login", // Error code passed in query string as ?error=
|
||||||
},
|
},
|
||||||
adapter: PrismaAdapter(prisma),
|
adapter: PrismaAdapter(prisma),
|
||||||
@ -99,7 +111,7 @@ export const authOptions: NextAuthOptions = {
|
|||||||
session.user = {
|
session.user = {
|
||||||
...session.user,
|
...session.user,
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
id: token.sub,
|
id: token?.sub,
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
username: token?.user?.username || token?.user?.gh_username,
|
username: token?.user?.username || token?.user?.gh_username,
|
||||||
};
|
};
|
||||||
|
13
lib/utils.ts
13
lib/utils.ts
@ -1,4 +1,4 @@
|
|||||||
|
import bcrypt from "bcryptjs";
|
||||||
import {get_encoding} from "tiktoken";
|
import {get_encoding} from "tiktoken";
|
||||||
|
|
||||||
|
|
||||||
@ -18,7 +18,6 @@ export async function fetcher<JSON = any>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const capitalize = (s: string) => {
|
export const capitalize = (s: string) => {
|
||||||
if (typeof s !== "string") return "";
|
|
||||||
return s.charAt(0).toUpperCase() + s.slice(1);
|
return s.charAt(0).toUpperCase() + s.slice(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -61,3 +60,13 @@ export const toDateString = (date: Date) => {
|
|||||||
export const random = (min: number, max: number) => {
|
export const random = (min: number, max: number) => {
|
||||||
return Math.floor(Math.random() * (max - min + 1) + min);
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 将明文处理为 hash
|
||||||
|
export function hashPassword(password: string) {
|
||||||
|
return bcrypt.hashSync(password, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对比明文和 hash 是否一致
|
||||||
|
export function comparePassword(password: string, hashPassword: string) {
|
||||||
|
return bcrypt.compareSync(password, hashPassword);
|
||||||
|
}
|
||||||
|
@ -18,19 +18,19 @@ export default async function middleware(req: NextRequest) {
|
|||||||
|
|
||||||
// console.log('==============,认证,', path, session)
|
// console.log('==============,认证,', path, session)
|
||||||
// 认证有点多此一举,页面中的认证应该已经够了
|
// 认证有点多此一举,页面中的认证应该已经够了
|
||||||
if (!session && path !== "/login") {
|
// if (!session && path !== "/login") {
|
||||||
// 给关键请求特殊待遇
|
// // 给关键请求特殊待遇
|
||||||
if (path.startsWith('/api/openai/')) {
|
// if (path.startsWith('/api/openai/')) {
|
||||||
return NextResponse.json(false, {
|
// return NextResponse.json(false, {
|
||||||
status: 401,
|
// status: 401,
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
return NextResponse.redirect(new URL("/login", req.url));
|
// return NextResponse.redirect(new URL("/login", req.url));
|
||||||
} else if (session) {
|
// } else if (session) {
|
||||||
// console.log('referer=====', DENY_LIST.includes(session?.name ?? ""))
|
// // console.log('referer=====', DENY_LIST.includes(session?.name ?? ""))
|
||||||
if (isName(session?.name ?? "") && path.startsWith("/login"))
|
// if (isName(session?.name ?? "") && path.startsWith("/login"))
|
||||||
return NextResponse.redirect(new URL("/", req.url));
|
// return NextResponse.redirect(new URL("/", req.url));
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (path == '/login') {
|
if (path == '/login') {
|
||||||
return NextResponse.rewrite(
|
return NextResponse.rewrite(
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
"@vercel/analytics": "^1.1.2",
|
"@vercel/analytics": "^1.1.2",
|
||||||
"@vercel/speed-insights": "^1.0.9",
|
"@vercel/speed-insights": "^1.0.9",
|
||||||
"antd": "^5.15.1",
|
"antd": "^5.15.1",
|
||||||
|
"bcryptjs": "^2.4.3",
|
||||||
"echarts": "^5.4.3",
|
"echarts": "^5.4.3",
|
||||||
"emoji-picker-react": "^4.7.10",
|
"emoji-picker-react": "^4.7.10",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
@ -35,6 +36,7 @@
|
|||||||
"next": "^14.1.0",
|
"next": "^14.1.0",
|
||||||
"next-auth": "^4.24.5",
|
"next-auth": "^4.24.5",
|
||||||
"node-fetch": "^3.3.1",
|
"node-fetch": "^3.3.1",
|
||||||
|
"nodemailer": "^6.9.13",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-markdown": "^9.0.1",
|
"react-markdown": "^9.0.1",
|
||||||
@ -53,6 +55,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^1.5.8",
|
"@tauri-apps/cli": "^1.5.8",
|
||||||
|
"@types/bcryptjs": "^2.4.6",
|
||||||
"@types/cookie": "^0.6.0",
|
"@types/cookie": "^0.6.0",
|
||||||
"@types/node": "^20.11.10",
|
"@types/node": "^20.11.10",
|
||||||
"@types/react": "^18.2.48",
|
"@types/react": "^18.2.48",
|
||||||
|
@ -21,6 +21,7 @@ model User {
|
|||||||
email String? @unique
|
email String? @unique
|
||||||
emailVerified DateTime?
|
emailVerified DateTime?
|
||||||
image String?
|
image String?
|
||||||
|
password String?
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
updatedAt DateTime @updatedAt
|
updatedAt DateTime @updatedAt
|
||||||
accounts Account[]
|
accounts Account[]
|
||||||
|
Loading…
Reference in New Issue
Block a user