mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-18 17:21:23 +02:00
managarten cutover: news-Modul liest jetzt aus mana-news-pool
Some checks are pending
CD Mac Mini / Detect Changes (push) Waiting to run
CD Mac Mini / Deploy (push) Blocked by required conditions
CI / Detect Changes (push) Waiting to run
CI / Validate (push) Waiting to run
CI / Build mana-search (push) Blocked by required conditions
CI / Build mana-sync (push) Blocked by required conditions
CI / Build mana-api-gateway (push) Blocked by required conditions
CI / Build mana-crawler (push) Blocked by required conditions
Docker Validate / Validate Dockerfiles (push) Waiting to run
Docker Validate / Build calendar-web (push) Blocked by required conditions
Docker Validate / Build quotes-web (push) Blocked by required conditions
Docker Validate / Build todo-backend (push) Blocked by required conditions
Docker Validate / Build todo-web (push) Blocked by required conditions
Mirror to Forgejo / Push to Forgejo (push) Waiting to run
Docker Validate / Build mana-auth (push) Blocked by required conditions
Docker Validate / Build mana-sync (push) Blocked by required conditions
Docker Validate / Build mana-media (push) Blocked by required conditions
Some checks are pending
CD Mac Mini / Detect Changes (push) Waiting to run
CD Mac Mini / Deploy (push) Blocked by required conditions
CI / Detect Changes (push) Waiting to run
CI / Validate (push) Waiting to run
CI / Build mana-search (push) Blocked by required conditions
CI / Build mana-sync (push) Blocked by required conditions
CI / Build mana-api-gateway (push) Blocked by required conditions
CI / Build mana-crawler (push) Blocked by required conditions
Docker Validate / Validate Dockerfiles (push) Waiting to run
Docker Validate / Build calendar-web (push) Blocked by required conditions
Docker Validate / Build quotes-web (push) Blocked by required conditions
Docker Validate / Build todo-backend (push) Blocked by required conditions
Docker Validate / Build todo-web (push) Blocked by required conditions
Mirror to Forgejo / Push to Forgejo (push) Waiting to run
Docker Validate / Build mana-auth (push) Blocked by required conditions
Docker Validate / Build mana-sync (push) Blocked by required conditions
Docker Validate / Build mana-media (push) Blocked by required conditions
apps/api/src/modules/news/routes.ts — ehemals Raw-SQL gegen mana_platform.news.curated_articles, jetzt HTTP-Proxy auf MANA_NEWS_POOL_URL/feed mit X-Service-Key. Identischer Query- Param-Vertrag (topics/lang/since/limit/offset), kein Drizzle- Schema-Coupling mehr für News. docker-compose.macmini.yml — MANA_NEWS_POOL_URL=http://mana-news-pool:3079 in mana-api environment. News-Ingester-Kommentar-Section aktualisiert (Container ist seit Lift-B abgeschaltet). Damit ist der vollständige Cutover-Pfad aus mana/services/mana-news-pool/CLAUDE.md durch: 1. Plattform-Service deployed (gestern) 2. managarten konsumiert ihn (jetzt) 3. alter news-ingester:3066-Container schon weg Type-check: news/routes.ts grün (2 pre-existing forms/-Errors unrelated).
This commit is contained in:
parent
17e5f80adf
commit
ad97c5362c
2 changed files with 42 additions and 76 deletions
|
|
@ -1,98 +1,59 @@
|
|||
/**
|
||||
* News module — Reads the curated article pool + extracts ad-hoc URLs.
|
||||
*
|
||||
* Pool population: handled by the standalone `services/news-ingester`
|
||||
* Bun service, which writes into `news.curated_articles` on a 15 min
|
||||
* loop. This route file just reads from that table.
|
||||
* Pool population: handled by the Plattform-Service `mana-news-pool`
|
||||
* (Port 3079, eigene DB `mana_news_pool`, Schema `pool.curated_articles`).
|
||||
* Cutover am 2026-05-17: ehemals direkter Raw-SQL-Read auf
|
||||
* `mana_platform.news.curated_articles` aus dem `news-ingester:3066`-
|
||||
* Container. Hier nur noch HTTP-Proxy auf den Plattform-Pool.
|
||||
*
|
||||
* Saved articles (the user's personal reading list) live entirely in
|
||||
* the unified Mana app's local-first IndexedDB and sync via mana-sync;
|
||||
* this module never sees them.
|
||||
* Saved articles (die persönliche Reading-List eines Users) leben
|
||||
* weiterhin client-side in der IndexedDB der unified Mana-App und
|
||||
* syncen via mana-sync; dieses Modul sieht sie nicht.
|
||||
*/
|
||||
|
||||
import { Hono } from 'hono';
|
||||
import { extractFromUrl } from '@mana/shared-rss';
|
||||
import { drizzle } from 'drizzle-orm/postgres-js';
|
||||
import { sql } from 'drizzle-orm';
|
||||
import { getConnection } from '../../lib/db';
|
||||
|
||||
// ─── DB Connection (reads from news.curated_articles) ──────
|
||||
|
||||
const db = drizzle(getConnection());
|
||||
const POOL_URL = process.env.MANA_NEWS_POOL_URL ?? 'http://mana-news-pool:3079';
|
||||
const POOL_KEY = process.env.MANA_SERVICE_KEY ?? '';
|
||||
|
||||
// ─── Routes ─────────────────────────────────────────────────
|
||||
|
||||
const routes = new Hono();
|
||||
|
||||
// ─── Feed (reads from news.curated_articles) ───────────────
|
||||
// ─── Feed (proxy on mana-news-pool) ────────────────────────
|
||||
//
|
||||
// Query params:
|
||||
// topics — comma-separated topic slugs (tech,wissenschaft,…). If
|
||||
// omitted, all topics are returned.
|
||||
// topics — comma-separated topic slugs (tech,wissenschaft,…)
|
||||
// lang — 'de' | 'en' | 'all' (default 'all')
|
||||
// since — ISO timestamp; only articles published after this
|
||||
// since — ISO timestamp
|
||||
// limit — default 50, max 200
|
||||
// offset — default 0
|
||||
//
|
||||
// Returns the full article body so the client can render the reader
|
||||
// without a second round-trip. Curated articles are small (≤30 KB
|
||||
// each) and the client caches them locally for offline reading.
|
||||
|
||||
routes.get('/feed', async (c) => {
|
||||
const topicsParam = c.req.query('topics');
|
||||
const lang = c.req.query('lang') ?? 'all';
|
||||
const since = c.req.query('since');
|
||||
const limit = Math.min(parseInt(c.req.query('limit') || '50', 10), 200);
|
||||
const offset = parseInt(c.req.query('offset') || '0', 10);
|
||||
const passthrough = ['topics', 'lang', 'since', 'limit', 'offset'] as const;
|
||||
const url = new URL(`${POOL_URL}/feed`);
|
||||
for (const k of passthrough) {
|
||||
const v = c.req.query(k);
|
||||
if (v) url.searchParams.set(k, v);
|
||||
}
|
||||
|
||||
const conditions: ReturnType<typeof sql>[] = [];
|
||||
|
||||
if (topicsParam) {
|
||||
const topics = topicsParam
|
||||
.split(',')
|
||||
.map((t) => t.trim())
|
||||
.filter(Boolean);
|
||||
if (topics.length > 0) {
|
||||
conditions.push(sql`topic = ANY(${topics})`);
|
||||
try {
|
||||
const res = await fetch(url.toString(), {
|
||||
headers: { 'X-Service-Key': POOL_KEY },
|
||||
signal: AbortSignal.timeout(8_000),
|
||||
});
|
||||
if (!res.ok) {
|
||||
console.warn(`[news] pool ${url} → ${res.status}`);
|
||||
return c.json([] as Record<string, unknown>[]);
|
||||
}
|
||||
const data = (await res.json()) as Record<string, unknown>[];
|
||||
return c.json(data);
|
||||
} catch (err) {
|
||||
console.warn('[news] pool fetch failed', err);
|
||||
return c.json([] as Record<string, unknown>[]);
|
||||
}
|
||||
if (lang === 'de' || lang === 'en') {
|
||||
conditions.push(sql`language = ${lang}`);
|
||||
}
|
||||
if (since) {
|
||||
conditions.push(sql`published_at > ${since}`);
|
||||
}
|
||||
|
||||
const whereClause =
|
||||
conditions.length > 0
|
||||
? sql.join([sql`WHERE`, sql.join(conditions, sql` AND `)], sql` `)
|
||||
: sql``;
|
||||
|
||||
const result = await db.execute(sql`
|
||||
SELECT
|
||||
id,
|
||||
original_url AS "originalUrl",
|
||||
title,
|
||||
excerpt,
|
||||
content,
|
||||
html_content AS "htmlContent",
|
||||
author,
|
||||
site_name AS "siteName",
|
||||
source_slug AS "sourceSlug",
|
||||
image_url AS "imageUrl",
|
||||
topic,
|
||||
language,
|
||||
word_count AS "wordCount",
|
||||
reading_time_minutes AS "readingTimeMinutes",
|
||||
published_at AS "publishedAt",
|
||||
ingested_at AS "ingestedAt"
|
||||
FROM news.curated_articles
|
||||
${whereClause}
|
||||
ORDER BY published_at DESC NULLS LAST, ingested_at DESC
|
||||
LIMIT ${limit} OFFSET ${offset}
|
||||
`);
|
||||
|
||||
return c.json(result as unknown as Record<string, unknown>[]);
|
||||
});
|
||||
|
||||
// ─── Extract (content extraction for user-pasted URLs) ─────
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue