feat(web): Library Management — delete an album (files + record)
First Library Management feature. On the Library album modal, "Delete album" (two-step confirm) removes the album: - deletes the folder on disk (guarded to only ever remove a path strictly inside the library root, LYRA_LIBRARY_ROOT, default /music), - drops the LibraryItem, - sets the matching MonitoredRelease monitored=false + currentQualityClass=null so the monitor won't immediately re-download it (re-Want later to restore). Requires the web container to see the library, so docker-compose now bind-mounts MUSIC_DIR at /music (rw) for web too. New DELETE /api/library/[id] (auth-gated, path-guarded). web 157 tests (delete happy-path + 404 + outside-root guard). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import { rm } from "node:fs/promises";
|
||||
import { resolve, sep } from "node:path";
|
||||
import { prisma } from "@/lib/db";
|
||||
|
||||
// DELETE /api/library/[id] — remove an owned album: delete its folder on disk, drop the
|
||||
// LibraryItem, and stop monitoring the matching release so the monitor doesn't re-grab it.
|
||||
export async function DELETE(_request: Request, { params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
const item = await prisma.libraryItem.findUnique({ where: { id } });
|
||||
if (!item) return Response.json({ error: "not found" }, { status: 404 });
|
||||
|
||||
// Guard: only ever delete a folder strictly inside the library root.
|
||||
const root = resolve(process.env.LYRA_LIBRARY_ROOT ?? "/music");
|
||||
const target = resolve(item.path);
|
||||
if (target === root || !target.startsWith(root + sep)) {
|
||||
return Response.json({ error: "refusing to delete a path outside the library" }, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
await rm(target, { recursive: true, force: true });
|
||||
} catch {
|
||||
return Response.json({ error: "failed to delete files on disk" }, { status: 500 });
|
||||
}
|
||||
|
||||
// Drop the record + stop monitoring so auto-monitor won't immediately re-download it. The
|
||||
// release stays in the artist's discography (dormant) and can be re-wanted later.
|
||||
await prisma.$transaction([
|
||||
prisma.libraryItem.delete({ where: { id } }),
|
||||
prisma.monitoredRelease.updateMany({
|
||||
where: { artistName: item.artist, album: item.album },
|
||||
data: { monitored: false, currentQualityClass: null },
|
||||
}),
|
||||
]);
|
||||
|
||||
return Response.json({ ok: true });
|
||||
}
|
||||
Reference in New Issue
Block a user