# 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:** ```bash 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`:** ```sql 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:** ```bash 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:** ```bash 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.**