Files
ReadMeABook/src/app/api/admin/blocklist/[id]/route.ts
T
kikootwo b1492fc32e Add release blocklist feature
Introduce a per-request release blocklist to auto-block permanently failing releases and provide admin management. Changes include:

- Database: add BlockedRelease model (blocked_releases) to Prisma schema with unique (requestId, releaseKey) and indexes; documented in backend database docs.
- Service & utils: new blocklist.service, release-key and filter helpers for normalization and matching; processors updated to emit auto-blocks (monitor-download, organize-files, search processors, RSS).
- HTTP API: add admin endpoints GET/DELETE /api/admin/blocklist, DELETE /api/admin/blocklist/[id], and GET /api/admin/blocklist/by-request/[requestId].
- Admin UI: new /admin/blocklist page and numerous React components (toolbar, filters, table, rows, pagination, skeleton, chips, date picker) with URL-driven state hook and per-row unblock UX.
- Tests: add unit/integration tests for service, routes, utils, and updated processor tests.

The blocklist is idempotent (upsert), filters search results before ranking (interactive search shows badges only), and admin-only APIs require auth. This commit wires docs, API, DB, frontend and tests for the new feature.
2026-05-18 12:15:51 -04:00

52 lines
1.7 KiB
TypeScript

/**
* Component: Admin Blocklist — Single Unblock
* Documentation: documentation/admin-features/release-blocklist.md
*
* DELETE /api/admin/blocklist/[id] → removes a single blocklist entry.
*/
import { NextRequest, NextResponse } from 'next/server';
import { requireAuth, requireAdmin, AuthenticatedRequest } from '@/lib/middleware/auth';
import { Prisma } from '@/generated/prisma';
import { RMABLogger } from '@/lib/utils/logger';
import { removeBlock } from '@/lib/services/blocklist.service';
const logger = RMABLogger.create('API.Admin.Blocklist.Unblock');
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ id: string }> }
) {
return requireAuth(request, async (req: AuthenticatedRequest) => {
return requireAdmin(req, async () => {
const { id } = await params;
if (!id || typeof id !== 'string' || id.trim().length === 0) {
return NextResponse.json({ error: 'Invalid id' }, { status: 400 });
}
try {
await removeBlock(id);
return NextResponse.json({ success: true });
} catch (error) {
if (
error instanceof Prisma.PrismaClientKnownRequestError &&
error.code === 'P2025'
) {
return NextResponse.json(
{ error: 'NotFound', message: 'Blocklist entry not found' },
{ status: 404 }
);
}
logger.error('Failed to remove blocklist entry', {
id,
error: error instanceof Error ? error.message : String(error),
});
return NextResponse.json(
{ error: 'Failed to remove blocklist entry' },
{ status: 500 }
);
}
});
});
}