m10z 2.0 technischer Reboot

m10z 2.0 technischer Reboot

Mein Weihnachtsurlaubsprojekt 2025. Nichts Geringeres als der komplette technische Reboot von mindestens 10 Zeichen.

Intro

Irgendwie nehme ich mir für jeden Weihnachtsurlaub ein (zu?) großes, mindestens 10 Zeichen, Projekt vor.

In 2023 habe ich Gothic 1 Classic auf der Switch 1 als launiges Kurzreview inklusive Screenshot-Story begleitet und in 2024 habe ich einen ausführlichen Kommentar über die Merkmale eines erfolgreichen Podcasts geschrieben.

Joa, was sollte ich mir dann wohl für 2025 vornehmen … Es wurde nichts anderes als der komplette technische Reboot von Mindestens 10 Zeichen. Und dazu dieser Post, wird etwas technisch, aber mei. So ist es halt. ;-)

Nachfolgend zeige ich in diesem Post unsere komplette Content-Delivery-Kette – vom Erstellen im CMS über Speicher & Datenbank bis hin zu Cache-Invalidierung, Next.js-Frontend und den beiden Feed-Endpunkten.

TL:DR

Vorher

  1. Text als Markdown per Git Commit ins Repository
  2. Bilder als externer Link oder ebenfalls direkt ins Repository
  3. MP3 Audiodatei über Adrian auf seinen privaten Server
  4. Podcast Episoden als Markdown Post – werden dann zur build-time per Script aggregiert und als XML ausgeliefert. (Dies ist bereits die dritte Iteration des Feed Scripts).
    • Initial: XML per Hand geschrieben
    • Iteration: XML aus manuell gepflegter .yaml Datei generiert
    • Final: XML aus Markdown Dateien pro Episode generiert
  5. Docusaurus stellt das Frontendframework und den optischen Rahmen der Seite

Nachher

  1. Full Self-Service: Inhaltsverwaltung per Content-Management-System
  2. Automatisches Publishing und Rendering im NextJS Frontend per technisch und optisch moderner Webseite

Überblick: Technische Bausteine und Konzepte:

  • Strapi (CMS): Autor:Innen pflegen Artikel, Podcasts, Autor:Innen/Teams und Kategorien.
  • PostgreSQL: Speichert Content-Metadaten, Relationen, Feeds/Single etc.
  • Hosting und Speicher: Uploads liegen auf dem Server im Dateisystem – tägliche automatische Backups inklusive.
  • Next.js Frontend: Rendert Seiten, liefert RSS-Feeds aus und cached serverseitig.
  • Cache-Invalidierung: Bei Content-Änderungen pingt Strapi gezielt Invalidation-Endpunkte im Frontend an, damit neue Inhalte sofort sichtbar sind.

Highlevel Architektur Diagram

Click to view full size

Das Diagramm: einmal „alles“

Click to view full size

Tipp: Das Diagramm ist anklickbar und öffnet sich groß (Lightbox), damit man es lesen und zoomen kann.

Schritt 1: Content entsteht im CMS.

Der Einstieg ist klassisch: Autor:innen erstellen/ändern Inhalte in Strapi. Dazu gehören Text (Artikel, Shownotes), Metadaten (Titel/Slug/Teaser), Relationen (Autor:innen/Kategorien) und Medien (Cover/Banner/MP3s).

Beim Speichern/Publizieren passieren dann mehrere Dinge parallel:

  • Persistenz: Strapi schreibt Content nach PostgreSQL.
  • Wortanzahl: Für Artikel und Shownotes werden die Worte im Hauptcontent gezählt und am Inhalt gespeichert.
  • MP3 Dauer: Für den Audiofeed benötigen wir die MP3 Dauer in Sekunden. Damit das nicht manuell erfasst und eingetragen werden muss, nutzen wir die [Music-Metadata Library] (https://github.com/Borewit/music-metadata), um die Datei zu analysieren und die Dauer abspeichern zu können!
  • Je nach editiertem Content schickt das CMS eine Anfrage an Endpunkte im Frontend (dem Node Server Prozess hinter https://m10z.de), um den Cache zu leeren.

Änderungen triggern Cache-Invalidierung

Damit neuer Content direkt bei Änderungen auf der Website sichtbar ist, müssen wir den Cache leeren.

Darum hängt sich im Strapi-Backend eine Middleware in den Dokumenten-Lifecycle (Publish/Update/etc.). Nach einem erfolgreichen Schreiben ruft sie gezielt Invalidierung-Endpunkte im Next.js-Frontend auf.

Das hat zwei große Vorteile:

  • Sofortige Aktualität: Kein „wartet, bis TTL (Time-To-Live) abläuft“.
  • Gezielte Invalidierung: Nur betroffene Tags/Pages werden neu gebaut.

Next.js Caching (Tags + Fallback-TTL):

Im Next.js-Frontend setzen wir auf ein zweistufiges Cache-Modell:

  • Tag-based Invalidation (Primär): Seiten/Fetches hängen an Cache-Tags wie strapi:article, strapi:podcast, feed:article, feed:audio, page:home, search:index.
  • Time-based Revalidation (Fallback): Falls mal keine Invalidierung durchkommt, greift ein Revalidation-Window (z.B. 15–60 Minuten, je nach Content-Typ).

Wenn Strapi eine Änderung meldet, ruft Next.js z.B. revalidateTag(...)/revalidatePath(...) auf, und die nächsten Requests ziehen frische Daten.

Schritt 4: Zwei „Feed“-Welten (Seiten vs. RSS)

Unsere Zielgruppen sind zweifach:

  • Menschen browsen Seiten wie /, /artikel, /podcasts, /kategorien.
  • Bots/Apps holen XML – konkret:
    • /rss.xml (Artikel-Feed).
    • /audiofeed.xml (Podcast-Feed).

Die Feeds sind „Non-UI“-Routes (Api Routes), liefern XML aus und sind zusätzlich on-disk gecached (damit wir nicht bei jeder Feed-Anfrage alles neu bauen müssen).

Um die Performance zu gewährleisten, sowie die Last auf dem Server gering halten zu können, erstellen wir per Script alle 30 Minuten den jeweils aktuellen Artikel- und Audiofeed und speichern diesen auf dem lokalen Filesystem. Anfragen nach /audiofeed.xml und /rss.xml liefern nun einfach diese statische XML Datei aus, ohne etwas neu generieren zu müssen.

Security & Betrieb (kurz, aber wichtig):

– Invalidation-Endpunkte sind geschützt: Strapi ruft sie mit einem Secret-Header auf (Token steht nur als Env, nie im Client).

  • Rate-Limits: Die Endpunkte sind pro IP begrenzt, um Missbrauch abzufedern.
  • Hetzner (DSGVO konform, Hosting komplett in Deutschland) erstellt automatische tägliche Backups
  • Coolify: Unsere Platform as a Service Lösung kümmert sich um die technische Orchestration der Docker Container sowie automatischen Re-Deployment bei Änderungen im Code. Coolify erstellt ebenfalls tägliche Backups der CMS Datenbank.

Sequenz Diagram

Click to view full size

Warum das Setup gut funktioniert?

  • Schnell: Next cached aggressiv, Feeds sind zusätzlich auf Platte.
  • Aktuell: Strapi invalidiert gezielt bei Änderungen.
  • Skalierbar: Bots können Feeds pollen, ohne das Backend zu killen.
  • Anpassbar: Ohne Abhängigkeiten zu „fertigen” Frontendlösungen können wir bauen und designen, was wir wollen.

Fazit

Es waren intensive zwei Wochen. Emotionale Achterbahn gegen Ende, aber dann doch mit erfolgreichem Launch zum 31.12.2025. Ich bin froh und erleichtert – und gleichzeitig ziemlich stolz. Das CMS ermöglicht nun sämtlichen M10Z RedakteurInnen das eigenständige Anlegen und Verwalten von Artikeln und Podcast Episoden. Weder Adrian noch ich sind jetzt der Flaschenhals – außer das System crasht mal. ;-)

Die neue https://m10z.de Seite ist, mit Verlaub, wirklich schick geworden. Sollte euch doch etwas auffallen, oder ihr habt Verbesserungswünsche, gerne direkt als Issue in GitHub.


Wenn euch ein Teil davon genauer interessiert (z.B. Feed-Caching, Tag-Strategie oder Search-Index), schreibt gerne im Discord.

Luca