chore: update packages

also migrate zod to v4
This commit is contained in:
trafficlunar 2025-07-14 13:03:19 +01:00
parent afb73ec3a6
commit 8b4842b584
20 changed files with 918 additions and 922 deletions

View file

@ -13,7 +13,7 @@ export async function GET(request: NextRequest) {
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 });
if (!parsed.success) return NextResponse.json({ error: parsed.error.issues[0].message }, { status: 400 });
const userId = parsed.data;
const user = await prisma.user.findUnique({

View file

@ -11,18 +11,15 @@ 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" }),
.number({ error: "Duration (days) must be a number" })
.int({ error: "Duration (days) must be an integer" })
.positive({ error: "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" }),
id: z.number({ error: "Mii ID must be a number" }).int({ error: "Mii ID must be an integer" }).positive({ error: "Mii ID must be valid" }),
reason: z.string(),
})
)
@ -38,13 +35,13 @@ export async function POST(request: NextRequest) {
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 });
if (!parsedUserId.success) return NextResponse.json({ error: parsedUserId.error.issues[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 });
if (!parsed.success) return NextResponse.json({ error: parsed.error.issues[0].message }, { status: 400 });
const { type, duration, notes, reasons, miiReasons } = parsed.data;
const expiresAt = type === "TEMP_EXILE" ? dayjs().add(duration, "days").toDate() : null;
@ -77,7 +74,7 @@ export async function DELETE(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
const parsedPunishmentId = idSchema.safeParse(searchParams.get("id"));
if (!parsedPunishmentId.success) return NextResponse.json({ error: parsedPunishmentId.error.errors[0].message }, { status: 400 });
if (!parsedPunishmentId.success) return NextResponse.json({ error: parsedPunishmentId.error.issues[0].message }, { status: 400 });
const punishmentId = parsedPunishmentId.data;
await prisma.punishment.delete({

View file

@ -18,7 +18,7 @@ export async function PATCH(request: NextRequest) {
if (!displayName) return rateLimit.sendResponse({ error: "New display name is required" }, 400);
const validation = displayNameSchema.safeParse(displayName);
if (!validation.success) return rateLimit.sendResponse({ error: validation.error.errors[0].message }, 400);
if (!validation.success) return rateLimit.sendResponse({ error: validation.error.issues[0].message }, 400);
// Check for inappropriate words
if (profanity.exists(displayName)) return rateLimit.sendResponse({ error: "Display name contains inappropriate words" }, 400);

View file

@ -40,7 +40,7 @@ export async function PATCH(request: NextRequest) {
image: formData.get("image"),
});
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.errors[0].message }, 400);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400);
const { image } = parsed.data;
// If there is no image, set the profile picture to the guest image

View file

@ -29,7 +29,7 @@ export async function PATCH(request: NextRequest) {
}
const validation = usernameSchema.safeParse(username);
if (!validation.success) return rateLimit.sendResponse({ error: validation.error.errors[0].message }, 400);
if (!validation.success) return rateLimit.sendResponse({ error: validation.error.issues[0].message }, 400);
// Check for inappropriate words
if (profanity.exists(username)) return rateLimit.sendResponse({ error: "Username contains inappropriate words" }, 400);

View file

@ -20,7 +20,7 @@ export async function DELETE(request: NextRequest, { params }: { params: Promise
const { id: slugId } = await params;
const parsed = idSchema.safeParse(slugId);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.errors[0].message }, 400);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400);
const miiId = parsed.data;
// Check ownership of Mii

View file

@ -37,7 +37,7 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
// Get Mii ID
const { id: slugId } = await params;
const parsedId = idSchema.safeParse(slugId);
if (!parsedId.success) return rateLimit.sendResponse({ error: parsedId.error.errors[0].message }, 400);
if (!parsedId.success) return rateLimit.sendResponse({ error: parsedId.error.issues[0].message }, 400);
const miiId = parsedId.data;
// Check ownership of Mii
@ -78,7 +78,7 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
image3: formData.get("image3"),
});
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.errors[0].message }, 400);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400);
const { name, tags, description, image1, image2, image3 } = parsed.data;
// Validate image files

View file

@ -15,7 +15,7 @@ export async function PATCH(request: NextRequest, { params }: { params: Promise<
const { id: slugId } = await params;
const parsed = idSchema.safeParse(slugId);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.errors[0].message }, 400);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400);
const miiId = parsed.data;
const result = await prisma.$transaction(async (tx) => {

View file

@ -8,8 +8,8 @@ import { RateLimit } from "@/lib/rate-limit";
import { MiiWithUsername } from "@/types";
const reportSchema = z.object({
id: z.coerce.number({ message: "ID must be a number" }).int({ message: "ID must be an integer" }).positive({ message: "ID must be valid" }),
type: z.enum(["mii", "user"], { message: "Type must be either 'mii' or 'user'" }),
id: z.coerce.number({ error: "ID must be a number" }).int({ error: "ID must be an integer" }).positive({ error: "ID must be valid" }),
type: z.enum(["mii", "user"], { error: "Type must be either 'mii' or 'user'" }),
reason: z.enum(["inappropriate", "spam", "copyright", "other"], {
message: "Reason must be either 'inappropriate', 'spam', 'copyright', or 'other'",
}),
@ -27,7 +27,7 @@ export async function POST(request: NextRequest) {
const body = await request.json();
const parsed = reportSchema.safeParse(body);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.errors[0].message }, 400);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400);
const { id, type, reason, notes } = parsed.data;
let mii: MiiWithUsername | null = null;

View file

@ -25,9 +25,7 @@ const submitSchema = z.object({
name: nameSchema,
tags: tagsSchema,
description: z.string().trim().max(256).optional(),
qrBytesRaw: z
.array(z.number(), { required_error: "A QR code is required" })
.length(372, { message: "QR code size is not a valid Tomodachi Life QR code" }),
qrBytesRaw: z.array(z.number(), { error: "A QR code is required" }).length(372, { error: "QR code size is not a valid Tomodachi Life QR code" }),
image1: z.union([z.instanceof(File), z.any()]).optional(),
image2: z.union([z.instanceof(File), z.any()]).optional(),
image3: z.union([z.instanceof(File), z.any()]).optional(),
@ -67,7 +65,7 @@ export async function POST(request: NextRequest) {
image3: formData.get("image3"),
});
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.errors[0].message }, 400);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400);
const { name: uncensoredName, tags: uncensoredTags, description: uncensoredDescription, qrBytesRaw, image1, image2, image3 } = parsed.data;
// Censor potential inappropriate words

View file

@ -25,11 +25,11 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
const { id: slugId } = await params;
const parsed = idSchema.safeParse(slugId);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.errors[0].message }, 400);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400);
const miiId = parsed.data;
const searchParamsParsed = searchParamsSchema.safeParse(Object.fromEntries(request.nextUrl.searchParams));
if (!searchParamsParsed.success) return rateLimit.sendResponse({ error: searchParamsParsed.error.errors[0].message }, 400);
if (!searchParamsParsed.success) return rateLimit.sendResponse({ error: searchParamsParsed.error.issues[0].message }, 400);
const { type: imageType } = searchParamsParsed.data;
const fileExtension = imageType === "metadata" ? ".png" : ".webp";

View file

@ -13,7 +13,7 @@ export async function GET(request: NextRequest, { params }: { params: Promise<{
const { id: slugId } = await params;
const parsed = idSchema.safeParse(slugId);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.errors[0].message }, 400);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400);
const userId = parsed.data;
const filePath = path.join(process.cwd(), "uploads", "user", `${userId}.webp`);