91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
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 })
|
|
}
|