Files
Synapsis/src/app/api/reports/route.ts
T
cyph3rasi 60f7dba46c
CI / smoke (push) Has been cancelled
Migrate Synapsis to embedded Turso
2026-07-14 15:50:12 -07:00

60 lines
2.1 KiB
TypeScript

import { NextResponse } from 'next/server';
import { db, reports, posts, users } from '@/db';
import { requireAuth } from '@/lib/auth';
import { requireSignedAction } from '@/lib/auth/verify-signature';
import { eq } from 'drizzle-orm';
import { z } from 'zod';
const reportSchema = z.object({
targetType: z.enum(['post', 'user']),
targetId: z.string().uuid(),
reason: z.string().min(3).max(500),
});
export async function POST(request: Request) {
try {
const signedAction = await request.json();
const reporter = await requireSignedAction(signedAction);
// Trust signed payload
const data = reportSchema.parse(signedAction.data);
if (data.targetType === 'post') {
const targetPost = await db.query.posts.findFirst({
where: { id: data.targetId },
});
if (!targetPost || targetPost.isRemoved) {
return NextResponse.json({ error: 'Post not found' }, { status: 404 });
}
}
if (data.targetType === 'user') {
const targetUser = await db.query.users.findFirst({
where: { id: data.targetId },
});
if (!targetUser) {
return NextResponse.json({ error: 'User not found' }, { status: 404 });
}
}
const [report] = await db.insert(reports).values({
reporterId: reporter.id,
targetType: data.targetType,
targetId: data.targetId,
reason: data.reason,
status: 'open',
}).returning();
return NextResponse.json({ report });
} catch (error) {
if (error instanceof z.ZodError) {
return NextResponse.json({ error: 'Invalid input', details: error.issues }, { status: 400 });
}
if (error instanceof Error && error.message === 'Authentication required') {
return NextResponse.json({ error: 'Authentication required' }, { status: 401 });
}
console.error('Report error:', error);
return NextResponse.json({ error: 'Failed to submit report' }, { status: 500 });
}
}