import { NextResponse } from 'next/server'; import { createClient } from '@/lib/supabase/server'; const PRESS_RELEASE_PATTERNS = [ /for immediate release/i, /is pleased to announce/i, /announced today/i, /press release/i, /contact:\s/i, ]; function isPressRelease(content: string): boolean { return PRESS_RELEASE_PATTERNS.filter((p) => p.test(content)).length >= 2; } function countWords(text: string): number { return (text || '').split(/\s+/).filter(Boolean).length; } /** * GET /api/content/audit * Returns a full content audit across all published articles. */ export async function GET() { try { const supabase = await createClient(); const [{ data: imported }, { data: native }] = await Promise.all([ supabase .from('wp_imported_posts') .select('id, title, content, status, featured_image, meta_title, meta_description, created_at') .order('created_at', { ascending: false }), supabase .from('native_articles') .select('id, title, content, status, featured_image, meta_title, meta_description, created_at') .order('created_at', { ascending: false }), ]); const allArticles = [ ...(imported || []).map((a) => ({ ...a, source: 'imported' })), ...(native || []).map((a) => ({ ...a, source: 'native' })), ]; const published = allArticles.filter((a) => a.status === 'published'); const total = allArticles.length; const totalPublished = published.length; // Thin content: under 300 words const thinContent = published.filter((a) => countWords(a.content) < 300); // Missing featured image const missingFeaturedImage = published.filter( (a) => !a.featured_image || a.featured_image === '' ); // Missing SEO meta const missingSeoTitle = published.filter((a) => !a.meta_title || a.meta_title === ''); const missingSeoDescription = published.filter( (a) => !a.meta_description || a.meta_description === '' ); const missingSeoAny = published.filter( (a) => !a.meta_title || !a.meta_description || a.meta_title === '' || a.meta_description === '' ); // Press releases (unedited) const pressReleases = published.filter((a) => isPressRelease(a.content || '')); // Word count distribution const wordCounts = published.map((a) => countWords(a.content)); const avgWordCount = wordCounts.length > 0 ? Math.round(wordCounts.reduce((s, c) => s + c, 0) / wordCounts.length) : 0; // Launch checklist const launchChecklist = { zeroPagesWithMissingFeaturedImages: missingFeaturedImage.length === 0, zeroPagesWithThinContent: thinContent.length === 0, zeroPagesMissingSeoMeta: missingSeoAny.length === 0, zeroUnrewrittenPressReleases: pressReleases.length === 0, }; return NextResponse.json({ summary: { totalArticles: total, totalPublished, thinContentCount: thinContent.length, missingFeaturedImageCount: missingFeaturedImage.length, missingSeoTitleCount: missingSeoTitle.length, missingSeoDescriptionCount: missingSeoDescription.length, missingSeoAnyCount: missingSeoAny.length, pressReleaseCount: pressReleases.length, averageWordCount: avgWordCount, }, launchChecklist, details: { thinContent: thinContent.map((a) => ({ id: a.id, title: a.title, wordCount: countWords(a.content), source: a.source, })), missingFeaturedImage: missingFeaturedImage.map((a) => ({ id: a.id, title: a.title, source: a.source, })), missingSeoMeta: missingSeoAny.map((a) => ({ id: a.id, title: a.title, missingTitle: !a.meta_title, missingDescription: !a.meta_description, source: a.source, })), pressReleases: pressReleases.map((a) => ({ id: a.id, title: a.title, source: a.source, created_at: a.created_at, })), }, }); } catch (err: any) { return NextResponse.json({ error: err.message }, { status: 500 }); } }