mirror of
https://github.com/trafficlunar/tomodachi-share.git
synced 2026-06-28 14:44:15 +00:00
feat: user lookup and user punishments in admin panel
need to work on actually punishing the user
This commit is contained in:
parent
0c7be71b2c
commit
e195d2e80b
7 changed files with 534 additions and 7 deletions
|
|
@ -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" />
|
||||
|
|
|
|||
53
src/app/api/admin/lookup/route.ts
Normal file
53
src/app/api/admin/lookup/route.ts
Normal 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,
|
||||
});
|
||||
}
|
||||
69
src/app/api/admin/punish/route.ts
Normal file
69
src/app/api/admin/punish/route.ts
Normal 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 });
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue