167 lines
4.6 KiB
TypeScript
167 lines
4.6 KiB
TypeScript
import { NextRequest } from "next/server";
|
|
import { z } from "zod";
|
|
import { NextResponse } from "next/server";
|
|
import { getPrisma } from "../../../../lib/db";
|
|
import { getSession } from "../../../../lib/auth/session";
|
|
import { ok, fail } from "../../../../lib/http";
|
|
|
|
export const runtime = "nodejs";
|
|
|
|
const QuerySchema = z.object({
|
|
page: z.string().default("1").transform(Number),
|
|
search: z.string().optional(),
|
|
});
|
|
|
|
const UpdateUserSchema = z.object({
|
|
role: z.enum(["USER", "ADMIN"]).optional(),
|
|
isActive: z.boolean().optional(),
|
|
});
|
|
|
|
export async function GET(req: NextRequest) {
|
|
const session = await getSession();
|
|
if (!session || session.role !== "ADMIN") {
|
|
return fail(new Error("Unauthorized"), { status: 401 });
|
|
}
|
|
|
|
const prisma = await getPrisma();
|
|
if (!prisma) return fail(new Error("Database not configured"), { status: 503 });
|
|
|
|
const parsed = QuerySchema.safeParse(
|
|
Object.fromEntries(new URL(req.url).searchParams)
|
|
);
|
|
if (!parsed.success) return fail(new Error("Invalid query parameters"));
|
|
|
|
const appSetup = await prisma.appSetup.findUnique({ where: { id: 1 } });
|
|
const pageSize = appSetup?.paginationItemsPerPage || 10;
|
|
const page = Math.max(1, parsed.data.page);
|
|
const skip = (page - 1) * pageSize;
|
|
|
|
const searchFilter = parsed.data.search
|
|
? {
|
|
OR: [
|
|
{ email: { contains: parsed.data.search, mode: "insensitive" as const } },
|
|
{ firstName: { contains: parsed.data.search, mode: "insensitive" as const } },
|
|
{ lastName: { contains: parsed.data.search, mode: "insensitive" as const } },
|
|
],
|
|
}
|
|
: undefined;
|
|
|
|
const [users, total] = await Promise.all([
|
|
prisma.user.findMany({
|
|
where: searchFilter,
|
|
select: {
|
|
id: true,
|
|
email: true,
|
|
firstName: true,
|
|
lastName: true,
|
|
role: true,
|
|
isActive: true,
|
|
emailVerified: true,
|
|
createdAt: true,
|
|
gender: true,
|
|
dob: true,
|
|
address: true,
|
|
image: true,
|
|
},
|
|
orderBy: { createdAt: "desc" },
|
|
skip,
|
|
take: pageSize,
|
|
}),
|
|
prisma.user.count({ where: searchFilter }),
|
|
]);
|
|
|
|
// Fetch webinars for each user
|
|
const usersWithWebinars = await Promise.all(
|
|
users.map(async (user) => {
|
|
const registrations = await prisma.webinarRegistration.findMany({
|
|
where: { userId: user.id, status: { not: "CANCELLED" } },
|
|
});
|
|
return {
|
|
...user,
|
|
_count: {
|
|
webinarRegistrations: registrations.length,
|
|
},
|
|
registeredWebinars: await prisma.webinarRegistration.findMany({
|
|
where: { userId: user.id, status: { not: "CANCELLED" } },
|
|
select: {
|
|
id: true,
|
|
status: true,
|
|
webinar: {
|
|
select: {
|
|
id: true,
|
|
title: true,
|
|
startAt: true,
|
|
},
|
|
},
|
|
},
|
|
orderBy: { createdAt: "desc" },
|
|
take: 5,
|
|
}),
|
|
};
|
|
})
|
|
);
|
|
|
|
return ok({
|
|
users: usersWithWebinars,
|
|
pagination: {
|
|
page,
|
|
pageSize,
|
|
total,
|
|
pages: Math.ceil(total / pageSize),
|
|
hasMore: page < Math.ceil(total / pageSize),
|
|
},
|
|
});
|
|
}
|
|
|
|
export async function PATCH(req: NextRequest) {
|
|
const session = await getSession();
|
|
if (!session || session.role !== "ADMIN") {
|
|
return fail(new Error("Unauthorized"), { status: 401 });
|
|
}
|
|
|
|
const prisma = await getPrisma();
|
|
if (!prisma) return fail(new Error("Database not configured"), { status: 503 });
|
|
|
|
const body = await req.json().catch(() => ({}));
|
|
const { userId, ...updateData } = body;
|
|
|
|
if (!userId) return fail(new Error("userId is required"));
|
|
|
|
const parsed = UpdateUserSchema.safeParse(updateData);
|
|
if (!parsed.success) return fail(new Error("Invalid update data"));
|
|
|
|
// Prevent disabling the current admin
|
|
if (session.sub === userId && parsed.data.isActive === false) {
|
|
return fail(new Error("Cannot disable your own account"), { status: 400 });
|
|
}
|
|
|
|
// Prevent removing admin role from self
|
|
if (session.sub === userId && parsed.data.role && parsed.data.role !== "ADMIN") {
|
|
return fail(new Error("Cannot change your own role"), { status: 400 });
|
|
}
|
|
|
|
const user = await prisma.user.update({
|
|
where: { id: userId },
|
|
data: {
|
|
...(parsed.data.role && { role: parsed.data.role }),
|
|
...(parsed.data.isActive !== undefined && { isActive: parsed.data.isActive }),
|
|
},
|
|
select: {
|
|
id: true,
|
|
email: true,
|
|
firstName: true,
|
|
lastName: true,
|
|
role: true,
|
|
isActive: true,
|
|
},
|
|
});
|
|
|
|
return ok({
|
|
message:
|
|
parsed.data.isActive === false
|
|
? "User blocked successfully"
|
|
: "User updated successfully",
|
|
user,
|
|
});
|
|
}
|