mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-02 20:30:10 +00:00
Add admin request deletion with soft delete and cleanup
Implements admin ability to delete requests with soft delete, media file cleanup, and seeding-aware torrent management. Adds new API endpoint, frontend confirmation dialog, and request actions dropdown. Updates database schema with deletedAt and deletedBy fields, and ensures all queries filter out deleted requests. Documentation added for feature and user flow.
This commit is contained in:
@@ -26,8 +26,11 @@ export async function GET(
|
||||
|
||||
const { id } = await params;
|
||||
|
||||
const requestRecord = await prisma.request.findUnique({
|
||||
where: { id },
|
||||
const requestRecord = await prisma.request.findFirst({
|
||||
where: {
|
||||
id,
|
||||
deletedAt: null, // Only show active requests
|
||||
},
|
||||
include: {
|
||||
audiobook: true,
|
||||
user: {
|
||||
@@ -100,13 +103,16 @@ export async function PATCH(
|
||||
const body = await req.json();
|
||||
const { action } = body;
|
||||
|
||||
const requestRecord = await prisma.request.findUnique({
|
||||
where: { id },
|
||||
const requestRecord = await prisma.request.findFirst({
|
||||
where: {
|
||||
id,
|
||||
deletedAt: null, // Only allow updates to active requests
|
||||
},
|
||||
});
|
||||
|
||||
if (!requestRecord) {
|
||||
return NextResponse.json(
|
||||
{ error: 'NotFound', message: 'Request not found' },
|
||||
{ error: 'NotFound', message: 'Request not found or already deleted' },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
@@ -161,8 +167,11 @@ export async function PATCH(
|
||||
|
||||
if (requestRecord.status === 'warn' || requestRecord.status === 'awaiting_import') {
|
||||
// Retry import
|
||||
const requestWithData = await prisma.request.findUnique({
|
||||
where: { id },
|
||||
const requestWithData = await prisma.request.findFirst({
|
||||
where: {
|
||||
id,
|
||||
deletedAt: null,
|
||||
},
|
||||
include: {
|
||||
audiobook: true,
|
||||
downloadHistory: {
|
||||
@@ -213,8 +222,11 @@ export async function PATCH(
|
||||
jobType = 'import';
|
||||
} else {
|
||||
// Retry search
|
||||
const requestWithData = await prisma.request.findUnique({
|
||||
where: { id },
|
||||
const requestWithData = await prisma.request.findFirst({
|
||||
where: {
|
||||
id,
|
||||
deletedAt: null,
|
||||
},
|
||||
include: {
|
||||
audiobook: true,
|
||||
},
|
||||
|
||||
@@ -80,13 +80,12 @@ export async function POST(request: NextRequest) {
|
||||
});
|
||||
}
|
||||
|
||||
// Check if user already has a request for this audiobook
|
||||
const existingRequest = await prisma.request.findUnique({
|
||||
// Check if user already has an active (non-deleted) request for this audiobook
|
||||
const existingRequest = await prisma.request.findFirst({
|
||||
where: {
|
||||
userId_audiobookId: {
|
||||
userId: req.user.id,
|
||||
audiobookId: audiobookRecord.id,
|
||||
},
|
||||
userId: req.user.id,
|
||||
audiobookId: audiobookRecord.id,
|
||||
deletedAt: null, // Only check active requests
|
||||
},
|
||||
});
|
||||
|
||||
@@ -112,12 +111,15 @@ export async function POST(request: NextRequest) {
|
||||
});
|
||||
}
|
||||
|
||||
// Create request
|
||||
// Check if we should skip auto-search (for interactive search)
|
||||
const skipAutoSearch = req.nextUrl.searchParams.get('skipAutoSearch') === 'true';
|
||||
|
||||
// Create request with appropriate status
|
||||
const newRequest = await prisma.request.create({
|
||||
data: {
|
||||
userId: req.user.id,
|
||||
audiobookId: audiobookRecord.id,
|
||||
status: 'pending',
|
||||
status: skipAutoSearch ? 'awaiting_search' : 'pending',
|
||||
progress: 0,
|
||||
},
|
||||
include: {
|
||||
@@ -131,13 +133,15 @@ export async function POST(request: NextRequest) {
|
||||
},
|
||||
});
|
||||
|
||||
// Trigger search job
|
||||
const jobQueue = getJobQueueService();
|
||||
await jobQueue.addSearchJob(newRequest.id, {
|
||||
id: audiobookRecord.id,
|
||||
title: audiobookRecord.title,
|
||||
author: audiobookRecord.author,
|
||||
});
|
||||
// Trigger search job only if not skipped
|
||||
if (!skipAutoSearch) {
|
||||
const jobQueue = getJobQueueService();
|
||||
await jobQueue.addSearchJob(newRequest.id, {
|
||||
id: audiobookRecord.id,
|
||||
title: audiobookRecord.title,
|
||||
author: audiobookRecord.author,
|
||||
});
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
@@ -194,6 +198,8 @@ export async function GET(request: NextRequest) {
|
||||
if (status) {
|
||||
where.status = status;
|
||||
}
|
||||
// Only show active (non-deleted) requests
|
||||
where.deletedAt = null;
|
||||
|
||||
const requests = await prisma.request.findMany({
|
||||
where,
|
||||
|
||||
Reference in New Issue
Block a user