Wrap song updates and deletes in database transactions for consistency
This commit is contained in:
@@ -469,51 +469,55 @@ export async function PUT(request: Request) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle SpecialSong relations separately
|
// Execute all database write operations in a transaction to ensure consistency
|
||||||
if (effectiveSpecialIds !== undefined) {
|
const updatedSong = await prisma.$transaction(async (tx) => {
|
||||||
// First, get current special assignments
|
// Handle SpecialSong relations separately
|
||||||
const currentSpecials = await prisma.specialSong.findMany({
|
if (effectiveSpecialIds !== undefined) {
|
||||||
where: { songId: Number(id) }
|
// First, get current special assignments (within transaction)
|
||||||
});
|
const currentSpecials = await tx.specialSong.findMany({
|
||||||
|
where: { songId: Number(id) }
|
||||||
const currentSpecialIds = currentSpecials.map(ss => ss.specialId);
|
|
||||||
const newSpecialIds = effectiveSpecialIds as number[];
|
|
||||||
|
|
||||||
// Delete removed specials
|
|
||||||
const toDelete = currentSpecialIds.filter(sid => !newSpecialIds.includes(sid));
|
|
||||||
if (toDelete.length > 0) {
|
|
||||||
await prisma.specialSong.deleteMany({
|
|
||||||
where: {
|
|
||||||
songId: Number(id),
|
|
||||||
specialId: { in: toDelete }
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
// Add new specials
|
const currentSpecialIds = currentSpecials.map(ss => ss.specialId);
|
||||||
const toAdd = newSpecialIds.filter(sid => !currentSpecialIds.includes(sid));
|
const newSpecialIds = effectiveSpecialIds as number[];
|
||||||
if (toAdd.length > 0) {
|
|
||||||
await prisma.specialSong.createMany({
|
|
||||||
data: toAdd.map(specialId => ({
|
|
||||||
songId: Number(id),
|
|
||||||
specialId,
|
|
||||||
startTime: 0
|
|
||||||
}))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updatedSong = await prisma.song.update({
|
// Delete removed specials
|
||||||
where: { id: Number(id) },
|
const toDelete = currentSpecialIds.filter(sid => !newSpecialIds.includes(sid));
|
||||||
data,
|
if (toDelete.length > 0) {
|
||||||
include: {
|
await tx.specialSong.deleteMany({
|
||||||
genres: true,
|
where: {
|
||||||
specials: {
|
songId: Number(id),
|
||||||
include: {
|
specialId: { in: toDelete }
|
||||||
special: true
|
}
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add new specials
|
||||||
|
const toAdd = newSpecialIds.filter(sid => !currentSpecialIds.includes(sid));
|
||||||
|
if (toAdd.length > 0) {
|
||||||
|
await tx.specialSong.createMany({
|
||||||
|
data: toAdd.map(specialId => ({
|
||||||
|
songId: Number(id),
|
||||||
|
specialId,
|
||||||
|
startTime: 0
|
||||||
|
}))
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update song (this also handles genre relations via Prisma's set operation)
|
||||||
|
return await tx.song.update({
|
||||||
|
where: { id: Number(id) },
|
||||||
|
data,
|
||||||
|
include: {
|
||||||
|
genres: true,
|
||||||
|
specials: {
|
||||||
|
include: {
|
||||||
|
special: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json(updatedSong);
|
return NextResponse.json(updatedSong);
|
||||||
@@ -559,7 +563,7 @@ export async function DELETE(request: Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete file
|
// Delete files first (outside transaction, as file system operations can't be rolled back)
|
||||||
const filePath = path.join(process.cwd(), 'public/uploads', song.filename);
|
const filePath = path.join(process.cwd(), 'public/uploads', song.filename);
|
||||||
try {
|
try {
|
||||||
await unlink(filePath);
|
await unlink(filePath);
|
||||||
@@ -578,9 +582,11 @@ export async function DELETE(request: Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete from database (will cascade delete related puzzles)
|
// Delete from database in transaction (will cascade delete related puzzles, SpecialSong, etc.)
|
||||||
await prisma.song.delete({
|
await prisma.$transaction(async (tx) => {
|
||||||
where: { id: Number(id) },
|
await tx.song.delete({
|
||||||
|
where: { id: Number(id) },
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json({ success: true });
|
return NextResponse.json({ success: true });
|
||||||
|
|||||||
Reference in New Issue
Block a user