release: v0.19.0

Release v0.19.0. See the `## [0.19.0] — 2026-09-29` section of
[CHANGELOG.md](https://github.com/ralksta/immich-folio/blob/dev/CHANGELOG.md)
for the full list.

Highlights: client proofing links (#683), custom content pages (#738),
asset picker search (#740), concurrent-edit detection (#742), env-locked
settings (#739), an accessibility baseline (#741), three security fixes
with advisories (#737, #755, #759), and a pre-release quality pass
(#780–#797).

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Ralf <rohlenbo79@gmail.com>
Co-authored-by: Paul Thies <110185070+ImScheinox@users.noreply.github.com>
Co-authored-by: lancetm714 <108419425+lancetm714@users.noreply.github.com>
Co-authored-by: Lance Martin <lancetaylor95@gmail.com>
This commit is contained in:
Ralksta
2026-09-29 22:50:30 +02:00
committed by GitHub
parent 5f251923f0
commit ad8b09700b
297 changed files with 19398 additions and 1800 deletions

View File

@@ -35,6 +35,7 @@ cp content/settings.yaml.example content/settings.yaml
- [Site Behaviour](#site-behaviour)
- [Navigation Links](#navigation-links)
- [Client Proofing](#client-proofing)
- [Client Proofing Links](#client-proofing-links)
- [Image Protection & Watermark](#image-protection--watermark)
- [SEO](#seo)
- [Footer](#footer)
@@ -269,6 +270,12 @@ so an edited URL cannot reach anything else. The ZIP keeps the original
filenames (deduplicated on collision) and is streamed, so album size does not
drive memory use.
One visitor (by client IP, see `TRUSTED_PROXY_HOPS`) can have at most two ZIPs
downloading at once, album and proofing downloads counted together; a third
gets a "too many requests" page until one finishes. A ZIP whose download stops
reading for 60 seconds is closed, so an abandoned or stalled download does not
hold its connection to Immich open.
## Password Protection
A password can be set on the whole site, on a subpage, on a single album, and on
@@ -617,8 +624,16 @@ Configured in `settings.yaml`:
proofing:
enabled: true # hearts, selection bar and export modal (default: true)
allowMailto: true # offer "send by email" alongside the copyable list (default: true)
email: proofs@example.com # recipient of that email (default: footer.email)
```
The email draft goes to `proofing.email`, or to the footer contact email
(`footer.email`) when that is unset. With neither configured the dialog hides
the email button rather than opening a draft with an empty **To:**. The address
is set in the admin panel under **Settings → General → Proofing email**, and it
reaches the page encoded like the footer address, so it does not appear in the
HTML in plain text.
A subpage overrides `enabled` in either direction — useful to keep proofing off
across a public portfolio and switch it on for a single client handover:
@@ -641,6 +656,59 @@ album reached without a subpage follows `proofing.enabled`.
> controls only when its subpage sets `proofing: true` **explicitly** — the
> global default does not reach into essays. Journal entries never show them.
## Client Proofing Links
The proofing above is anonymous: picks live in the visitor's browser and come
back to you as a link or an email, if they come back at all. A **client
proofing link** is the handover version. Create one per client in
**Admin → Proofing**:
- **Client** — a name for your overview and the greeting on the page.
- **Album** — any Immich album. It does not have to be in `gallery.yaml`; the
link itself is what grants access to it.
- **Valid until** — optional. From the day after, the link shows "This link has
expired" and every action on it is refused.
- **Downloads** — none, the client's selection as a ZIP, or the whole album as
a ZIP, with an optional limit on how many ZIPs the link may start.
The client opens `https://your-site/proof/<token>` and hearts photos as usual.
Every change is **saved on the server** within a second, so they can stop and
come back later, from another device too. **Submit selection** locks it and
notifies you. Until then, you can watch the picks come in under
**Admin → Proofing**. **Reopen for changes** unlocks a submitted selection.
**Export.** Each link offers:
- **Copy file names (Lightroom)** — the selected file names without extension,
comma-separated. Pasted into Lightroom's or Capture One's filename filter,
`IMG_0412` finds the RAW as well as the JPEG the client saw.
- **CSV** — position in the album, file name and capture time.
- **TXT** — full file names, one per line.
**Notification.** Set `PROOFING_WEBHOOK_URL` and every first submit POSTs JSON
to it. The body carries `content` (Discord), `text` (Slack, Mattermost),
`title` and `message` (Gotify) and a structured `proofing` object (n8n, Home
Assistant, your own script), so most receivers work without an adapter. With
`SITE_URL` set, the message links straight to the selection in the admin panel.
A failing webhook never fails the client's submit.
```env
PROOFING_WEBHOOK_URL=https://discord.com/api/webhooks/…
```
**Security notes.**
- The token in the link is the only credential: 192 random bits, never shown
in the admin panel's own URLs. Treat the link like a password; delete it in
the admin panel to revoke it at once.
- Links are stored in `content/proofing.json` (mode `0600`), next to
`analytics.json`, and are not part of the YAML backups.
- A site-wide password still applies: on a locked site, the client needs it
too.
- Proofing pages are `noindex`, left out of the sitemap, and send no
`Referer`.
- A subpage slugged `proof` would be hidden behind this route.
## Image Protection & Watermark
```yaml
@@ -744,6 +812,79 @@ The admin panel's **Settings → About** section edits the same file — portrai
name, location, gear list and bio — so `about.md` does not have to be written by
hand.
## Content Pages
Pages beyond the gallery — pricing and packages, workshops, press, a booking
FAQ — are content pages. Each one is a file, `content/pages/<slug>.md`, served
at `/<slug>`.
```markdown
---
title: 'Pricing'
description: 'Packages and rates for portrait and wedding sessions'
draft: true
---
## Portrait sessions
One hour on location, 25 edited photos.
![0a1b2c3d-…:wide](A session in the Highlands)
```
The frontmatter keys are `title`, `description` (used for search engines and
link previews), `password` and `draft`. The body uses the journal's block
syntax ([Journal & Photo Essays](journal.md)): headings, text and photo blocks
(`:wide` or `:fullbleed`). A page has no date, no reading time and no index,
and it has no map block.
### In the menu
A page appears in the header menu through a reference among the subpages in
`gallery.yaml`, at the position it should take:
```yaml
subpages:
- name: Landscapes
albums:
- 'album-uuid-1'
- page: pricing
```
A page without a reference is still reachable at `/<slug>`, but it is not in
the menu, which suits a page that is only shared by link. The menu stays flat:
a page cannot sit inside a subpage's sections. A reference to a page whose file
does not exist is skipped, and the config doctor reports it.
### Slugs
A page's slug must not be taken by a subpage, a standalone album, a journal
entry or one of the built-in routes (`about`, `map`, `journal`, `impressum`,
`privacy`, `install`, `admin`, `gate`, `api`, `proof`, `contact`). The admin
panel refuses to save a page on such a slug, and the doctor reports a collision
in a hand-edited file. Renaming a page's slug breaks old links: there are no
automatic redirects.
### Draft and password
A draft is visible only to a signed-in admin, and it is left out of the menu and
the sitemap. A `password` locks the page like a journal entry, with its own
cookie (`lb_auth_page_<slug>`). The site password, when one is set, applies
before either. Password-protected pages are not listed in the sitemap.
### In the admin panel
Admin → Pages lists pages next to the subpages, with a document icon and a
PAGE tag, and a separate "Not in menu" group for pages without a reference.
**+ New page** creates one (it starts blank and as a draft). Select a page to
edit its title, URL, draft flag, password and description, and to switch
"Show in menu"; dragging a page into or out of "Not in menu" does the same.
The menu change is saved with **Save Changes**, like any other change to the
structure. **Edit content →** opens the journal's block editor with a live
preview. Every save keeps a backup in `content/pages/.backups/`, and deleting a
page that is in the menu also removes its `gallery.yaml` reference; both files
are backed up first.
## Journal & Photo Essays
Long-form storytelling with fullbleed photos, side-by-side pairs, quotes and