12 KiB
Autoren-Portfolio-Suite
Eine selbst gehostete Portfolio-Anwendung für mehrere voneinander getrennte Autorenprofile. Inhalte, Bücher, laufende Projekte, Designs, SEO-Daten und Rechtstexte werden über eine gemeinsame, passwortgeschützte Administrationsoberfläche gepflegt.
Technisch besteht die Anwendung aus einem React-/Tailwind-Frontend und einem Express-Backend. Sie kann als einzelner Docker-Container betrieben werden und benötigt keine externe Datenbank.
Funktionsumfang
- Mehrere unabhängig konfigurierte Autorenprofile mit eigenen Domains und Themes
- Biografie, Schlagworte, individuelle Texte und optionale Zusatzsektion mit bis zu drei CTA-Buttons
- Bücherregal mit Detailansicht, Cover, Kauflink und optionaler Spotify-Playlist
- Aktuelle Projekte mit Fortschritt, Markdown-Detailtext, Bild und optionaler Spotify-Playlist
- Lokaler Bild-Upload über den Adminbereich
- Verwaltung von Impressum, Datenschutzerklärung und weiteren Rechtstexten
- Optionale serverseitige Gemini-Unterstützung für Klappentexte
- Profilabhängige SEO-Metadaten, Open Graph, strukturierte Daten, Sitemap und
robots.txt - Health-Endpunkte, persistente JSON-Datenhaltung und automatische Migrationsbackups
Trennung der Profile
Die öffentliche Seite erhält ausschließlich die Daten des Profils, das zur aufgerufenen Domain gehört. Andere Profile, deren Domains und interne Pfadkonfigurationen werden von der öffentlichen API nicht ausgeliefert.
Im Produktionsmodus gilt der Hostname als Trennlinie:
- Konfigurierte Domains reagieren nur auf die exakte Domain und deren
www-Variante. - Ein URL-Pfad oder Query-Parameter kann nicht zu einem anderen Profil wechseln.
- Pfadbasierte Profilvorschauen stehen nur in der lokalen Entwicklungsumgebung zur Verfügung.
- Vollständige Profildaten sind ausschließlich über einen authentifizierten Admin-Endpunkt verfügbar.
- Gemeinsam gepflegte Rechtstexte werden bei der öffentlichen Ausgabe von strukturiert bekannten Kennungen anderer Profile bereinigt. Die gespeicherten Originaltexte bleiben unverändert.
- Querverlinkungen entstehen nur durch ausdrücklich gepflegte Buttons oder Inhalte des jeweiligen Profils; es gibt keine automatische Verbindung zu anderen Profilen.
Der Reverse Proxy sollte zusätzlich nur die tatsächlich verwendeten Domains an den Container weiterleiten.
Architektur
Die öffentlichen Portfolioseiten verwenden eine gemeinsame, daten- und themegesteuerte React-Komponente. Dadurch liegen Layout, Buch- und Projektmodale, Rechtstexte sowie die Medienintegration nur einmal im Code vor. Profilbezogene Inhalte und Farben kommen aus der Datenbank; die Auswahl des Profils findet serverseitig statt.
Wichtige Verzeichnisse und Dateien:
src/App.tsx Öffentliche/Admin-Routen und Datenabruf
src/components/PortfolioPage.tsx Gemeinsame öffentliche Portfolioseite
src/components/AdminPanel.tsx Administrationsarbeitsbereich
src/components/admin/ Ausgelagerte Admin-Komponenten
src/defaultData.ts Ausgangsdaten für eine neue Installation
server.ts API, Routing, Sessions, Uploads und SEO
data/database.json Persistente Inhaltsdaten
data/uploads/ Lokal hochgeladene Bilder
data/backups/ Automatische Migrationsbackups
tests/ Integrations- und Sicherheitstests
Docker-Inbetriebnahme
1. Konfiguration anlegen
Kopiere .env.example nach .env und ersetze die Beispielwerte:
ADMIN_PASSWORD=EinLangesZufaelligesAdminPasswort
SESSION_SECRET=EineUnabhaengigeZufaelligeZeichenfolgeMitMindestens32Zeichen
# Optional: aktiviert den Klappentext-Assistenten
GEMINI_API_KEY=
# Nur setzen, wenn genau ein vertrauenswürdiger Reverse Proxy vorgeschaltet ist
# TRUST_PROXY=1
Für den Produktionsbetrieb gelten folgende Mindestanforderungen:
ADMIN_PASSWORD: gesetzt und mindestens 12 Zeichen langSESSION_SECRET: gesetzt, unabhängig vom Passwort und mindestens 32 Zeichen lang
Ohne gültige Werte startet der Server im Produktionsmodus bewusst nicht. Geheimnisse gehören ausschließlich in .env beziehungsweise in die Secret-Verwaltung der Betriebsumgebung und nicht in docker-compose.yml oder Git.
2. Container starten
docker compose up -d --build
Die Anwendung ist anschließend am veröffentlichten Container-Port 3000 erreichbar. In einer öffentlichen Installation sollte davor ein Reverse Proxy wie Caddy, Traefik oder Nginx Proxy Manager TLS übernehmen und ausschließlich die vorgesehenen Domains weiterleiten.
Wenn genau ein vertrauenswürdiger Proxy vorgeschaltet und Port 3000 nicht direkt öffentlich erreichbar ist, kann TRUST_PROXY=1 gesetzt werden. Das sorgt dafür, dass das Login-Limit die ursprüngliche Client-IP verwendet. Bei direkter Veröffentlichung des Ports darf diese Option nicht aktiviert werden.
Installation ohne Docker
Voraussetzung ist eine unterstützte Node.js-Version (empfohlen: Node.js 20 oder neuer).
npm install
npm run build
NODE_ENV=production \
ADMIN_PASSWORD="EinLangesZufaelligesAdminPasswort" \
SESSION_SECRET="EineUnabhaengigeZufaelligeZeichenfolgeMitMindestens32Zeichen" \
npm start
Optional können GEMINI_API_KEY, TRUST_PROXY, PORT und DATA_DIR ergänzt werden. Standardmäßig lauscht die Anwendung auf Port 3000 und verwendet ./data als Datenverzeichnis.
Für die lokale Entwicklung genügt:
npm install
npm run dev
Die im Entwicklungsmodus vorhandenen Fallback-Secrets sind ausschließlich für lokale Entwicklung gedacht und werden in Produktion nicht akzeptiert.
Admin-Authentifizierung und Schutzmaßnahmen
Das Admin-Passwort wird nur beim Login übertragen und weder als Bearer-Token zurückgegeben noch im Browser gespeichert. Nach erfolgreicher Anmeldung wird eine zufällige, serverseitig verwaltete Sitzung erzeugt.
- Sitzungsdauer: 24 Stunden
- Cookie:
HttpOnly,SameSite=Strict, in Produktion zusätzlichSecure - Sitzungen liegen nur im Arbeitsspeicher und enden spätestens bei einem Server-/Container-Neustart
- Abgelaufene Sitzungen führen im Adminbereich kontrolliert zurück zur Anmeldung
- Schreibende Admin-Anfragen werden auf gleiche Herkunft geprüft
- Fehlgeschlagene Logins werden pro Client-IP begrenzt
- Uploads und Gemini-Aufrufe besitzen zusätzliche Sitzungslimits
- Sicherheitsheader und eine Content Security Policy werden serverseitig gesetzt
- Profil- und Rechtstextänderungen verwenden eine Revision; parallele Änderungen werden mit einem Konflikthinweis abgelehnt statt überschrieben
Ein vergessenes Admin-Passwort wird über die Betriebsumgebung geändert; es gibt keine öffentliche Passwort-zurücksetzen-Funktion.
Datenhaltung und Backups
Alle Inhaltsdaten liegen in data/database.json. Docker bindet mit ./data:/app/data das vollständige Datenverzeichnis auf dem Host ein. Damit bleiben Daten und Uploads beim Neubauen oder Ersetzen des Containers erhalten.
Für ein vollständiges Backup sollte der gesamte Ordner data/ gesichert werden:
data/database.json
data/uploads/
data/backups/
Schreibvorgänge auf die JSON-Datenbank erfolgen atomar und nacheinander. Vor schemaändernden Migrationen legt die Anwendung eine unveränderte Sicherung in data/backups/ an. Diese lokalen Sicherungen ersetzen kein externes, regelmäßig getestetes Backup.
Bild-Uploads
Der Adminbereich akzeptiert JPEG, PNG, WebP, GIF und AVIF mit maximal 8 MB pro Datei.
Der Server:
- prüft Dateiendung, angegebenen MIME-Typ und den tatsächlichen Dateikopf,
- lehnt SVG, HTML und andere aktive Formate ab,
- erzeugt zufällige, nicht überschreibbare Dateinamen,
- begrenzt Uploadversuche pro Admin-Sitzung und Stunde,
- liefert Uploads mit
nosniffund langfristigen Cache-Headern aus. - erlaubt das Löschen unbenutzter Bilder in der Server-Mediathek, schützt aber Bilder, die noch in einem Profil referenziert werden.
Die Bilder können für Avatare, Banner, Buchcover und aktuelle Projekte verwendet werden. Leere Bildfelder erzeugen keinen Request zu einem externen Standardbild.
Optionale Buttons im Zusatzabschnitt
Im zusätzlichen Textabschnitt eines Profils können bis zu drei CTA-Buttons gepflegt werden. Jeder Button benötigt eine Beschriftung und eine vollständige http://- oder https://-Adresse. Unvollständige Einträge werden nicht angezeigt, andere URL-Schemata werden serverseitig abgelehnt.
Die Buttons öffnen das Ziel in einem neuen Tab und werden automatisch mit den Akzentfarben des Profils gestaltet. Auf kleinen Bildschirmen stehen sie untereinander, auf größeren Bildschirmen nebeneinander. Damit können ausgewählte Pseudonyme bewusst miteinander verknüpft werden, ohne dass daraus eine automatische Verlinkung zu weiteren Profilen entsteht.
Spotify und externe Dienste
Spotify-Playlists werden sowohl in Buch- als auch in Projektdetails nach dem Zwei-Klick-Prinzip eingebunden. Beim Öffnen eines Details erscheint zunächst nur ein lokaler Platzhalter. Erst nach einem bewussten Klick auf „Spotify-Player laden“ wird das Spotify-iframe erzeugt und eine Verbindung zu Spotify hergestellt.
Weitere mögliche externe Verbindungen:
GEMINI_API_KEY: Der optionale Admin-Assistent sendet die eingegebenen Buchinformationen serverseitig an die Google-Gemini-API.- Kauf- und sonstige Markdown-Links: Erst ein Klick führt zur jeweiligen externen Website.
- Externe Bild-URLs: Das System unterstützt sie weiterhin, empfohlen werden jedoch lokal hochgeladene Bilder.
Die Anwendung enthält kein Analytics- oder Tracking-System und lädt keine externen Webfonts. Sie verwendet für Besucher weder localStorage noch sessionStorage. Der Browser speichert lediglich das notwendige Admin-Sitzungscookie nach einer Anmeldung. Diese Punkte sowie das übliche Logging des Reverse Proxys/Hosters sollten passend zur tatsächlichen Installation in der Datenschutzerklärung beschrieben werden.
SEO und Betriebsendpunkte
/health/live: Prozess läuft/health/ready: Daten wurden geladen und der Server ist bereit/robots.txt: profilabhängige Crawling-Regeln/sitemap.xml: profilabhängige Sitemap/admin: mitnoindexgekennzeichneter Administrationsbereich
Seitentitel, Beschreibung, Canonical URL, Open-Graph-Daten und strukturierte Personendaten werden serverseitig für die aufgerufene Domain erzeugt. Optionale SEO-Felder können je Profil im Adminbereich gepflegt werden; fehlende Angaben werden aus bestehenden Profildaten abgeleitet.
Qualitätschecks
npm run typecheck # TypeScript-Typprüfung
npm test # Produktionsbuild und Integrationstests
npm run check # Typprüfung, Build und Tests
Die Integrationstests verwenden ein temporäres Datenverzeichnis und einen kurzlebigen lokalen HTTP-Server. Geprüft werden unter anderem:
- verpflichtende Produktions-Secrets,
- Admin-Sitzung und Logout,
- Origin-Schutz und Login-Limit,
- exaktes Domain-Routing und öffentliche Profilisolation,
- Schutz des vollständigen Admin-Datenendpunkts,
- Revisionskonflikte und serverseitige Inhaltsvalidierung,
- gültige und manipulierte Bild-Uploads,
- Löschung unbenutzter sowie Schutz referenzierter Uploads,
- wesentliche Sicherheitsheader.
Hinweise zur Aktualisierung
Vor einem Update sollte der komplette data/-Ordner gesichert werden. Danach kann das Image neu gebaut werden:
docker compose up -d --build
Vorhandene Inhaltsdaten werden nicht durch die im Quellcode enthaltenen Ausgangsdaten ersetzt. Neue optionale Felder werden migrationsfähig ergänzt; vor notwendigen Schemaänderungen wird automatisch ein Backup angelegt.