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 {
|
enum UserStatus {
|
||||||
ACTIVE
|
ACTIVE
|
||||||
|
PENDING_EMAIL
|
||||||
DISABLED
|
DISABLED
|
||||||
BANNED
|
BANNED
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export async function requestRegistrationVerification(formData: FormData) {
|
|||||||
select: { id: true, email: true, status: true, emailVerifiedAt: true },
|
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({
|
await sendRegistrationVerificationEmail({
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
email: user.email,
|
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 { prisma } from "@/lib/prisma";
|
||||||
import { parsePage } from "@/lib/utils";
|
import { parsePage } from "@/lib/utils";
|
||||||
import {
|
import {
|
||||||
@@ -11,7 +11,7 @@ type RiskUser = {
|
|||||||
id: string;
|
id: string;
|
||||||
email: string;
|
email: string;
|
||||||
name: string | null;
|
name: string | null;
|
||||||
status: "ACTIVE" | "DISABLED" | "BANNED";
|
status: UserStatus;
|
||||||
};
|
};
|
||||||
|
|
||||||
type RiskSubscription = {
|
type RiskSubscription = {
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ export default async function UsersPage({
|
|||||||
options: [
|
options: [
|
||||||
{ label: "全部状态", value: "" },
|
{ label: "全部状态", value: "" },
|
||||||
{ label: "正常", value: "ACTIVE" },
|
{ label: "正常", value: "ACTIVE" },
|
||||||
|
{ label: "待邮箱验证", value: "PENDING_EMAIL" },
|
||||||
{ label: "禁用", value: "DISABLED" },
|
{ label: "禁用", value: "DISABLED" },
|
||||||
{ label: "封禁", value: "BANNED" },
|
{ 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 { prisma } from "@/lib/prisma";
|
||||||
import { parsePage } from "@/lib/utils";
|
import { parsePage } from "@/lib/utils";
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ export async function getAdminUsers(
|
|||||||
|
|
||||||
const where = {
|
const where = {
|
||||||
...(role ? { role: role as "ADMIN" | "USER" } : {}),
|
...(role ? { role: role as "ADMIN" | "USER" } : {}),
|
||||||
...(status ? { status: status as "ACTIVE" | "DISABLED" | "BANNED" } : {}),
|
...(status ? { status: status as UserStatus } : {}),
|
||||||
...(q
|
...(q
|
||||||
? {
|
? {
|
||||||
OR: [
|
OR: [
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ export async function POST(req: Request) {
|
|||||||
data: {
|
data: {
|
||||||
email,
|
email,
|
||||||
emailVerifiedAt: config.emailVerificationRequired ? null : new Date(),
|
emailVerifiedAt: config.emailVerificationRequired ? null : new Date(),
|
||||||
|
status: config.emailVerificationRequired ? "PENDING_EMAIL" : "ACTIVE",
|
||||||
password: hashedPassword,
|
password: hashedPassword,
|
||||||
name: name || null,
|
name: name || null,
|
||||||
invitedById: inviterId,
|
invitedById: inviterId,
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ export const userRoleLabels: Record<Role, string> = {
|
|||||||
|
|
||||||
export const userStatusLabels: Record<UserStatus, string> = {
|
export const userStatusLabels: Record<UserStatus, string> = {
|
||||||
ACTIVE: "正常",
|
ACTIVE: "正常",
|
||||||
|
PENDING_EMAIL: "待邮箱验证",
|
||||||
DISABLED: "禁用",
|
DISABLED: "禁用",
|
||||||
BANNED: "封禁",
|
BANNED: "封禁",
|
||||||
};
|
};
|
||||||
@@ -104,6 +105,7 @@ export function getSubscriptionTypeTone(type: SubscriptionType): StatusTone {
|
|||||||
|
|
||||||
export function getUserStatusTone(status: UserStatus): StatusTone {
|
export function getUserStatusTone(status: UserStatus): StatusTone {
|
||||||
if (status === "ACTIVE") return "success";
|
if (status === "ACTIVE") return "success";
|
||||||
|
if (status === "PENDING_EMAIL") return "info";
|
||||||
if (status === "DISABLED") return "warning";
|
if (status === "DISABLED") return "warning";
|
||||||
return "danger";
|
return "danger";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,17 @@ export const authOptions: NextAuthOptions = {
|
|||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
where: { email: credentials.email.trim().toLowerCase() },
|
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);
|
const valid = await bcrypt.compare(credentials.password, user.password);
|
||||||
if (!valid) return null;
|
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");
|
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 };
|
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: "验证链接缺少账户信息" };
|
if (!record.userId) return { ok: false as const, message: "验证链接缺少账户信息" };
|
||||||
await prisma.user.update({
|
await prisma.user.update({
|
||||||
where: { id: record.userId },
|
where: { id: record.userId },
|
||||||
data: { emailVerifiedAt: new Date() },
|
data: {
|
||||||
|
emailVerifiedAt: new Date(),
|
||||||
|
status: "ACTIVE",
|
||||||
|
},
|
||||||
});
|
});
|
||||||
return { ok: true as const, message: "邮箱验证完成,现在可以登录账户。" };
|
return { ok: true as const, message: "邮箱验证完成,现在可以登录账户。" };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user