127 lines
4.1 KiB
TypeScript
127 lines
4.1 KiB
TypeScript
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 });
|
|
}
|
|
}
|