← back to Homesonspec

apps/admin/src/app/api/review/route.ts

49 lines

import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";
import { prisma } from "@homesonspec/database";
import { publishStagedRecord } from "@homesonspec/publisher";

const actionSchema = z.object({
  reviewItemId: z.string(),
  stagedRecordId: z.string(),
  action: z.enum(["approve", "reject"]),
});

/**
 * Human review resolution. Approve = staged record becomes APPROVED and the
 * publish stage (the only published-tables writer) runs. Reject = REJECTED,
 * never published. Either way the ReviewItem records who/when.
 */
export async function POST(request: NextRequest) {
  const parsed = actionSchema.safeParse(await request.json().catch(() => null));
  if (!parsed.success) return NextResponse.json({ error: "invalid request" }, { status: 400 });
  const { reviewItemId, stagedRecordId, action } = parsed.data;

  const item = await prisma.reviewItem.findUnique({ where: { id: reviewItemId } });
  if (!item || item.status !== "OPEN") {
    return NextResponse.json({ error: "review item not open" }, { status: 409 });
  }

  if (action === "approve") {
    await prisma.stagedRecord.update({ where: { id: stagedRecordId }, data: { status: "APPROVED" } });
    try {
      await publishStagedRecord(stagedRecordId);
    } catch (error) {
      return NextResponse.json({ error: `publish failed: ${String(error)}` }, { status: 500 });
    }
  } else {
    await prisma.stagedRecord.update({ where: { id: stagedRecordId }, data: { status: "REJECTED" } });
  }

  await prisma.reviewItem.update({
    where: { id: reviewItemId },
    data: {
      status: action === "approve" ? "APPROVED" : "REJECTED",
      reviewer: "admin",
      resolvedAt: new Date(),
    },
  });

  return NextResponse.json({ ok: true });
}