Initial commit

This commit is contained in:
root
2026-01-26 08:34:48 +01:00
commit 387314581f
216 changed files with 81204 additions and 0 deletions
+55
View File
@@ -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 });
}
}
+106
View File
@@ -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 });
}
}