200 lines
10 KiB
Markdown
200 lines
10 KiB
Markdown
# 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
|
|
- 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.
|
|
|
|
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:
|
|
|
|
```text
|
|
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:
|
|
|
|
```dotenv
|
|
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 lang
|
|
- `SESSION_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
|
|
|
|
```bash
|
|
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).
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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ätzlich `Secure`
|
|
- Sitzungen liegen nur im Arbeitsspeicher und enden spätestens bei einem Server-/Container-Neustart
|
|
- 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
|
|
|
|
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:
|
|
|
|
```text
|
|
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 `nosniff` und langfristigen Cache-Headern aus.
|
|
|
|
Die Bilder können für Avatare, Banner, Buchcover und aktuelle Projekte verwendet werden. Leere Bildfelder erzeugen keinen Request zu einem externen Standardbild.
|
|
|
|
## 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`: mit `noindex` gekennzeichneter 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
|
|
|
|
```bash
|
|
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,
|
|
- gültige und manipulierte Bild-Uploads,
|
|
- wesentliche Sicherheitsheader.
|
|
|
|
## Hinweise zur Aktualisierung
|
|
|
|
Vor einem Update sollte der komplette `data/`-Ordner gesichert werden. Danach kann das Image neu gebaut werden:
|
|
|
|
```bash
|
|
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.
|