import { NextResponse } from 'next/server'; import { revalidatePath } from 'next/cache'; import { setSection, resetSection } from '@/lib/db/store'; import { POSTS_KEY, loadPost, loadPostOverrides, isKnownSlug, } from '@/lib/content/posts-store'; import type { PostContent } from '@/lib/content/posts'; export const runtime = 'nodejs'; const ACCENTS = ['electric', 'violet', 'magenta', 'emerald', 'cyan']; /** Minimal structural validation for an incoming PostContent payload. */ function validPost(data: unknown): data is { date: string; accent: string; en: { lead: string; blocks: unknown[] }; fa: { lead: string; blocks: unknown[] }; } { if (!data || typeof data !== 'object') return false; const d = data as Record; if (typeof d.date !== 'string') return false; if (typeof d.accent !== 'string' || !ACCENTS.includes(d.accent)) return false; for (const loc of ['en', 'fa'] as const) { const art = d[loc] as Record | undefined; if (!art || typeof art !== 'object') return false; if (typeof art.lead !== 'string') return false; if (!Array.isArray(art.blocks)) return false; } return true; } // GET ?slug=rag-eval-framework -> live (merged) article + override flag export async function GET(req: Request) { const slug = new URL(req.url).searchParams.get('slug') ?? ''; if (!isKnownSlug(slug)) { return NextResponse.json({ error: 'unknown post' }, { status: 400 }); } return NextResponse.json({ slug, post: loadPost(slug), overridden: slug in loadPostOverrides(), }); } // POST { slug, data: PostContent } -> save the article override export async function POST(req: Request) { let body: { slug?: string; data?: unknown }; try { body = await req.json(); } catch { return NextResponse.json({ error: 'bad json' }, { status: 400 }); } const slug = body.slug ?? ''; if (!isKnownSlug(slug)) { return NextResponse.json({ error: 'unknown post' }, { status: 400 }); } if (!validPost(body.data)) { return NextResponse.json( { error: 'expected a { date, accent, en:{lead,blocks}, fa:{lead,blocks} } payload' }, { status: 400 }, ); } const overrides = loadPostOverrides(); // validPost has confirmed the shape (incl. accent ∈ ACCENTS) above. overrides[slug] = body.data as unknown as PostContent; setSection(POSTS_KEY, overrides); revalidatePath(`/blog/${slug}`); revalidatePath('/', 'layout'); return NextResponse.json({ ok: true }); } // DELETE ?slug=… -> revert one article to its in-code default export async function DELETE(req: Request) { const slug = new URL(req.url).searchParams.get('slug') ?? ''; if (!isKnownSlug(slug)) { return NextResponse.json({ error: 'unknown post' }, { status: 400 }); } const overrides = loadPostOverrides(); delete overrides[slug]; if (Object.keys(overrides).length === 0) { resetSection(POSTS_KEY); } else { setSection(POSTS_KEY, overrides); } revalidatePath(`/blog/${slug}`); revalidatePath('/', 'layout'); return NextResponse.json({ ok: true }); }