feat: user lookup and user punishments in admin panel

need to work on actually punishing the user
This commit is contained in:
trafficlunar 2025-05-25 22:16:41 +01:00
parent 0c7be71b2c
commit e195d2e80b
7 changed files with 534 additions and 7 deletions

View file

@ -5,6 +5,7 @@ import { auth } from "@/lib/auth";
import BannerForm from "@/components/admin/banner-form";
import ControlCenter from "@/components/admin/control-center";
import UserManagement from "@/components/admin/user-management";
import Reports from "@/components/admin/reports";
export const metadata: Metadata = {
@ -46,6 +47,15 @@ export default async function AdminPage() {
<ControlCenter />
{/* Separator */}
<div className="flex items-center gap-4 text-zinc-500 text-sm font-medium my-1">
<hr className="flex-grow border-zinc-300" />
<span>User Management</span>
<hr className="flex-grow border-zinc-300" />
</div>
<UserManagement />
{/* Separator */}
<div className="flex items-center gap-4 text-zinc-500 text-sm font-medium my-1">
<hr className="flex-grow border-zinc-300" />

View file

@ -0,0 +1,53 @@
import { NextRequest, NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { idSchema } from "@/lib/schemas";
export async function GET(request: NextRequest) {
const session = await auth();
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
if (Number(session.user.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
const searchParams = request.nextUrl.searchParams;
const parsed = idSchema.safeParse(searchParams.get("id"));
if (!parsed.success) return NextResponse.json({ error: parsed.error.errors[0].message }, { status: 400 });
const userId = parsed.data;
const user = await prisma.user.findUnique({
where: {
id: userId,
},
include: {
punishments: {
select: {
id: true,
type: true,
notes: true,
reasons: true,
violatingMiis: {
select: {
miiId: true,
reason: true,
},
},
expiresAt: true,
createdAt: true,
},
},
},
});
if (!user) return NextResponse.json({ error: "No user found" }, { status: 404 });
return NextResponse.json({
success: true,
name: user.name,
username: user.username,
image: user.image,
createdAt: user.createdAt,
punishments: user.punishments,
});
}

View file

@ -0,0 +1,69 @@
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import dayjs from "dayjs";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
import { idSchema } from "@/lib/schemas";
import { PunishmentType } from "@prisma/client";
const punishSchema = z.object({
type: z.enum([PunishmentType.WARNING, PunishmentType.TEMP_EXILE, PunishmentType.PERM_EXILE]),
duration: z
.number({ message: "Duration (days) must be a number" })
.int({ message: "Duration (days) must be an integer" })
.positive({ message: "Duration (days) must be valid" }),
notes: z.string(),
reasons: z.array(z.string()).optional(),
miiReasons: z
.array(
z.object({
id: z
.number({ message: "Mii ID must be a number" })
.int({ message: "Mii ID must be an integer" })
.positive({ message: "Mii ID must be valid" }),
reason: z.string(),
})
)
.optional(),
});
export async function POST(request: NextRequest) {
const session = await auth();
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
if (Number(session.user.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
const searchParams = request.nextUrl.searchParams;
const parsedUserId = idSchema.safeParse(searchParams.get("id"));
if (!parsedUserId.success) return NextResponse.json({ error: parsedUserId.error.errors[0].message }, { status: 400 });
const userId = parsedUserId.data;
const body = await request.json();
const parsed = punishSchema.safeParse(body);
if (!parsed.success) return NextResponse.json({ error: parsed.error.errors[0].message }, { status: 400 });
const { type, duration, notes, reasons, miiReasons } = parsed.data;
const expiresAt = type === "TEMP_EXILE" ? dayjs().add(duration, "days").toDate() : null;
await prisma.punishment.create({
data: {
userId,
type: type as PunishmentType,
expiresAt,
notes,
reasons: reasons?.length !== 0 ? reasons : [],
violatingMiis: {
create: miiReasons?.map((mii) => ({
miiId: mii.id,
reason: mii.reason,
})),
},
},
});
return NextResponse.json({ success: true });
}