Phase B polish: revalidatePath on approve, reject empty submissions

src/app/api/admin/review-queue/[id]/route.ts
  - Call revalidatePath on /news, /news/<slug>, /articles/<slug>, /home-page,
    section pages, and /sitemap.xml on approve/reject. The /news listing is
    ISR-cached (revalidate=1800) so approval would otherwise be invisible
    for up to 30 minutes; this flushes the cache immediately.

src/app/api/ai/submit/route.ts
  - Reject submissions where rawTitle/title is whitespace or rawContent/body
    is empty after stripping HTML. Previously empty bodies fell through into
    the LLM and Claude dutifully wrote a 400-word piece about the empty
    submission. Hard-cap at the gate to avoid the spend.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude (Phase B)
2026-05-14 19:38:20 +00:00
parent 85599d14cc
commit 8fd7165a6a
2 changed files with 20 additions and 2 deletions

View File

@@ -1,4 +1,5 @@
import { NextRequest, NextResponse } from "next/server";
import { revalidatePath, revalidateTag } from "next/cache";
import { createClient } from "@/lib/supabase/server";
import { createAdminClient } from "@/lib/supabase/admin";
@@ -55,7 +56,7 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
...extras,
})
.eq("id", id)
.select("id, original_submission_id")
.select("id, slug, original_submission_id")
.single();
if (error || !data) return NextResponse.json({ error: error?.message || "Not found" }, { status: 500 });
@@ -67,5 +68,20 @@ export async function POST(req: NextRequest, { params }: { params: Promise<{ id:
.eq("id", (data as any).original_submission_id);
}
return NextResponse.json({ ok: true, id: (data as any).id, status: nextStatus });
const slug = (data as any).slug as string;
for (const path of [
"/news",
`/news/${slug}`,
`/articles/${slug}`,
"/home-page",
"/technology",
"/gear",
"/show-coverage",
"/sitemap.xml",
]) {
try { revalidatePath(path); } catch {}
}
try { revalidateTag("articles"); } catch {}
return NextResponse.json({ ok: true, id: (data as any).id, status: nextStatus, slug });
}

View File

@@ -52,10 +52,12 @@ function normalize(raw: any): NativePayload | null {
if (!raw || typeof raw !== "object") return null;
// Native shape: { rawTitle, rawContent, ... }
if (typeof raw.rawTitle === "string" && typeof raw.rawContent === "string") {
if (!raw.rawTitle.trim() || !raw.rawContent.trim()) return null;
return raw as NativePayload;
}
// Distribute-rmp shape: { title, body, contributor: {...} }
if (typeof raw.title === "string" && typeof raw.body === "string") {
if (!raw.title.trim() || !raw.body.replace(/<[^>]*>/g, "").trim()) return null;
const d = raw as DistributePayload;
const contributorName = d.contributor?.contact_name || d.contributor?.firm_name || undefined;
const contributorEmail = d.contributor?.email || undefined;