delete sampleArticles seed; serve all sections from bb.wp_imported_posts (DB)

Eliminates 72 hardcoded rocket.new image refs in one shot. Section pages
(/gear, /technology, /show-coverage, /news) now render from
bb.wp_imported_posts via getLegacyArticlesBySection(section), which maps
each non-news section to a curated tag set (e.g. gear -> Audio, Cameras,
lighting, Blackmagic Design, monitoring; technology -> AI, Streaming,
OTT, IP, Cloud, AV; show-coverage -> NAB, IBC, BroadcastAsia).

Also:
- src/lib/articles/types.ts: Article interface lifted out of the deleted
  seed file. Type-only consumers (NewsArticleDetailClient,
  ArticleDetailClient, legacy-source) re-imported from here.
- /news (client component) now wrapped: server fetches articles from DB
  and passes to NewsPageClient. Filter UI unchanged.
- generateStaticParams on /news/[slug] and /articles/[slug] no longer
  references seed slugs; pre-renders 50 most-recent imported slugs and
  relies on dynamicParams + revalidate=3600 for the rest.
- sitemap.ts now sources article URLs from
  getLegacyRecentSitemapEntries() (up to 5000 most-recent imported posts).
  Default BASE_URL fallback updated from the rocket.new placeholder to
  bb-staging.onsethost.com.
- src/app/api/ai/article-suggestions/route.ts now pulls candidates from
  the DB (top 100 recent news) and resolves AI-returned slugs via DB
  lookup if not in the candidate window.

Inline rocket.new image refs in homepage components (ArticleFeed,
FeaturedBento) are unchanged in this commit; those are inline seed
arrays in the components, not imports of sampleArticles.
This commit is contained in:
Ryan Salazar
2026-05-08 14:31:25 +00:00
parent 08df1ad4c1
commit f1e1d5ea76
14 changed files with 483 additions and 1321 deletions

View File

@@ -1,22 +1,26 @@
import { NextRequest, NextResponse } from 'next/server';
import { hybridAI } from '@/lib/ai/hybridRouter';
import { sampleArticles } from '@/lib/articles/sampleArticles';
import {
getLegacyArticlesBySection,
getLegacyArticleBySlug,
} from '@/lib/articles/legacy-source';
export async function POST(request: NextRequest) {
try {
const { interests, readingHistory } = await request.json();
// Build a compact article index for the AI to reason over
const articleIndex = sampleArticles
.filter((a) => a.section === 'news')
.map((a) => ({
slug: a.slug,
title: a.title,
excerpt: a.excerpt,
tags: a.tags,
category: a.category,
date: a.date,
}));
// Pull a recent slice of news for the AI to choose from. Cap at 100 so the
// prompt stays bounded.
const news = await getLegacyArticlesBySection('news', 100);
const articleIndex = news.map((a) => ({
slug: a.slug,
title: a.title,
excerpt: a.excerpt,
tags: a.tags,
category: a.category,
date: a.date,
}));
const systemPrompt = `You are a personalized content recommendation engine for BroadcastBeat, a broadcast engineering news platform.
Given a reader's topic interests and reading history, select the 4 most relevant articles from the provided article list.
@@ -36,45 +40,38 @@ Return the 4 most relevant article slugs as a JSON array.`;
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userPrompt },
],
{
maxTokens: 200,
temperature: 0.3,
priority: 4,
}
{ maxTokens: 200, temperature: 0.3, priority: 4 }
);
const content = result.text;
let slugs: string[] = [];
try {
// Extract JSON array from response (model may wrap it in text)
const match = content.match(/\[[\s\S]*?\]/);
slugs = match ? JSON.parse(match[0]) : JSON.parse(content.trim());
} catch {
// Fallback: extract slugs with regex
const matches = content.match(/"([^"]+)"/g);
slugs = matches ? matches.map((m: string) => m.replace(/"/g, '')).slice(0, 4) : [];
}
// Resolve full article objects
const suggested = slugs
.map((slug: string) => sampleArticles.find((a) => a.slug === slug))
.filter(Boolean)
.slice(0, 4);
// Resolve from the candidate set first; if a slug wasn't in the recent
// window, look it up via DB so AI hallucinations or older slugs still resolve.
const suggested = (
await Promise.all(
slugs.slice(0, 4).map(async (slug: string) =>
news.find((a) => a.slug === slug) || (await getLegacyArticleBySlug(slug))
)
)
).filter(Boolean);
// Fallback: return latest news if AI returned nothing useful
if (suggested.length === 0) {
const fallback = sampleArticles
.filter((a) => a.section === 'news')
.slice(0, 4);
return NextResponse.json({ suggestions: fallback });
return NextResponse.json({ suggestions: news.slice(0, 4) });
}
return NextResponse.json({ suggestions: suggested });
} catch (error) {
console.error('Article suggestions error:', error);
// Graceful fallback
const fallback = sampleArticles.filter((a) => a.section === 'news').slice(0, 4);
const fallback = await getLegacyArticlesBySection('news', 4);
return NextResponse.json({ suggestions: fallback });
}
}