lomavuokraus/app/api/listings/[id]/images/[imageId]/route.ts
Tero Halla-aho bc8aa87bdc
Some checks failed
CI / checks (push) Has been cancelled
CI / checks (pull_request) Has been cancelled
Allow owners to delete listing images
2025-12-19 22:11:53 +02:00

71 lines
2.5 KiB
TypeScript

import { Role } from '@prisma/client';
import { NextResponse } from 'next/server';
import { prisma } from '../../../../../../lib/prisma';
import { requireAuth } from '../../../../../../lib/jwt';
export async function DELETE(req: Request, { params }: { params: { id: string; imageId: string } }) {
try {
const auth = await requireAuth(req);
const listing = await prisma.listing.findUnique({
where: { id: params.id, removedAt: null },
select: {
id: true,
ownerId: true,
status: true,
images: { orderBy: { order: 'asc' }, select: { id: true, isCover: true, order: true } },
},
});
if (!listing) {
return NextResponse.json({ error: 'Listing not found' }, { status: 404 });
}
const isOwner = listing.ownerId === auth.userId;
const isAdmin = auth.role === Role.ADMIN;
if (!isOwner && !isAdmin) {
return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
}
const targetImage = listing.images.find((img) => img.id === params.imageId);
if (!targetImage) {
return NextResponse.json({ error: 'Image not found' }, { status: 404 });
}
if (listing.images.length <= 1) {
return NextResponse.json({ error: 'At least one image is required' }, { status: 400 });
}
const remaining = listing.images.filter((img) => img.id !== params.imageId);
const newCoverId = remaining.find((img) => img.isCover)?.id ?? remaining[0]?.id ?? null;
await prisma.$transaction([
prisma.listingImage.delete({ where: { id: params.imageId } }),
...remaining.map((img, idx) =>
prisma.listingImage.update({
where: { id: img.id },
data: { order: idx + 1, isCover: newCoverId ? img.id === newCoverId : img.isCover },
}),
),
]);
const updated = await prisma.listing.findUnique({
where: { id: listing.id },
select: {
images: {
orderBy: { order: 'asc' },
select: { id: true, url: true, altText: true, order: true, isCover: true, size: true, mimeType: true },
},
},
});
return NextResponse.json({ ok: true, images: updated?.images ?? [] });
} catch (error: any) {
console.error('Delete listing image error', error);
if (String(error).includes('Unauthorized')) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
return NextResponse.json({ error: 'Failed to delete image' }, { status: 500 });
}
}
export const dynamic = 'force-dynamic';