Forked from Forthena (PERSONAL/forthena) and retargeted: - NO login, NO LLM: the edition is public and the ranking is deterministic (stories that name Sen. BAM lead; freshest first) — Daniel: no like/dislike or store profile for visitors. - Crawler scoped to BAM: Bing news keyword queries + Inquirer/Rappler fallback wires, with a two-tier relevance gate (named-BAM always; Aquino+legislative-context only from fallback) so op-eds and non-Bam senators are dropped. - Desks: BAM (lead), Gaming, Education, Politics, Energy, PH, Business. - Community board /board: 'Thank you Senator BAM' — nickname-only posts, HTML-stripped, URL-only rejected, IP rate-limited (3/10min, 20/day). - Yellow-gamer theme (Orbitron/Rajdhani/JetBrains Mono, dark + gold, HUD corners), supporter disclaimer in header and footer. - Tests: category/date/relevance/text suites + Mongo board suite + full HTTP e2e (all passing). Infra: 66.181.46.132 (bidaangmamamayan.com), local Mongo 7.0, nginx + certbot TLS, PM2 bama (:3000) + 15-min crawl cron.
24 lines
995 B
JavaScript
24 lines
995 B
JavaScript
import { NextResponse } from "next/server";
|
|
|
|
// The article body for the reader pane. Public (no auth), zero LLM.
|
|
// Mongo first (the crawler warms it on the tick), publisher fetch on a miss.
|
|
|
|
export const dynamic = "force-dynamic";
|
|
export const maxDuration = 60;
|
|
|
|
export async function GET(req) {
|
|
const q = new URL(req.url).searchParams;
|
|
const url = q.get("url") || "";
|
|
if (!/^https?:\/\//i.test(url)) return NextResponse.json({ ok: false, error: "url required" }, { status: 400 });
|
|
|
|
try {
|
|
const { getArticle } = await import("../../../lib/article");
|
|
const a = await getArticle({ url, force: q.get("force") === "1" });
|
|
const { story, realUrl, ...article } = a;
|
|
return NextResponse.json({ ok: true, url, realUrl: realUrl || url, article, story: story || null });
|
|
} catch (e) {
|
|
console.warn("[bama] /api/article failed:", String((e && e.message) || e).slice(0, 160));
|
|
return NextResponse.json({ ok: false, error: "article unavailable" }, { status: 500 });
|
|
}
|
|
}
|