Files
immich-folio/docs/journal-studio-design.md
Ralksta 2f546f440d release: v0.11.0 (#459)
Ships `dev` as **v0.11.0**. `main` contains nothing that `dev` does not,
so this is a clean sync.

## Headline changes since v0.10.0

- **Journal & Journal Studio** — photo essays at `/journal`,
split-screen block editor with live preview, drafts, per-entry
passwords, cover images
- **First-run setup wizard at `/install`** — configure a fresh
deployment from the browser; credentials are verified against Immich
before anything is written, and the wizard is gated by a one-time token
- **About page editor and toggle** in the admin panel
- **Per-album photo order** independent of Immich, with a drag & drop
editor
- **Custom favicon upload**
- **Experimental portfolio features** — justified grid, `cover` hero,
unlisted subpages, per-album grid overrides, cover focal points,
external nav links
- **Security** — asset tokens length-capped before decoding (#423),
journal author markdown escaped rather than filtered, journal file paths
contained, admin session key derived with scrypt (#432), Next.js 16.3.0
for CVE-2025-13465
- **Fixed** — client proofing honours its own configuration again
(#454); journal photo rendering (#432)

Full detail in [CHANGELOG.md](CHANGELOG.md#0110--2026-08-15).

## Verified

`npx tsc --noEmit` clean · `npx vitest run` 435 tests pass · `npm run
build` succeeds · lint 0 errors · `prettier --check` clean · local
`docker build` of the release tree.

## After merge

Tag `v0.11.0` on `main` and publish the release — that triggers
`docker-publish.yml`, which pushes the image and moves `latest`.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 09:09:18 +02:00

119 lines
6.5 KiB
Markdown

# Design-Spezifikation: Journal Studio (Foto-Essays & Stories)
_Status: Entwurf genehmigt (Brainstorming abgeschlossen) · Datum: 2026-08-14_
---
## 1. Understanding Summary
- **Ziel & Vision:** Bereitstellung eines intuitiven, erstklassigen **Journal Studios** für Fotografen, um Bild-Text-Reportagen, Reisetagebücher und Ausstellungsberichte (im Stil von Leica Journal / National Geographic) zu verfassen und zu veröffentlichen.
- **Zielgruppe:**
- **Besucher:** Immersives, typografisch elegantes Leseerlebnis mit progressiv ladenden Bildern (ThumbHash), responsiven Layouts und Lightbox-Interaktion.
- **Fotograf:** Mühelose Redaktion im Admin-Panel mit Live-Split-Screen-Vorschau und universeller Immich-Bildauswahl.
- **Konzept:** Reines Markdown mit Frontmatter im Content-Volume (`content/journal/*.md`), verwaltet über eine dedizierte Admin-UI und ausgeliefert über saubere Next.js Server Components.
- **Kern-Vorteile:**
- Trennung von Galerie-Konfiguration (`gallery.yaml`) und redaktionellen Inhalten.
- Volle Git- & Backup-Kompatibilität durch Standard-Markdown.
- Schnelle Erstellung ohne manuelle UUID-Eingabe.
---
## 2. Annahmen (Assumptions)
1. **Storage-Pfad:** Journal-Einträge werden standardmäßig unter `content/journal/[slug].md` gespeichert.
2. **Bild-Referenzierung:** Bilder verweisen über `![Alt Text](asset:IMMICH_UUID)` auf Immich-Assets und werden über den internen Image-Proxy mit Cache und ThumbHash ausgeliefert.
3. **Theming:** Die Leseansicht (`/journal/[slug]`) bindet sich nahtlos an das global konfigurierte Design-Theme an (CSS-Variablen, Fonts, Margins).
4. **Resilienz:** Nicht auffindbare Bilder führen nicht zu einem Seitenabsturz, sondern werden durch neutrale Platzhalter ersetzt.
---
## 3. Decision Log
| # | Entscheidung | Alternativen | Begründung |
| --- | ------------------------------------------------ | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------ |
| 1 | **Nomenklatur "Journal"** | Stories, Photo Essays, Magazin | "Stories" wirkt wie flüchtige Social-Media-Inhalte. "Journal" spiegelt zeitlose, fotografische Wertigkeit (wie Leica Journal) wider. |
| 2 | **Architektur-Ansatz 1 (Eigenständiges Studio)** | Subpage-Drawer, Quick-Fix | Maximale Schreib- und Layout-Ergonomie durch echten Split-Screen und Entkopplung von `gallery.yaml`. |
| 3 | **Persistenz als `.md`-Dateien** | Strings in `gallery.yaml`, SQLite/DB | Behält das dateibasierte, zustandsarme Self-Hosting-Paradigma bei; einfache Versionierung via Git. |
| 4 | **Bidirektionaler Editor (Blöcke ↔ Markdown)** | Nur WYSIWYG, Nur Markdown | Fotografen können visuell Blöcke anordnen oder direkt mit Markdown-Code arbeiten, ohne Datenverlust. |
| 5 | **Sicherheit via Slug-Sanitisierung** | Ungeprüfte Dateinamen | Verhindert Path Traversal (`../`) im Docker-Container/Dateisystem. |
---
## 4. Technische Architektur & Komponenten
### A. Dateisystem & Datenmodell
Jeder Eintrag ist eine `.md`-Datei mit folgendem Schema:
```markdown
---
title: 'Expedition Nordkap'
subtitle: 'Mit dem Bulli durch die Fjorde'
date: '2026-08-14'
author: 'Ralf'
coverAssetId: 'b8c2d111-0000-4000-8000-000000000000'
password: '' # Optionaler Passwortschutz (scrypt)
draft: false # Entwurf oder öffentlich
---
Hier beginnt der Text der Reportage...
![Fjord im Abendlicht](asset:93f812ab-1111-4000-8000-000000000000)
```
Unterstützte Block-Typen:
- **Heading:** `# H1`, `## H2`, `### H3`
- **Paragraph:** Fliesstext mit Inline-Markdown (`**fett**`, `*kursiv*`, `[Link](url)`)
- **Pullquote:** `> Zitattext` mit optionalem `— Autor`
- **Photo (Fullbleed / Wide / Contained):** `![Caption](asset:UUID)`
- **Photo-Pair (2-Spaltig):** Zwei aufeinanderfolgende `asset:`-Bilder
---
### B. Admin API Endpunkte (`/api/admin/journal`)
- **`GET /api/admin/journal`**: Liefert Liste aller Einträge (geparstes Frontmatter, Slug, Cover-Thumbnail, Word Count / Read Time).
- **`GET /api/admin/journal/[slug]`**: Liefert vollen Inhalt & Metadaten eines spezifischen Eintrags.
- **`PUT /api/admin/journal/[slug]`**: Speichert den Eintrag atomar (Write to `.tmp` ➔ Rename), legt `.bak`-Sicherungen an und validiert den Markdown-Baum.
- **`DELETE /api/admin/journal/[slug]`**: Löscht die `.md`-Datei nach Bestätigung.
---
### C. UI: Das Journal Studio (`/admin` ➔ Tab "Journal")
1. **Journal Dashboard:**
- Übersicht aller Einträge als Karten mit Thumbnail, Status (_Draft_, _Live_, _Protected_), Erstellungsdatum.
- Button `+ New Journal Entry`.
2. **Studio Split-Screen Editor:**
- **Top Navigation:** Titel-Eingabe, Slug, Status-Schalter, Metadaten-Modal (Autor, Datum, Passwort, Cover-Foto) und Save-Button (`Cmd+S`).
- **Linker Bereich (Authoring):**
- Umschaltbar zwischen **[Visual Blocks]** (Karten mit Drag & Drop Griffleisten) und **[Raw Markdown]**.
- Universeller Asset-Picker für Fotos aus allen Immich-Alben.
- **Rechter Bereich (Live Preview):**
- 1:1 Rendering via `EssayView` mit den CSS-Variablen des aktiven Themes.
- Viewport-Umschalter (Desktop / Mobile).
---
### D. Frontend Routing & Rendering
- **`/journal`**: Übersicht aller veröffentlichten Journal-Einträge mit Cover-Bildern und Exzerpten.
- **`/journal/[slug]`**: Detailansicht der Reportage mit Passwort-Gate (`PasswordGate`) bei geschützten Einträgen.
- **Subpage-Integration:** Kompatibilität mit `gallery.yaml` (`essayFile: "nordkap.md"`) bleibt vollständig erhalten.
---
## 5. Test- & Absicherungsstrategie
1. **Unit-Tests (`lib/__tests__/journal.test.ts`):**
- Frontmatter-Parsing & -Serialisierung.
- XSS-Sanitisierung bei bösartigem Markdown-HTML.
- Block-Transformation (Fullbleed, Pairs, Quotes).
2. **API-Tests (`app/api/admin/journal/__tests__/route.test.ts`):**
- Auth-Schutz (401/403 bei nicht-angemeldeten Requests).
- Path-Traversal-Abwehr (Rejection von `../`, `%2e%2e%2f`).
- Atomare Speicherung und Fehlerbehandlung bei Disk-Fehlern.
3. **E2E / Integration:**
- Aufruf von `/journal/[slug]` im Browser und Verifikation des Lightbox-Klicks auf eingebettete Fotos.