Files
immich-folio/docs/brainstorm-2026-08-05.md
Ralksta 10ec68f493 release: v0.12.0 (#520)
Forty-one commits since v0.11.0. See CHANGELOG.md for the full list and the upgrade notes.
2026-08-23 23:18:14 +02:00

338 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Feature-Brainstorm: Verbreitung & Robustheit
_Stand: 2026-08-05 · Basis: v0.9.1 · Fokus: Open-Source-Verbreitung + Betrieb & Robustheit_
Dieses Dokument ergänzt [`ideas.md`](ideas.md) (Präsentations-Features) und
[`brainstorm.md`](brainstorm.md) (Stand Mai 2026). Es wiederholt deren Ideen nicht,
sondern nimmt eine andere Perspektive ein: **Was hindert andere Fotografen daran,
Folio einzusetzen — und was geht im Betrieb kaputt?**
Alle Ideen sind gegen den tatsächlichen Code-Stand geprüft. Aufwand: XS (Stunden),
S (1 Tag), M (2–5 Tage), L (Wochen).
---
## Kontext: Was seit Mai dazugekommen ist
Aus `brainstorm.md` bereits erledigt: **Map-View** (`app/map/page.tsx`,
`components/MapView.tsx`), Video-Support, Webhook-Invalidierung, Album-Beschreibungen.
Weiterhin offen aus den alten Listen (hier bewusst _nicht_ nochmal ausgeführt):
RSS-Feed, Slideshow, Download-Button, Share-Link, Fullscreen, Timeline-View,
Immich Smart Search, Photo Stories, PWA, Watermarking, Client-Proofing, EXIF-Analytics.
---
## A. Betrieb & Robustheit
### A1. Fehler-, Lade- und 404-Boundaries — **XS, hoher Impact**
**Befund:** Im gesamten `app/`-Verzeichnis existiert keine einzige `error.tsx`,
`global-error.tsx`, `loading.tsx` oder `not-found.tsx`.
Weil alle Seiten `dynamic = 'force-dynamic'` sind und live gegen Immich laufen,
heißt das: Immich neu gestartet, API-Key rotiert, Netzwerk kurz weg → der Besucher
bekommt die generische Next.js-Fehlerseite. Ein Tippfehler in der URL → ebenfalls
ungestylt. Für ein Produkt, dessen Verkaufsargument „schön" ist, ist das der
peinlichste mögliche Zustand.
**Umsetzung:** `app/error.tsx` und `app/global-error.tsx` im Theme-Look mit
Retry-Button; `app/not-found.tsx`; `loading.tsx` pro Route-Segment mit
ThumbHash-Skeletons (`lib/thumbhash.ts` liefert die Bausteine schon).
**Warum zuerst:** Billigste Maßnahme mit der größten Wirkung auf den
wahrgenommenen Reifegrad.
---
### A2. Stale-while-error — Galerie bleibt online, wenn Immich weg ist — **M, hoher Impact**
**Befund:** `lib/cache.ts` ist ein 200-Einträge-LRU mit harter TTL (`CACHE_TTL`,
Default 300s). Läuft ein Eintrag ab und Immich antwortet nicht, gibt es keinen
Fallback — die Seite ist tot.
**Idee:** Abgelaufene Einträge nicht wegwerfen, sondern als _stale_ markieren.
Bei Upstream-Fehler den veralteten Eintrag trotzdem ausliefern, im Hintergrund
erneut versuchen, und (optional konfigurierbar) einen dezenten Hinweis
einblenden. Das öffentliche Portfolio überlebt damit ein Immich-Update,
ein Backup-Fenster oder einen NAS-Reboot.
**Warum das zieht:** Genau das ist die Angst des Self-Hosters — „meine
öffentliche Seite geht offline, weil mein privater Server hustet". Folio als
_Puffer_ vor Immich zu positionieren statt als bloßen Proxy ist ein echtes
Argument.
**Anker:** `lib/cache.ts`, `lib/immich.ts` (Request-Coalescing ist schon da und
passt gut dazu).
---
### A3. Persistenter Bild-Cache auf Platte — **M, hoher Impact auf schwacher Hardware**
**Befund:** `streamAsset()` proxied jedes Bild bei jedem Kaltstart erneut von
Immich. Der LRU-Cache hält nur Metadaten, keine Bytes.
**Idee:** Optionales Cache-Volume (`CACHE_DIR`), in dem proxied Thumbnails und
Previews pro Größen-Tier landen. Die Asset-Tokens sind bereits deterministisch
(`lib/tokens.ts`: gleiche UUID → gleicher Token), also ist der Cache-Key
geschenkt. Mit LRU-Eviction und konfigurierbarem Größenlimit.
**Warum:** Die typische Zielgruppe hostet auf Synology, RasPi oder einem alten
NUC. Dort ist wiederholtes Proxying der Flaschenhals — und es hält gleichzeitig
den Immich-Server unter Last.
---
### A4. ISR statt `force-dynamic`, getrieben vom vorhandenen Webhook — **M**
**Befund:** Alle Seiten sind `force-dynamic`. Gleichzeitig existiert bereits
`POST /api/webhook` mit HMAC-Signatur und gezielter Cache-Invalidierung.
**Idee:** Seiten auf Next.js-Cache-Tags umstellen (`albums`, `album-<id>`) und
den Webhook `revalidateTag()` aufrufen lassen statt nur den internen LRU zu
leeren. Ergebnis: statisch schnelle Auslieferung, trotzdem sofort aktuell,
sobald sich in Immich etwas ändert.
**Achtung:** Passwortgeschützte Subpages/Alben müssen dynamisch bleiben —
die Cookie-Prüfung in `lib/auth.ts` darf nicht wegcachen. Das ist die eigentliche
Arbeit an dieser Idee, nicht das Umstellen selbst.
_Stand in `ideas.md` schon als Einzeiler; hier mit dem Webhook als Auslöser
konkretisiert._
---
### A5. Config-Doctor — **S, hoher Impact auf Support-Aufwand**
**Befund:** `GET /api/admin/status` existiert bereits als Ansatzpunkt. Es gibt
aber keine Stelle, die typische Fehlkonfigurationen aktiv meldet.
**Idee:** Ein Diagnose-Panel im Admin (plus `npm run doctor` fürs Terminal), das
prüft:
- `AUTH_SECRET` gesetzt und ausreichend lang?
- `TRUSTED_PROXY_HOPS` plausibel? — tatsächlich eingehende `X-Forwarded-For`-Kette
messen und mit dem konfigurierten Wert vergleichen. Ein falscher Wert legt das
Rate-Limiting still oder macht es spoofbar; heute merkt das niemand.
- Existieren alle Album-IDs aus `gallery.yaml` noch in Immich?
- Reichen die Permissions des API-Keys?
- Liegen Passwörter im Klartext statt als `scrypt:`-Hash? (`lib/auth.ts` warnt
bislang nur in die Logs, wo es keiner liest.)
- Ist `content/` beschreibbar? (war real ein Bug — siehe Commit-History)
**Warum:** Fast jedes „geht bei mir nicht"-Issue eines Self-Hosting-Projekts ist
eine dieser Zeilen. Der Doctor beantwortet sie, bevor das Issue aufgemacht wird.
---
### A6. `AUTH_SECRET` automatisch bereitstellen — **XS**
**Befund:** `lib/secret.ts` wirft in Production hart, wenn `AUTH_SECRET` fehlt.
Korrekt gedacht (nicht auf eine ratbare Konstante zurückfallen), aber es
bedeutet: `docker run` ohne gesetztes Secret → App startet nicht, mit einem
Fehler, den Einsteiger nicht sofort einordnen.
**Idee:** Fehlt die Env-Variable, beim ersten Start ein Zufalls-Secret erzeugen
und nach `content/.secret` (Mode 0600) persistieren. Weiterhin warnen, aber
nicht blockieren. Env-Variable behält Vorrang.
**Trade-off, der dokumentiert gehört:** Das Secret liegt dann im Content-Volume
statt in der Umgebung. Für ein Self-Hosted-Portfolio ist das der richtige
Kompromiss — das Volume enthält ohnehin die Galerie-Passwörter.
---
### A7. Strukturierte Logs + optionale Metriken — **S bis M**
Heute: `console.warn`-Prosa. Vorschlag: JSON-Logzeilen hinter `LOG_LEVEL`, plus
optionales `GET /api/metrics` im Prometheus-Textformat mit Cache-Hit-Rate,
Immich-Latenz-Histogramm, Rate-Limit-Treffern und Bytes durch den Bild-Proxy.
**Warum es sich lohnt:** Die Self-Hosting-Community betreibt Grafana. Ein
Projekt, das ein fertiges Dashboard-JSON mitliefert, wirkt sofort erwachsen —
und es ist wenig Arbeit, weil die Zählpunkte (`lib/cache.ts`, `lib/immich.ts`,
`lib/rate-limit.ts`) schon zentralisiert sind.
---
### A8. Redis-Backend, aber nur optional — **M, niedrige Priorität**
Rate-Limiter und Cache sind ausdrücklich In-Memory und skalieren nicht über
mehrere Instanzen. Ein optionales Backend hinter `REDIS_URL` würde das lösen.
**Ehrliche Einschätzung:** Die Zielgruppe fährt einen Container. Das ist
Dokumentation eines bekannten Limits, kein dringendes Feature. Nur angehen, wenn
konkret danach gefragt wird.
---
## B. Open-Source-Verbreitung
### B1. Setup-Assistent im Browser statt Anleitung — **M, größter Adoption-Hebel**
**Befund:** `components/SetupScreen.tsx` erklärt dem Nutzer, welche Dateien er
per Hand kopieren und ausfüllen soll. Gleichzeitig kann das Admin-Panel bereits
YAML atomar schreiben (`lib/admin/yaml-service.ts`) und alle Immich-Alben
durchsuchen (`/api/admin/albums`) — die Bausteine für einen echten Assistenten
liegen also schon da.
**Idee:** Aus dem Setup-Screen einen geführten Ablauf machen:
1. Immich-URL + API-Key eingeben → Verbindung sofort testen, Permissions prüfen
2. Aus den gefundenen Shared Albums auswählen (visuelle Auswahl, keine UUIDs)
3. Theme-Preset aus Vorschaukarten wählen (die Screenshots existieren in `docs/screenshots/`)
4. Titel/Untertitel eintragen → fertig, Galerie steht
Schreibt `gallery.yaml`, `settings.yaml` und die Credentials. Absicherung: nur
erreichbar, solange `needsSetup` gilt — danach dicht.
**Warum das die wichtigste Einzelmaßnahme ist:** Der Unterschied zwischen
„funktioniert nach fünf Minuten" und „ich muss erst UUIDs aus der Immich-API
kopieren" entscheidet, ob jemand dabei bleibt. Immich selbst hat diesen
Onboarding-Standard gesetzt; Folio wird daran gemessen.
---
### B2. Demo-Modus ohne Immich-Instanz — **S bis M**
**Befund:** Ohne laufenden Immich lässt sich Folio überhaupt nicht ansehen.
Auch die E2E-Tests (`e2e/`) hängen deshalb an einer echten Instanz.
**Idee:** `DEMO_MODE=true` schaltet `lib/immich.ts` auf einen Fixture-Adapter mit
mitgelieferten, frei lizenzierten Bildern um. Drei Effekte auf einen Schlag:
- Eine öffentliche Live-Demo, die man im README verlinken kann
- E2E-Tests laufen deterministisch in CI, ohne Immich
- Interessenten können `docker run` machen und sofort etwas sehen
**Warum:** Bei Self-Hosted-Software ist die Live-Demo der Konversionspunkt.
Screenshots im README reichen nicht — Leute wollen durchklicken.
---
### B3. Deploy-Templates für die Kanäle, wo die Zielgruppe sucht — **S**
Docker-Compose ist vorhanden. Was fehlt, sind die Verzeichnisse, in denen
Immich-Nutzer tatsächlich stöbern: unRAID Community Applications, CasaOS- und
Umbrel-App-Store, Portainer-Stack, Coolify-/Dokploy-Template.
Wenig Code, überwiegend Metadaten und PRs an fremde Repos. Aber es ist der
Unterschied zwischen „muss man kennen" und „findet man".
---
### B4. Internationalisierung (DE/EN) — **M**
Steht in der alten Liste, hier aber mit anderer Begründung: nicht als
Besucher-Komfort, sondern als Verbreitungs-Voraussetzung. Das Projekt hat
deutsche Wurzeln (`app/impressum/page.tsx`, das `legal:`-Feld ist auf § 18 MStV
gemünzt), die Oberfläche ist Englisch, und deutsche Nutzer stolpern über den
Mix. Umgekehrt schreckt ein „Impressum" im Footer internationale Nutzer ab,
solange nicht klar ist, dass es abschaltbar ist.
**Reihenfolge:** Erst die Besucher-Oberfläche, dann das Admin-Panel. Ohne
Framework — ein einfacher Wörterbuch-Lookup gegen `settings.yaml: locale`
genügt und passt zur Vanilla-CSS-/Kein-Framework-Linie des Projekts.
---
### B5. Update-Hinweis im Admin-Dashboard — **XS bis S**
Version aus `package.json` gegen die GitHub-Releases-API prüfen (gecacht,
täglich, abschaltbar) und im Admin-Dashboard dezent anzeigen. Standard bei
Self-Hosted-Tools; Nutzer laufen sonst monatelang auf alten Versionen —
insbesondere problematisch, weil es bereits Security-Releases gab (`37f7f62`).
---
### B6. Themes teilbar machen — **S**
Export/Import von `settings.yaml` als „Theme-Paket" plus ein
`themes/community/`-Verzeichnis im Repo, in das Nutzer per PR ihre Presets
beisteuern.
**Warum:** Es schafft einen Beitragsweg für Leute, die kein TypeScript
schreiben. Fotografen mit Gestaltungsanspruch sind genau die Zielgruppe — und
jeder eingereichte Look ist zugleich Werbematerial.
---
### B7. Tests für die API-Routen — **M**
`lib/__tests__/` deckt die Bibliotheken gut ab (Tokens, Auth, Rate-Limit,
Config), `e2e/` deckt fünf Seiten ab. Die Routen selbst — Bild-Proxy,
Auth-Endpoint, die sieben Admin-Routen — haben keine direkten Tests.
Das ist ausgerechnet die Schicht, an der die Sicherheitsgarantien des Projekts
hängen: Allowlist-Durchsetzung, Token-Decoding, Rate-Limit-Namespaces,
Admin-Session-Prüfung. Nach `37f7f62` („fix pre-auth bypasses") sollte hier eine
Regressions-Suite liegen. Sie ist außerdem das, was Contributors Sicherheit
gibt, überhaupt PRs zu stellen.
---
### B8. Kleinigkeit: Projektname in `package.json` — **XS** — ✅ erledigt
`"name": "immich-lightbox"` bei einem Projekt namens Immich Folio. Fällt jedem
Contributor beim ersten `npm install` auf. Heißt seit dem Housekeeping-PR
`immich-folio`.
---
## C. Ein größerer Differenziator
### C1. Mehrere Galerien aus einer Instanz — **L**
Heute: ein Container, ein Portfolio. Denkbar: mehrere Sites aus einer Instanz,
je nach Host-Header, mit eigener `gallery.yaml`, eigenem Theme und eigener
Domain — bei geteiltem Immich-Backend und geteiltem Bild-Cache.
**Zielgruppen:** Fotografen-Kollektive; Leute, die für Kunden hosten; jemand,
der öffentliches Portfolio und private Familiengalerie trennen will.
**Ehrliche Einschätzung:** Deutlich mehr Aufwand als alles andere hier, weil
Config-Singleton (`lib/config/index.ts`), Cache-Keys und Auth-Cookie-Namen alle
mandantenfähig werden müssten. Nur sinnvoll, wenn tatsächlich Nachfrage
entsteht — aber es wäre etwas, das kein vergleichbares Tool bietet. Als
Beobachtungsposten notieren, nicht als Plan.
---
## Empfohlene Reihenfolge
**Erst (kleiner Aufwand, sofort sichtbar):**
1. A1 — Fehler-/Lade-/404-Boundaries · _XS_
2. A6 — `AUTH_SECRET` automatisch bereitstellen · _XS_
3. B8 — Projektname korrigieren · _XS_
4. B5 — Update-Hinweis im Admin · _XS–S_
**Dann (die zwei Hebel, die den Unterschied machen):**
5. B1 — Setup-Assistent im Browser · _M_
6. A2 — Stale-while-error · _M_
**Danach:**
7. A5 — Config-Doctor · _S_
8. B2 — Demo-Modus · _S–M_
9. B3 — Deploy-Templates · _S_
10. A3 — Persistenter Bild-Cache · _M_
11. B7 — API-Routen-Tests · _M_
**Später / nach Bedarf:** A4 (ISR), A7 (Logs & Metriken), B4 (i18n), B6 (Theme-Sharing),
A8 (Redis), C1 (Multi-Site).
---
## Roter Faden
Zwei Sätze, die die Auswahl zusammenhalten:
**Verbreitung** entscheidet sich in den ersten fünf Minuten — deshalb
Setup-Assistent (B1) und Demo-Modus (B2) vor allem anderen.
**Robustheit** heißt bei Folio nicht Uptime der eigenen App, sondern
Unabhängigkeit von der Verfügbarkeit des privaten Immich — deshalb ist
Stale-while-error (A2) das strategisch wichtigste Backend-Feature, nicht ISR.