mirror of
https://github.com/JetSprow/J-Board-Lite.git
synced 2026-05-01 01:14:10 +05:30
fix: require email verification before activation
This commit is contained in:
@@ -13,6 +13,7 @@ enum Role {
|
||||
|
||||
enum UserStatus {
|
||||
ACTIVE
|
||||
PENDING_EMAIL
|
||||
DISABLED
|
||||
BANNED
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ export async function requestRegistrationVerification(formData: FormData) {
|
||||
select: { id: true, email: true, status: true, emailVerifiedAt: true },
|
||||
});
|
||||
|
||||
if (user?.status === "ACTIVE" && !user.emailVerifiedAt) {
|
||||
if (user && ["ACTIVE", "PENDING_EMAIL"].includes(user.status) && !user.emailVerifiedAt) {
|
||||
await sendRegistrationVerificationEmail({
|
||||
userId: user.id,
|
||||
email: user.email,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Prisma, SubscriptionRiskEvent } from "@prisma/client";
|
||||
import type { Prisma, SubscriptionRiskEvent, UserStatus } from "@prisma/client";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { parsePage } from "@/lib/utils";
|
||||
import {
|
||||
@@ -11,7 +11,7 @@ type RiskUser = {
|
||||
id: string;
|
||||
email: string;
|
||||
name: string | null;
|
||||
status: "ACTIVE" | "DISABLED" | "BANNED";
|
||||
status: UserStatus;
|
||||
};
|
||||
|
||||
type RiskSubscription = {
|
||||
|
||||
@@ -44,6 +44,7 @@ export default async function UsersPage({
|
||||
options: [
|
||||
{ label: "全部状态", value: "" },
|
||||
{ label: "正常", value: "ACTIVE" },
|
||||
{ label: "待邮箱验证", value: "PENDING_EMAIL" },
|
||||
{ label: "禁用", value: "DISABLED" },
|
||||
{ label: "封禁", value: "BANNED" },
|
||||
],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import type { Prisma, UserStatus } from "@prisma/client";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { parsePage } from "@/lib/utils";
|
||||
|
||||
@@ -31,7 +31,7 @@ export async function getAdminUsers(
|
||||
|
||||
const where = {
|
||||
...(role ? { role: role as "ADMIN" | "USER" } : {}),
|
||||
...(status ? { status: status as "ACTIVE" | "DISABLED" | "BANNED" } : {}),
|
||||
...(status ? { status: status as UserStatus } : {}),
|
||||
...(q
|
||||
? {
|
||||
OR: [
|
||||
|
||||
@@ -90,6 +90,7 @@ export async function POST(req: Request) {
|
||||
data: {
|
||||
email,
|
||||
emailVerifiedAt: config.emailVerificationRequired ? null : new Date(),
|
||||
status: config.emailVerificationRequired ? "PENDING_EMAIL" : "ACTIVE",
|
||||
password: hashedPassword,
|
||||
name: name || null,
|
||||
invitedById: inviterId,
|
||||
|
||||
@@ -51,6 +51,7 @@ export const userRoleLabels: Record<Role, string> = {
|
||||
|
||||
export const userStatusLabels: Record<UserStatus, string> = {
|
||||
ACTIVE: "正常",
|
||||
PENDING_EMAIL: "待邮箱验证",
|
||||
DISABLED: "禁用",
|
||||
BANNED: "封禁",
|
||||
};
|
||||
@@ -104,6 +105,7 @@ export function getSubscriptionTypeTone(type: SubscriptionType): StatusTone {
|
||||
|
||||
export function getUserStatusTone(status: UserStatus): StatusTone {
|
||||
if (status === "ACTIVE") return "success";
|
||||
if (status === "PENDING_EMAIL") return "info";
|
||||
if (status === "DISABLED") return "warning";
|
||||
return "danger";
|
||||
}
|
||||
|
||||
@@ -27,12 +27,17 @@ export const authOptions: NextAuthOptions = {
|
||||
const user = await prisma.user.findUnique({
|
||||
where: { email: credentials.email.trim().toLowerCase() },
|
||||
});
|
||||
if (!user || user.status !== "ACTIVE") return null;
|
||||
if (!user) return null;
|
||||
const valid = await bcrypt.compare(credentials.password, user.password);
|
||||
if (!valid) return null;
|
||||
if (config?.emailVerificationRequired && user.role !== "ADMIN" && !user.emailVerifiedAt) {
|
||||
if (
|
||||
user.role !== "ADMIN" &&
|
||||
!user.emailVerifiedAt &&
|
||||
(config?.emailVerificationRequired || user.status === "PENDING_EMAIL")
|
||||
) {
|
||||
throw new Error("EMAIL_NOT_VERIFIED");
|
||||
}
|
||||
if (user.status !== "ACTIVE") return null;
|
||||
return { id: user.id, email: user.email, name: user.name, role: user.role };
|
||||
},
|
||||
}),
|
||||
|
||||
@@ -244,7 +244,10 @@ export async function verifyEmailToken(token: string) {
|
||||
if (!record.userId) return { ok: false as const, message: "验证链接缺少账户信息" };
|
||||
await prisma.user.update({
|
||||
where: { id: record.userId },
|
||||
data: { emailVerifiedAt: new Date() },
|
||||
data: {
|
||||
emailVerifiedAt: new Date(),
|
||||
status: "ACTIVE",
|
||||
},
|
||||
});
|
||||
return { ok: true as const, message: "邮箱验证完成,现在可以登录账户。" };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user