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.
53 lines
1.4 KiB
JavaScript
53 lines
1.4 KiB
JavaScript
import { MongoClient } from "mongodb";
|
|
|
|
// BidaAngMamamayan — local Mongo on the site's own box, db "bama".
|
|
// No auth: the site is public and the data is a public news cache + an
|
|
// open community board. bindIp 127.0.0.1 keeps it off the wire.
|
|
const uri =
|
|
process.env.MONGODB_URI ||
|
|
"mongodb://127.0.0.1:27017/bama";
|
|
|
|
const client = new MongoClient(uri, {
|
|
serverSelectionTimeoutMS: 8000,
|
|
});
|
|
|
|
let cached = global.__bama_mongo;
|
|
if (!cached) {
|
|
cached = global.__bama_mongo = { client, promise: null };
|
|
}
|
|
|
|
export const DB_NAME = process.env.BAMA_DB_NAME || "bama";
|
|
|
|
export async function getDb() {
|
|
if (!cached.promise) {
|
|
cached.promise = cached.client.connect();
|
|
}
|
|
const c = await cached.promise;
|
|
return c.db(DB_NAME);
|
|
}
|
|
|
|
// One-shot scripts (db-reset, the digest trigger) need the client to close so
|
|
// the process actually exits.
|
|
export async function closeDb() {
|
|
if (!cached.client) return;
|
|
try {
|
|
await cached.client.close();
|
|
} finally {
|
|
cached.client = null;
|
|
cached.promise = null;
|
|
if (global.__bama_mongo === cached) delete global.__bama_mongo;
|
|
}
|
|
}
|
|
|
|
export async function getCollections() {
|
|
const db = await getDb();
|
|
return {
|
|
// Crawled news (the shared pool — one site, one public edition).
|
|
stories: db.collection("stories"),
|
|
// Community board ("Thank you Senator BAM").
|
|
board: db.collection("board"),
|
|
// Rate-limit counters (board spam control).
|
|
rate: db.collection("rate"),
|
|
};
|
|
}
|