← back to Norma
app/api/ingest/news/route.ts
68 lines
import { NextRequest, NextResponse } from 'next/server';
import { requireRole } from '@/lib/require-role';
import { syncNews, DEFAULT_NEWS_FEEDS } from '@/lib/ingest-news';
/**
* POST /api/ingest/news
*
* Trigger a news RSS sync. Fetches headlines from configured RSS feeds,
* generates Gemini summaries, and persists to news_items.
* Requires an authenticated session cookie.
*
* Optional body:
* { feeds?: string[] } — override the default feed list with custom RSS URLs
*
* Response:
* 200 { inserted, skipped, total, feeds, errors } — sync completed
* 400 { error: string } — invalid body
* 500 { error: string } — sync failed
*/
export async function POST(request: NextRequest) {
const auth = requireRole(request, 'admin', 'staff');
if (auth instanceof NextResponse) return auth;
let feedUrls: string[] = DEFAULT_NEWS_FEEDS;
// Parse optional body — if malformed JSON just fall through to defaults
const contentType = request.headers.get('content-type') ?? '';
if (contentType.includes('application/json')) {
try {
const body = await request.json();
if (body.feeds !== undefined) {
if (
!Array.isArray(body.feeds) ||
body.feeds.some((f: unknown) => typeof f !== 'string')
) {
return NextResponse.json(
{ error: '`feeds` must be an array of URL strings' },
{ status: 400 },
);
}
if (body.feeds.length === 0) {
return NextResponse.json(
{ error: '`feeds` array must not be empty' },
{ status: 400 },
);
}
feedUrls = body.feeds as string[];
}
} catch {
// Non-JSON or empty body — use defaults silently
}
}
try {
const result = await syncNews(feedUrls);
return NextResponse.json(result, { status: 200 });
} catch (err) {
console.error('[api/ingest/news] POST error:', (err as Error).message);
return NextResponse.json(
{ error: `News sync failed: ${(err as Error).message}` },
{ status: 500 },
);
}
}