57c29f62b4
- user_blocks table (composite PK), Block/Unblock button on profiles. Blocking severs any existing follow relationship both ways and prevents the blocked party from following, commenting on the blocker's recipes, or the blocker from seeing their comments. - reports table (recipe/comment/user targets, pending/reviewed/ dismissed status). ReportButton on comments, admin review queue at /admin/reports with dismiss/mark-reviewed actions, audit-logged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
50 lines
1.9 KiB
TypeScript
50 lines
1.9 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
|
import { db, users, userFollows, eq, and } from "@epicure/db";
|
|
import { requireSession } from "@/lib/api-auth";
|
|
import { applyRateLimit } from "@/lib/rate-limit";
|
|
import { createNotification } from "@/lib/notifications";
|
|
import { isBlockedEitherWay } from "@/lib/blocks";
|
|
|
|
type Params = { params: Promise<{ username: string }> };
|
|
|
|
export async function POST(_req: NextRequest, { params }: Params) {
|
|
const { session, response } = await requireSession();
|
|
if (response) return response;
|
|
|
|
const limited = await applyRateLimit(`rl:follow:${session!.user.id}`, 30, 60);
|
|
if (limited) return limited;
|
|
|
|
const { username } = await params;
|
|
|
|
const target = await db.query.users.findFirst({ where: eq(users.username, username) });
|
|
if (!target) return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
if (target.id === session!.user.id) return NextResponse.json({ error: "Cannot follow yourself" }, { status: 400 });
|
|
|
|
if (await isBlockedEitherWay(session!.user.id, target.id)) {
|
|
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
}
|
|
|
|
await db.insert(userFollows)
|
|
.values({ followerId: session!.user.id, followingId: target.id })
|
|
.onConflictDoNothing();
|
|
|
|
void createNotification({ userId: target.id, type: "follow", actorId: session!.user.id });
|
|
|
|
return NextResponse.json({ following: true });
|
|
}
|
|
|
|
export async function DELETE(_req: NextRequest, { params }: Params) {
|
|
const { session, response } = await requireSession();
|
|
if (response) return response;
|
|
const { username } = await params;
|
|
|
|
const target = await db.query.users.findFirst({ where: eq(users.username, username) });
|
|
if (!target) return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
|
|
await db.delete(userFollows).where(
|
|
and(eq(userFollows.followerId, session!.user.id), eq(userFollows.followingId, target.id))
|
|
);
|
|
|
|
return NextResponse.json({ following: false });
|
|
}
|