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

6.5 KiB

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:

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