Initial commit
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db, reports } from '@/db';
|
||||
import { requireAdmin } from '@/lib/auth/admin';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { z } from 'zod';
|
||||
|
||||
type RouteContext = { params: Promise<{ id: string }> };
|
||||
|
||||
const updateSchema = z.object({
|
||||
status: z.enum(['open', 'resolved']),
|
||||
note: z.string().max(240).optional(),
|
||||
});
|
||||
|
||||
export async function PATCH(request: Request, context: RouteContext) {
|
||||
try {
|
||||
const admin = await requireAdmin();
|
||||
|
||||
if (!db) {
|
||||
return NextResponse.json({ error: 'Database not available' }, { status: 503 });
|
||||
}
|
||||
|
||||
const { id } = await context.params;
|
||||
const body = await request.json();
|
||||
const data = updateSchema.parse(body);
|
||||
|
||||
const report = await db.query.reports.findFirst({
|
||||
where: eq(reports.id, id),
|
||||
});
|
||||
|
||||
if (!report) {
|
||||
return NextResponse.json({ error: 'Report not found' }, { status: 404 });
|
||||
}
|
||||
|
||||
const [updated] = await db.update(reports)
|
||||
.set({
|
||||
status: data.status,
|
||||
resolvedAt: data.status === 'resolved' ? new Date() : null,
|
||||
resolvedBy: data.status === 'resolved' ? admin.id : null,
|
||||
resolutionNote: data.note || null,
|
||||
})
|
||||
.where(eq(reports.id, id))
|
||||
.returning();
|
||||
|
||||
return NextResponse.json({ report: updated });
|
||||
} catch (error) {
|
||||
if (error instanceof z.ZodError) {
|
||||
return NextResponse.json({ error: 'Invalid input', details: error.issues }, { status: 400 });
|
||||
}
|
||||
if (error instanceof Error && error.message === 'Admin required') {
|
||||
return NextResponse.json({ error: 'Admin required' }, { status: 403 });
|
||||
}
|
||||
console.error('Report update error:', error);
|
||||
return NextResponse.json({ error: 'Failed to update report' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { db, reports, posts, users } from '@/db';
|
||||
import { requireAdmin } from '@/lib/auth/admin';
|
||||
import { desc, inArray, eq } from 'drizzle-orm';
|
||||
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
await requireAdmin();
|
||||
|
||||
if (!db) {
|
||||
return NextResponse.json({ error: 'Database not available' }, { status: 503 });
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const status = searchParams.get('status') || 'open'; // open | resolved | all
|
||||
const limit = Math.min(parseInt(searchParams.get('limit') || '25'), 50);
|
||||
|
||||
const reportRows = await db.query.reports.findMany({
|
||||
where: status === 'all' ? undefined : eq(reports.status, status),
|
||||
orderBy: [desc(reports.createdAt)],
|
||||
limit,
|
||||
with: {
|
||||
reporter: true,
|
||||
resolver: true,
|
||||
},
|
||||
});
|
||||
|
||||
const postIds = reportRows
|
||||
.filter((report) => report.targetType === 'post')
|
||||
.map((report) => report.targetId);
|
||||
const userIds = reportRows
|
||||
.filter((report) => report.targetType === 'user')
|
||||
.map((report) => report.targetId);
|
||||
|
||||
const postTargetsRaw = postIds.length
|
||||
? await db.query.posts.findMany({
|
||||
where: inArray(posts.id, postIds),
|
||||
with: { author: true },
|
||||
})
|
||||
: [];
|
||||
const userTargetsRaw = userIds.length
|
||||
? await db.query.users.findMany({
|
||||
where: inArray(users.id, userIds),
|
||||
})
|
||||
: [];
|
||||
|
||||
const postTargets = postTargetsRaw.map((post) => {
|
||||
const author = post.author as { id: string; handle: string; displayName: string | null };
|
||||
return {
|
||||
id: post.id,
|
||||
content: post.content,
|
||||
createdAt: post.createdAt,
|
||||
isRemoved: post.isRemoved,
|
||||
author: {
|
||||
id: author.id,
|
||||
handle: author.handle,
|
||||
displayName: author.displayName,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const userTargets = userTargetsRaw.map((user) => ({
|
||||
id: user.id,
|
||||
handle: user.handle,
|
||||
displayName: user.displayName,
|
||||
isSuspended: user.isSuspended,
|
||||
isSilenced: user.isSilenced,
|
||||
}));
|
||||
|
||||
const postMap = new Map(postTargets.map((post) => [post.id, post]));
|
||||
const userMap = new Map(userTargets.map((user) => [user.id, user]));
|
||||
|
||||
type UserInfo = { id: string; handle: string };
|
||||
|
||||
const reportsWithTargets = reportRows.map((report) => {
|
||||
const reporter = report.reporter as UserInfo | null;
|
||||
const resolver = report.resolver as UserInfo | null;
|
||||
return {
|
||||
id: report.id,
|
||||
targetType: report.targetType,
|
||||
targetId: report.targetId,
|
||||
reason: report.reason,
|
||||
status: report.status,
|
||||
createdAt: report.createdAt,
|
||||
reporter: reporter
|
||||
? { id: reporter.id, handle: reporter.handle }
|
||||
: null,
|
||||
resolver: resolver
|
||||
? { id: resolver.id, handle: resolver.handle }
|
||||
: null,
|
||||
target:
|
||||
report.targetType === 'post'
|
||||
? postMap.get(report.targetId) || null
|
||||
: userMap.get(report.targetId) || null,
|
||||
};
|
||||
});
|
||||
|
||||
return NextResponse.json({ reports: reportsWithTargets });
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message === 'Admin required') {
|
||||
return NextResponse.json({ error: 'Admin required' }, { status: 403 });
|
||||
}
|
||||
console.error('Admin reports error:', error);
|
||||
return NextResponse.json({ error: 'Failed to fetch reports' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user