Files
w-make-portfolio/.hermes/plans/2026-08-16_000000-batchmaker-newsfeed-backend.md
T

13 KiB

Batchmaker Newsfeed Backend Implementation Plan

For Hermes: Use subagent-driven-development skill to implement this plan task-by-task.

Goal: Einen kleinen, dauerhaft betriebenen Update-Feed für Batchmaker und Batchmaker Standalone bauen, der von beiden Produkten und dem Portfolio gelesen werden kann.

Architecture: Separater, schlanker updates-api-Service statt Newsdaten im Next.js-Build oder in der Portfolio-App. Der Service bietet eine öffentliche, read-only JSON-/RSS-Schnittstelle und einen geschützten Admin-Schreibpfad. SQLite ist für die geringe erwartete Änderungsfrequenz ausreichend, wird aber auf einem persistenten Docker-Volume betrieben und regelmäßig gesichert. Das Portfolio und beide Batchmaker-Oberflächen bleiben Clients; kein Produkt greift direkt auf die Datenbank zu.

Tech Stack: Node.js 22, TypeScript, native node:sqlite, kleiner HTTP-Server ohne neues Framework sofern die vorhandene Runtime das zulässt, Docker Compose, SQLite WAL, Bearer-Token aus Secret/Environment, Next.js Server Component oder Route-Proxy für das Portfolio.


1. Ist-Zustand und Integrationsgrenzen verifizieren

Objective: Vor der Implementierung die realen Repositories, Deployment-Ziele und vorhandenen Batchmaker-Backend-Konventionen festhalten.

Read-only checks:

find /home/eldov-ryzen5/workspace/Coding -maxdepth 3 -type f \( -name package.json -o -name docker-compose.yml -o -name Dockerfile \)
rg -n "express|node:sqlite|sqlite|health|PORT|docker" /home/eldov-ryzen5/workspace/Coding/Batchmaker

Festhalten:

  • Welches Repository ist die kanonische Quelle für Batchmaker?
  • Wo läuft Batchmaker Standalone tatsächlich?
  • Soll der Feed auf free-warez.win oder am selben Host wie das Portfolio betrieben werden?
  • Existiert bereits ein Secret-/Backup-Verfahren, das wiederverwendet werden muss?
  • Welche Produkt-URLs sollen aus einem Update verlinkt werden?

Gate: Keine Codeänderung, bevor Host, Repo-Pfade und Deployment-SSOT eindeutig sind.

2. Datenmodell und API-Vertrag als kleine, stabile Oberfläche definieren

Objective: Ein minimales Schema schaffen, das sowohl öffentliche Updates als auch spätere redaktionelle Pflege trägt.

Proposed entity updates:

id            INTEGER PRIMARY KEY
slug          TEXT NOT NULL UNIQUE
product       TEXT NOT NULL CHECK (product IN ('batchmaker', 'standalone'))
title         TEXT NOT NULL
summary       TEXT NOT NULL
body_markdown TEXT NOT NULL DEFAULT ''
status        TEXT NOT NULL CHECK (status IN ('draft', 'published', 'archived'))
published_at  TEXT NULL
created_at    TEXT NOT NULL
updated_at    TEXT NOT NULL
link_url      TEXT NULL

Indexes: (product, status, published_at DESC) und slug.

Public API:

  • GET /healthz{ "status": "ok" }, ohne Datenbankdetails.
  • GET /v1/updates?product=batchmaker|standalone&limit=1..50&cursor=... → nur published und published_at <= now, newest first.
  • GET /v1/updates/:slug → ein veröffentlichtes Update.
  • GET /feed.xml?product=... → RSS 2.0 oder Atom; für Browser/Feed-Reader und SEO.

Admin API:

  • POST /v1/admin/updates
  • PATCH /v1/admin/updates/:id
  • POST /v1/admin/updates/:id/publish
  • POST /v1/admin/updates/:id/archive

Admin-Endpunkte verlangen Authorization: Bearer ...; kein Token in URLs, Logs oder Frontend-Code. Schreibdaten werden serverseitig validiert; slug, product, Titel, Status, Limits und Datumswerte sind keine vertrauenswürdigen Eingaben.

Entscheidungen: Cursor-Pagination nur, wenn sie mit vertretbarem Aufwand sauber umgesetzt wird; sonst zunächst limit mit hartem Maximum. Kein Login-System, kein CMS und keine Datei-Uploads in v1.

3. Backend als eigenständigen Service anlegen

Likely files:

  • Create: services/updates-api/package.json
  • Create: services/updates-api/tsconfig.json
  • Create: services/updates-api/src/server.ts
  • Create: services/updates-api/src/db.ts
  • Create: services/updates-api/src/validation.ts
  • Create: services/updates-api/src/feed.ts
  • Create: services/updates-api/migrations/001_initial.sql
  • Create: services/updates-api/Dockerfile
  • Create: services/updates-api/.env.example

Implementation constraints:

  • Native Node APIs und node:sqlite zuerst; keine neue ORM-Abhängigkeit.
  • DB-Verzeichnis per UPDATES_DB_PATH, Standard lokal unter data/updates.sqlite.
  • Beim Start Migrationen idempotent ausführen, WAL und foreign keys aktivieren.
  • JSON-Fehlerformat stabil halten: { "error": { "code": "...", "message": "..." } }.
  • Request body und Content-Length begrenzen, damit der Admin-Endpunkt kein unkontrollierter Speicher-/Disk-Sink wird.
  • Cache-Control für öffentliche Antworten setzen; Admin-Antworten nicht cachen.
  • Logs ohne Authorization-Header und ohne vollständigen Markdown-Body.
  • HTML-Escaping/Content-Type beim RSS-Generator korrekt behandeln; Markdown nicht ungefiltert als HTML ausgeben.

4. Tests zuerst für die Invarianten schreiben

Test target: services/updates-api/test/updates.test.ts

Abdecken:

  1. Drafts erscheinen niemals in öffentlichen Listen.
  2. Zukünftige published_at-Einträge erscheinen erst nach ihrem Veröffentlichungszeitpunkt.
  3. product akzeptiert nur batchmaker und standalone.
  4. limit wird auf den erlaubten Bereich begrenzt oder mit 400 abgewiesen.
  5. Fehlendes/falsches Bearer-Token blockiert alle Admin-Schreibpfade.
  6. Publish setzt status und published_at atomar.
  7. Archivierte Einträge verschwinden aus Public API und RSS.
  8. RSS enthält keine ungefilterten/kaputten XML-Zeichen.
  9. /healthz prüft die DB-Verbindung, gibt aber keine internen Pfade preis.

Run:

node --test --import tsx services/updates-api/test/updates.test.ts

Falls tsx nicht bereits verfügbar ist: nicht blind eine Dependency hinzufügen; zuerst prüfen, ob TypeScript-Tests über den vorhandenen Build/Node-Mechanismus laufen. Nur dann die kleinste notwendige devDependency ergänzen.

5. Öffentliche Clients anbinden

Portfolio

Likely files:

  • Modify: src/app/page.tsx oder neue src/components/updates-feed.tsx
  • Modify: src/app/layout.tsx nur falls Metadata/Feed-Link benötigt wird
  • Modify: docker-compose.yml nur für URL-/Netzwerk-Konfiguration
  • Modify: .env.example oder Runtime-Dokumentation

Serverseitig vom Next.js-Server abrufen, nicht per Browser direkt gegen eine private interne URL. Bei Feed-Ausfall darf die Portfolio-Seite nicht failen: leerer Zustand bzw. „Updates momentan nicht verfügbar“, mit serverseitigem Timeout. Produktfilter sichtbar machen: „Batchmaker“ / „Standalone“ / „Alle“. Keine Admin-Funktion im öffentlichen Portfolio.

Batchmaker und Standalone

Je Produkt eine kleine, vorhandene UI-Integration verwenden; keine gemeinsame UI-Bibliothek nur für drei Karten einführen. Die Clients konsumieren denselben Public API-Vertrag und zeigen maximal die letzten drei Updates plus Link „Alle Updates“. Die Feed-API-URL wird konfiguriert, nicht hart codiert.

6. Deployment- und Persistenzpfad definieren

Likely files:

  • Modify/create: Compose-Datei des Zielhosts bzw. eines dedizierten updates-api-Stacks
  • Create: services/updates-api/backup.sh oder vorhandenes Backup-Verfahren erweitern
  • Modify: Traefik dynamic config/labels, falls öffentliches Routing dort erfolgt

Deployment design:

  • Service intern auf Port 8080.
  • Traefik-Router z. B. updates.w-make.com oder ein eindeutig festgelegter Pfad; Entscheidung erst nach Hostprobe.
  • SQLite unter /data/updates.sqlite auf named volume, nicht im Container-Layer.
  • Admin-Token ausschließlich über Secret/Environment auf dem Host.
  • /healthz als Container-Healthcheck.
  • DB-Backup vor jedem Deployment und per täglichem, atomarem sqlite3 .backup/Copy-Verfahren; Restore-Test in temporärem Verzeichnis.
  • Keine Docker-Socket-Berechtigung für den Newsfeed-Service.

Gate: Erst deployen, wenn Traefik-Docker-Socket-Zugriff und Router-Konfiguration live geprüft sind; kein blindes 404-Debugging.

7. Redaktions- und Betriebsworkflow festlegen

V1: Updates werden per dokumentiertem CLI-Skript oder curl aus einer sicheren Admin-Umgebung angelegt/publiziert. Kein browserbasiertes Admin-Panel, solange nicht mehrere Autoren oder regelmäßige Redaktionsarbeit nachgewiesen sind.

Create: services/updates-api/README.md mit:

  • lokalem Start
  • Beispiel für Draft → Publish
  • Token-Handling ohne Credential-Beispiele
  • Backup/Restore
  • Rollback
  • Public API examples

Optional später: kleines Admin-Formular hinter Auth, wenn der CLI-Workflow nachweislich hinderlich ist.

8. Ende-zu-Ende-Verifikation

Local gates:

npm run lint
npm run build
node --test --import tsx services/updates-api/test/updates.test.ts
curl -fsS http://127.0.0.1:8080/healthz
curl -fsS 'http://127.0.0.1:8080/v1/updates?product=standalone&limit=3'

Integration checks:

  • Ein Test-Draft je Produkt anlegen.
  • Sicherstellen, dass Public API beide Drafts nicht zeigt.
  • Beide veröffentlichen und Responses/RSS prüfen.
  • Portfolio und beide Produkte laden; Feed-Karten und Deep-Link prüfen.
  • API absichtlich stoppen: Clients bleiben renderbar und zeigen Fallback.
  • Falsches Admin-Token, übergroßer Body und ungültiges Produkt testen.

Production gates:

  • curl über den finalen HTTPS-Host → 200 für /healthz, /v1/updates, /feed.xml.
  • Traefik-Router trifft den richtigen Service; keine 404/502.
  • Container-Restart: Test-Update bleibt vorhanden.
  • Backup erstellen und in temporäre SQLite-Datei zurücklesen.
  • Logs enthalten keine Secrets.
  • Erst danach echte News veröffentlichen.

Risiken und Trade-offs

  • SQLite vs. PostgreSQL: SQLite ist für seltene redaktionelle Writes und wenige Leser ausreichend und reduziert Ops deutlich. Bei mehreren Autoren, hoher Schreiblast oder bestehender PostgreSQL-SSOT migrieren; API-Vertrag bleibt gleich.
  • Separate API vs. Next Route Handler: Separate API verhindert Kopplung von Persistenz und Portfolio-Build und kann von beiden Batchmaker-Produkten genutzt werden. Mehr Deployment-Artefakt, aber sauberere Verantwortungsgrenze.
  • Token vs. Auth-System: Ein einzelnes Admin-Token ist für v1 klein und ausreichend, solange es nur server-/CLI-seitig genutzt wird. Bei mehreren Autoren oder Browser-Admin auf OIDC wechseln.
  • Markdown vs. rich HTML: Markdown als gespeicherter Inhalt bleibt diff-/backup-freundlich; Ausgabe zunächst plain text bzw. strikt sanitised rendern. Kein raw HTML in v1.
  • Public cache: CDN-/Traefik-Caching kann Updates verzögern. Bei Veröffentlichung Cache-Control kurz halten oder gezielt invalidieren; keine clientseitige Dauer-Cacheschicht einführen.

Offene Entscheidungen für den Session-Start

  1. Finaler API-Host bzw. Pfad und Zielhost.
  2. Sind Batchmaker und Standalone beide öffentlich erreichbar und in welchen Repositories liegen sie?
  3. Soll der öffentliche Feed vollständig anonym sein oder nur über die Produktseiten erreichbar sein?
  4. Reicht CLI/curl als Redaktionsworkflow für v1? Default: ja.
  5. Welche Pflichtfelder braucht ein Update redaktionell: nur Titel/Summary/Body oder auch Release-Version und CTA-Link?
  6. Gibt es bereits einen PostgreSQL-Service, der als SSOT genutzt werden muss? Default: nein; nicht aus Gründen der vermeintlichen Zukunftssicherheit einführen.

Definition of Done

  • Ein eigenständiger, health-checkbarer Updates-Service läuft reproduzierbar lokal und im Ziel-Compose.
  • Beide Produktwerte werden korrekt getrennt, Draft/Published/Archived sind invariant.
  • Public JSON und RSS funktionieren; Admin-Schreibpfad ist authentifiziert und validiert.
  • Portfolio, Batchmaker und Standalone konsumieren denselben Vertrag mit Graceful Fallback.
  • SQLite liegt persistent, Backup und Restore sind real getestet.
  • Lint, Build, Unit-/Integrationstests und HTTPS-Smoke-Tests sind grün.
  • Keine Credentials, Produktionsdaten oder Formeln im Repository.

Nicht in v1

  • Kein vollständiges CMS.
  • Kein User-/Role-System.
  • Keine Kommentare, Likes, Suche oder Analytics.
  • Keine Webhooks/Event-Sourcing-Struktur.
  • Kein PostgreSQL/Redis/Kafka ohne gemessenen Bedarf.
  • Keine direkte Kopplung an Batchmaker-interne Datenbanktabellen.

ponytail: SQLite + separater API-Service ist die kleinste belastbare Grenze. Auf PostgreSQL/OIDC migrieren, sobald Multi-Author-Redaktion, hohe Write-Last oder vorhandene DB-Governance das rechtfertigt.


Session handoff: Start mit Abschnitt 1, dann Architektur-Gate vor dem ersten Code. Nach jeder implementierten Invariante Tests ausführen; Deployment erst nach Persistenz- und Traefik-Probe.

Constitution note: Die geladene batchmaker-Constitution beschränkt Repo-Operationen auf /home/eldov-ryzen5/workspace/Coding/Batchmaker/; dieses Portfolio-Repository liegt außerhalb. Für die nächste Session muss die zuständige Portfolio-/CEO-Freigabe bzw. der korrekte Projektkontext geklärt werden, bevor Batchmaker-Repositories verändert werden.

Created: 2026-08-16 Status: Proposed No implementation performed.