fix: profile picture points to mii pictures

i copy pasted the wrong thing
This commit is contained in:
trafficlunar 2026-04-19 22:01:24 +01:00
parent 7f87a42b11
commit 61cbdad812

View file

@ -1,115 +1,27 @@
import { NextRequest } from "next/server"; import { NextRequest, NextResponse } from "next/server";
import { Prisma } from "@prisma/client";
import fs from "fs/promises"; import fs from "fs/promises";
import path from "path"; import path from "path";
import { z } from "zod";
import { idSchema } from "@tomodachi-share/shared/schemas"; import { idSchema } from "@tomodachi-share/shared/schemas";
import { RateLimit } from "@/lib/rate-limit"; import { RateLimit } from "@/lib/rate-limit";
import { generateMetadataImage } from "@/lib/images";
import { prisma } from "@/lib/prisma";
const searchParamsSchema = z.object({
type: z
.enum(["mii", "qr-code", "features", "image0", "image1", "image2", "metadata"], {
message: "Image type must be either 'mii', 'qr-code', 'features', 'image[number from 0 to 2]' or 'metadata'",
})
.default("mii"),
});
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
const rateLimit = new RateLimit(request, 200, "/mii/image"); const rateLimit = new RateLimit(request, 16, "/profile/picture");
const check = await rateLimit.handle(); const check = await rateLimit.handle();
if (check) return check; if (check) return check;
const { id: slugId } = await params; const { id: slugId } = await params;
const parsed = idSchema.safeParse(slugId); const parsed = idSchema.safeParse(slugId);
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400); if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.issues[0].message }, 400);
const miiId = parsed.data; const userId = parsed.data;
const searchParamsParsed = searchParamsSchema.safeParse(Object.fromEntries(request.nextUrl.searchParams)); const filePath = path.join(process.cwd(), "uploads", "user", `${userId}.png`);
if (!searchParamsParsed.success) return rateLimit.sendResponse({ error: searchParamsParsed.error.issues[0].message }, 400);
const { type: imageType } = searchParamsParsed.data;
const filePath = path.join(process.cwd(), "uploads", "mii", miiId.toString(), `${imageType}.png`);
let buffer: Buffer | undefined;
// Only find Mii if image type is 'metadata'
let mii: Prisma.MiiGetPayload<{
include: {
user: {
select: {
name: true;
};
};
};
}> | null = null;
if (imageType === "metadata") {
mii = await prisma.mii.findUnique({
where: {
id: miiId,
},
include: {
user: {
select: {
name: true,
},
},
},
});
if (!mii) {
return rateLimit.sendResponse({ error: "Mii not found" }, 404);
}
}
try { try {
// Try to read file const buffer = await fs.readFile(filePath);
buffer = await fs.readFile(filePath); return new NextResponse(new Uint8Array(buffer)); // convert to Uint8Array due to weird types issue
} catch { } catch {
// If the readFile() fails, that probably means it doesn't exist
if (imageType === "metadata" && mii) {
// Metadata images were added after 1274 Miis were submitted, so we generate it on-the-fly
console.log(`Metadata image not found for mii ID ${miiId}, generating metadata image...`);
const { buffer: metadataBuffer, error, status } = await generateMetadataImage(mii, mii.user.name!);
if (error) {
return rateLimit.sendResponse({ error }, status);
}
buffer = metadataBuffer;
} else {
return rateLimit.sendResponse({ error: "Image not found" }, 404); return rateLimit.sendResponse({ error: "Image not found" }, 404);
} }
} }
if (!buffer) return rateLimit.sendResponse({ error: "Image not found" }, 404);
// Set the file name for the metadata image in the response for SEO
if (mii && imageType === "metadata") {
const slugify = (str: string) =>
str
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-") // replace non-alphanumeric with hyphens
.replace(/^-+|-+$/g, "");
const name = slugify(mii.name);
const tags = mii.tags.map(slugify).join("-");
const filename = `${name}-mii-${tags}.png`;
return rateLimit.sendResponse(buffer, 200, {
"Content-Type": "image/png",
"Content-Disposition": `inline; filename="${filename}"`,
"Cache-Control": "public, max-age=31536000",
});
}
return rateLimit.sendResponse(buffer, 200, {
"Content-Type": "image/png",
"X-Robots-Tag": "noindex, noimageindex, nofollow",
"Cache-Control": "public, max-age=60, stale-while-revalidate=30",
});
}