import fs from 'fs' import { NextRequest, NextResponse } from 'next/server' import { getLibrary, resolveLibraryRoot, resolveAndJail } from '@/lib/libraries' import { scanDirectory, scanDirectoryRecursive } from '@/lib/files' import { requireLibraryAccess, requireAdmin } from '@/lib/auth' import { removeAllAssignmentsForItem } from '@/lib/tags' import { getDb } from '@/lib/db' export async function GET(request: NextRequest) { const { searchParams } = request.nextUrl const libraryId = searchParams.get('libraryId') const subpath = searchParams.get('path') ?? '' if (!libraryId) { return NextResponse.json({ error: 'Missing libraryId' }, { status: 400 }) } const auth = await requireLibraryAccess(request, libraryId) if (auth instanceof NextResponse) return auth const library = getLibrary(libraryId) if (!library) { return NextResponse.json({ error: 'Library not found' }, { status: 404 }) } if (library.type !== 'mixed') { return NextResponse.json({ error: 'Library is not a mixed library' }, { status: 400 }) } const root = resolveLibraryRoot(library) const recursive = request.nextUrl.searchParams.get('recursive') === 'true' const listing = recursive ? scanDirectoryRecursive(root, libraryId, subpath) : scanDirectory(root, libraryId, subpath) return NextResponse.json(listing) } export async function DELETE(request: NextRequest) { const auth = await requireAdmin(request) if (auth instanceof NextResponse) return auth const { searchParams } = request.nextUrl const libraryId = searchParams.get('libraryId') const itemPath = searchParams.get('path') if (!libraryId || !itemPath) { return NextResponse.json({ error: 'Missing libraryId or path' }, { status: 400 }) } const library = getLibrary(libraryId) if (!library) { return NextResponse.json({ error: 'Library not found' }, { status: 404 }) } if (library.type !== 'mixed') { return NextResponse.json({ error: 'Library is not a mixed library' }, { status: 400 }) } const root = resolveLibraryRoot(library) let absPath: string try { absPath = resolveAndJail(root, itemPath) } catch { return NextResponse.json({ error: 'Invalid path' }, { status: 400 }) } try { const stat = fs.statSync(absPath) if (stat.isDirectory()) { fs.rmSync(absPath, { recursive: true, force: true }) } else { fs.unlinkSync(absPath) } } catch { return NextResponse.json({ error: 'Failed to delete' }, { status: 500 }) } const db = getDb() const itemKey = `${libraryId}:mixed_file:${encodeURIComponent(itemPath)}` removeAllAssignmentsForItem(itemKey) db.prepare('DELETE FROM media_items WHERE item_key = ?').run(itemKey) // For directories, also clean up children const prefix = `${libraryId}:mixed_file:${encodeURIComponent(itemPath + '/')}` const children = db.prepare('SELECT item_key FROM media_items WHERE item_key LIKE ?').all(`${prefix}%`) as { item_key: string }[] for (const child of children) { removeAllAssignmentsForItem(child.item_key) } db.prepare('DELETE FROM media_items WHERE item_key LIKE ?').run(`${prefix}%`) return new NextResponse(null, { status: 204 }) }