fix: move mii images to new uploads directory and add route to access it
This commit is contained in:
parent
594309d22d
commit
eea3df283c
12 changed files with 73 additions and 33 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -41,4 +41,5 @@ yarn-error.log*
|
|||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
||||
public/mii/
|
||||
# tomodachi-share
|
||||
uploads/
|
||||
|
|
@ -20,7 +20,6 @@ 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);
|
||||
const miiId = parsed.data;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ 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);
|
||||
const miiId = parsedId.data;
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ 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);
|
||||
const miiId = parsed.data;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import { convertQrCode } from "@/lib/qr-codes";
|
|||
import Mii from "@/lib/mii.js/mii";
|
||||
import TomodachiLifeMii from "@/lib/tomodachi-life-mii";
|
||||
|
||||
const uploadsDirectory = path.join(process.cwd(), "public", "mii");
|
||||
const uploadsDirectory = path.join(process.cwd(), "uploads");
|
||||
|
||||
const submitSchema = z.object({
|
||||
name: nameSchema,
|
||||
|
|
|
|||
|
|
@ -6,15 +6,15 @@ import { prisma } from "@/lib/prisma";
|
|||
import EditForm from "@/components/submit-form/edit-form";
|
||||
|
||||
interface Props {
|
||||
params: Promise<{ slug: string }>;
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const { id } = await params;
|
||||
|
||||
const mii = await prisma.mii.findUnique({
|
||||
where: {
|
||||
id: Number(slug),
|
||||
id: Number(id),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -29,12 +29,12 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|||
}
|
||||
|
||||
export default async function MiiPage({ params }: Props) {
|
||||
const { slug } = await params;
|
||||
const { id } = await params;
|
||||
const session = await auth();
|
||||
|
||||
const mii = await prisma.mii.findUnique({
|
||||
where: {
|
||||
id: Number(slug),
|
||||
id: Number(id),
|
||||
},
|
||||
include: {
|
||||
_count: {
|
||||
40
src/app/mii/[id]/image/route.ts
Normal file
40
src/app/mii/[id]/image/route.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
|
||||
import fs from "fs/promises";
|
||||
import path from "path";
|
||||
|
||||
import { idSchema } from "@/lib/schemas";
|
||||
import { RateLimit } from "@/lib/rate-limit";
|
||||
|
||||
const searchParamsSchema = z.object({
|
||||
type: z
|
||||
.enum(["mii", "qr-code", "image0", "image1", "image2"], {
|
||||
message: "Image type must be either 'mii', 'qr-code' or 'image[number from 0 to 2]'",
|
||||
})
|
||||
.default("mii"),
|
||||
});
|
||||
|
||||
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
|
||||
const rateLimit = new RateLimit(request, 200);
|
||||
const check = await rateLimit.handle();
|
||||
if (check) return check;
|
||||
|
||||
const { id: slugId } = await params;
|
||||
const parsed = idSchema.safeParse(slugId);
|
||||
if (!parsed.success) return rateLimit.sendResponse({ error: parsed.error.errors[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);
|
||||
const { type: imageType } = searchParamsParsed.data;
|
||||
|
||||
const filePath = path.join(process.cwd(), "uploads", miiId.toString(), `${imageType}.webp`);
|
||||
|
||||
try {
|
||||
const buffer = await fs.readFile(filePath);
|
||||
return new NextResponse(buffer);
|
||||
} catch {
|
||||
return rateLimit.sendResponse({ success: false, error: "Image not found" }, 404);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,15 +14,15 @@ import DeleteMiiButton from "@/components/delete-mii";
|
|||
import ScanTutorialButton from "@/components/tutorial/scan";
|
||||
|
||||
interface Props {
|
||||
params: Promise<{ slug: string }>;
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const { id } = await params;
|
||||
|
||||
const mii = await prisma.mii.findUnique({
|
||||
where: {
|
||||
id: Number(slug),
|
||||
id: Number(id),
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
|
|
@ -39,8 +39,8 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|||
// Bots get redirected anyways
|
||||
if (!mii) return {};
|
||||
|
||||
const miiImageUrl = `/mii/${mii.id}/mii.webp`;
|
||||
const qrCodeUrl = `/mii/${mii.id}/qrcode.webp`;
|
||||
const miiImageUrl = `/mii/${mii.id}/image?type=mii`;
|
||||
const qrCodeUrl = `/mii/${mii.id}/image?type=qr-code`;
|
||||
|
||||
const username = `@${mii.user.username}`;
|
||||
|
||||
|
|
@ -73,12 +73,12 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|||
}
|
||||
|
||||
export default async function MiiPage({ params }: Props) {
|
||||
const { slug } = await params;
|
||||
const { id } = await params;
|
||||
const session = await auth();
|
||||
|
||||
const mii = await prisma.mii.findUnique({
|
||||
where: {
|
||||
id: Number(slug),
|
||||
id: Number(id),
|
||||
},
|
||||
include: {
|
||||
user: {
|
||||
|
|
@ -103,9 +103,9 @@ export default async function MiiPage({ params }: Props) {
|
|||
if (!mii) redirect("/404");
|
||||
|
||||
const images = [
|
||||
`/mii/${mii.id}/mii.webp`,
|
||||
`/mii/${mii.id}/qr-code.webp`,
|
||||
...Array.from({ length: mii.imageCount }, (_, index) => `/mii/${mii.id}/image${index}.webp`),
|
||||
`/mii/${mii.id}/image?type=mii`,
|
||||
`/mii/${mii.id}/image?type=qr-code`,
|
||||
...Array.from({ length: mii.imageCount }, (_, index) => `/mii/${mii.id}/image?type=image${index}`),
|
||||
];
|
||||
|
||||
return (
|
||||
|
|
@ -11,15 +11,15 @@ import { prisma } from "@/lib/prisma";
|
|||
import MiiList from "@/components/mii-list";
|
||||
|
||||
interface Props {
|
||||
params: Promise<{ slug: string }>;
|
||||
params: Promise<{ id: string }>;
|
||||
}
|
||||
|
||||
export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const { id } = await params;
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: Number(slug),
|
||||
id: Number(id),
|
||||
},
|
||||
include: {
|
||||
_count: {
|
||||
|
|
@ -68,17 +68,17 @@ export async function generateMetadata({ params }: Props): Promise<Metadata> {
|
|||
|
||||
export default async function ProfilePage({ params }: Props) {
|
||||
const session = await auth();
|
||||
const { slug } = await params;
|
||||
const { id } = await params;
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: Number(slug),
|
||||
id: Number(id),
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) redirect("/404");
|
||||
|
||||
const likedMiis = await prisma.like.count({ where: { userId: Number(slug) } });
|
||||
const likedMiis = await prisma.like.count({ where: { userId: Number(id) } });
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
@ -102,7 +102,7 @@ export default async function ProfilePage({ params }: Props) {
|
|||
Created: {user?.createdAt.toLocaleDateString("en-GB", { month: "long", day: "2-digit", year: "numeric" })}
|
||||
</h4>
|
||||
|
||||
{session?.user.id == slug && (
|
||||
{session?.user.id == id && (
|
||||
<Link href="/profile/settings" className="pill button absolute right-0 bottom-0 !px-4">
|
||||
<Icon icon="material-symbols:settings-rounded" className="text-2xl mr-2" />
|
||||
<span>Settings</span>
|
||||
|
|
@ -78,7 +78,7 @@ export default function DeleteMiiButton({ miiId, miiName, likes }: Props) {
|
|||
<p className="text-sm text-zinc-500">Are you sure? This will delete your Mii permanently. This action cannot be undone.</p>
|
||||
|
||||
<div className="bg-orange-100 rounded-xl border-2 border-orange-400 mt-4 flex">
|
||||
<Image src={`/mii/${miiId}/mii.webp`} alt="mii image" width={128} height={128} />
|
||||
<Image src={`/mii/${miiId}/image?type=mii`} alt="mii image" width={128} height={128} />
|
||||
<div className="p-4">
|
||||
<p className="text-xl font-bold line-clamp-1" title={miiName}>
|
||||
{miiName}
|
||||
|
|
|
|||
|
|
@ -83,9 +83,9 @@ export default function MiiList({ isLoggedIn, userId, sessionUserId }: Props) {
|
|||
>
|
||||
<Carousel
|
||||
images={[
|
||||
`/mii/${mii.id}/mii.webp`,
|
||||
`/mii/${mii.id}/qr-code.webp`,
|
||||
...Array.from({ length: mii.imageCount }, (_, index) => `/mii/${mii.id}/image${index}.webp`),
|
||||
`/mii/${mii.id}/image?type=mii`,
|
||||
`/mii/${mii.id}/image?type=qr-code`,
|
||||
...Array.from({ length: mii.imageCount }, (_, index) => `/mii/${mii.id}/image?type=image${index}`),
|
||||
]}
|
||||
/>
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ export default function EditForm({ mii, likes }: Props) {
|
|||
try {
|
||||
const existing = await Promise.all(
|
||||
Array.from({ length: mii.imageCount }, async (_, index) => {
|
||||
const path = `/mii/${mii.id}/image${index}.webp`;
|
||||
const path = `/mii/${mii.id}/image?type=image${index}`;
|
||||
const response = await fetch(path);
|
||||
const blob = await response.blob();
|
||||
|
||||
|
|
@ -112,7 +112,9 @@ export default function EditForm({ mii, likes }: Props) {
|
|||
<form className="flex justify-center gap-4 w-full max-lg:flex-col max-lg:items-center">
|
||||
<div className="flex justify-center">
|
||||
<div className="w-[18.75rem] h-min flex flex-col bg-zinc-50 rounded-3xl border-2 border-zinc-300 shadow-lg p-3">
|
||||
<Carousel images={[`/mii/${mii.id}/mii.webp`, `/mii/${mii.id}/qr-code.webp`, ...files.map((file) => URL.createObjectURL(file))]} />
|
||||
<Carousel
|
||||
images={[`/mii/${mii.id}/image?type=mii`, `/mii/${mii.id}/image?type=qr-code`, ...files.map((file) => URL.createObjectURL(file))]}
|
||||
/>
|
||||
|
||||
<div className="p-4 flex flex-col gap-1 h-full">
|
||||
<h1 className="font-bold text-2xl line-clamp-1" title={name}>
|
||||
|
|
|
|||
Loading…
Reference in a new issue