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

240 lines
13 KiB
Markdown

# 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.**