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>
This commit is contained in:
@@ -1,13 +1,16 @@
|
||||
# Admin Panel
|
||||
|
||||
Immich Folio includes a built-in visual admin panel at `/admin` that lets you manage your gallery structure and site settings without editing YAML files by hand.
|
||||
Immich Folio includes a built-in visual admin panel at `/admin` that lets you manage your gallery structure, journal entries and site settings without editing YAML files by hand.
|
||||
|
||||
**Contents:**
|
||||
|
||||
- [Enabling the Admin Panel](#enabling-the-admin-panel)
|
||||
- [Page Builder](#page-builder)
|
||||
- [Settings Editor](#settings-editor)
|
||||
- [Album Picker](#album-picker)
|
||||
- [Layout](#layout)
|
||||
- [Pages](#pages)
|
||||
- [Journal](#journal)
|
||||
- [Settings](#settings)
|
||||
- [Analytics](#analytics)
|
||||
- [Album & Asset Pickers](#album--asset-pickers)
|
||||
- [Security](#security)
|
||||
- [Backups](#backups)
|
||||
- [Docker Usage](#docker-usage)
|
||||
@@ -22,97 +25,147 @@ ADMIN_PASSWORD=your-secure-admin-password
|
||||
|
||||
Then navigate to `http://your-site/admin`. The panel is completely disabled if no password is set — it won't even render the login page.
|
||||
|
||||
The [setup wizard](../README.md#first-run-setup) can set the password instead, storing it as an scrypt hash in `content/install.json`. An `ADMIN_PASSWORD` environment variable takes precedence if both are present.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> The admin password is separate from any album or subpage passwords. It controls access to the entire gallery configuration.
|
||||
|
||||
## Page Builder
|
||||
## Layout
|
||||
|
||||
The **Pages** tab provides a visual tree view of your gallery structure:
|
||||
Each area has its own URL, so a section can be bookmarked and the browser's back button works as expected:
|
||||
|
||||
| Tab | URL | Writes to |
|
||||
| ------------- | ------------------ | ------------------------------------------- |
|
||||
| **Pages** | `/admin/pages` | `content/gallery.yaml` |
|
||||
| **Journal** | `/admin/journal` | `content/journal/<slug>.md` |
|
||||
| **Settings** | `/admin/settings` | `content/settings.yaml`, `content/about.md` |
|
||||
| **Analytics** | `/admin/analytics` | nothing — read-only |
|
||||
|
||||
Unsaved changes raise a save bar pinned to the bottom of the viewport, so the Save button is reachable without scrolling back up. Saving applies immediately — no server restart.
|
||||
|
||||
## Pages
|
||||
|
||||
A visual tree of your gallery structure. Each item opens a slide-over drawer with its settings.
|
||||
|
||||
### Hero Images
|
||||
|
||||
Add Immich asset UUIDs for the homepage hero carousel. Supports single or multiple images that crossfade automatically.
|
||||
Immich asset UUIDs for the homepage hero. One image, or several that crossfade as a carousel. Pick them from your library with the [asset picker](#album--asset-pickers) instead of typing UUIDs.
|
||||
|
||||
### Standalone Albums
|
||||
|
||||
Albums displayed directly on the homepage. Click **+ Add Album** to open the Album Picker and select from your Immich library.
|
||||
Albums displayed directly on the homepage. **+ Add Album** opens the [Album Picker](#album--asset-pickers).
|
||||
|
||||
Each album card supports:
|
||||
- **Title override** — display a custom name instead of the Immich album name
|
||||
- **Description** — shown below the album title on the subpage
|
||||
- **Password** — protect individual albums with a password
|
||||
Each album supports:
|
||||
|
||||
- **Title override** — a display name instead of the Immich album name
|
||||
- **Description** — shown below the album title
|
||||
- **Password** — protect a single album (written as an scrypt hash)
|
||||
- **Cover image** — override the album cover with any asset
|
||||
- **Photo order** — Immich order, newest, oldest, filename, or **Manual** with a drag & drop editor
|
||||
- **Grid override** _(experimental)_ — a different grid layout for this album alone (the panel exposes the layout; columns, gap and aspect ratio can be set in `gallery.yaml`)
|
||||
- **Cover focal point** _(experimental)_ — which part of the cover survives the crop
|
||||
|
||||
### Subpages
|
||||
|
||||
Group albums under custom URL paths (e.g., `/japan`, `/wedding-smith`).
|
||||
Group albums under custom URL paths (e.g. `/japan`, `/wedding-smith`).
|
||||
|
||||
Each subpage has:
|
||||
- **Name** — used for navigation and URL generation (auto-slugified)
|
||||
- **Title** — optional display title (defaults to name)
|
||||
- **Subtitle** — optional text shown below the page heading
|
||||
- **Password** — optional protection for the entire subpage
|
||||
- **Name** — navigation label; the URL slug is derived from it
|
||||
- **Title / Subtitle** — page heading and subline
|
||||
- **Password** — protect the whole subpage
|
||||
- **Grid override** — layout settings for this page
|
||||
- **Enabled** — take the page offline without deleting it
|
||||
- **Hidden** _(experimental)_ — unlisted: reachable by direct link, absent from the navigation
|
||||
- **Essay** — turn the page into a photo essay, with a block editor for the text
|
||||
|
||||
A live preview shows the page as visitors will see it.
|
||||
|
||||
### Sections
|
||||
|
||||
Within a subpage, you can organize albums into named sections. This generates a table of contents with anchor links. Add sections with **+ Add Section** and fill each with albums from the picker.
|
||||
Within a subpage, albums can be grouped into named sections, which renders a table of contents with anchor links.
|
||||
|
||||
### Reordering
|
||||
|
||||
Use the **↑/↓** buttons on subpages to change their display order. The order in the admin panel matches the order on your site.
|
||||
Subpages, albums and sections are reordered in place; the order in the panel is the order on the site.
|
||||
|
||||
## Settings Editor
|
||||
## Journal
|
||||
|
||||
The **Settings** tab lets you configure global site behavior:
|
||||
The **Journal Studio** — a split-screen editor with the blocks on the left and a live preview of the real page on the right, separated by a divider you can drag with mouse, keyboard or touch.
|
||||
|
||||
| Section | Controls |
|
||||
| --------- | ---------------------------------------------------------------- |
|
||||
| General | Site title, subtitle, language, EXIF on hover, map, transitions, scroll-to-top |
|
||||
| Theme | Preset, accent color, photo frame, hero style, grain, header dot |
|
||||
| Grid | Layout mode, columns, gap, aspect ratio |
|
||||
| Footer | Name, Instagram, email, website |
|
||||
| Legal | Impressum toggle and all legal fields |
|
||||
| SEO | Meta title, description, noindex, nofollow |
|
||||
Blocks are added from a menu (heading, text, quote, photo, photo pair), reordered, and deleted; each carries a type chip so long entries stay scannable. Photos are inserted through the asset picker, which fills in the Immich asset UUID for you. The frontmatter — cover image, title, subtitle, author, date, draft flag, password — is edited as form fields.
|
||||
|
||||
Changes are applied immediately after saving — no server restart required.
|
||||
Saving writes plain Markdown to `content/journal/<slug>.md`, which can equally be edited by hand.
|
||||
|
||||
## Album Picker
|
||||
→ **[Journal & Photo Essays](journal.md)** for the file format.
|
||||
|
||||
When adding albums, a modal overlay shows **all shared albums** from your Immich instance (not just the ones already configured). Features:
|
||||
## Settings
|
||||
|
||||
Eight sections, each at its own URL (`/admin/settings/theme`, …):
|
||||
|
||||
| Section | Controls |
|
||||
| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **General** | Site title, subtitle, language, favicon upload, and the feature toggles (EXIF on hover, map, transitions, scroll-to-top, analytics) |
|
||||
| **Theme** | Preset, colour mode, accent, photo frame, hero style, grain, header dot |
|
||||
| **Grid** | Layout algorithm, columns, gap, aspect ratio |
|
||||
| **Footer** | Name, Instagram, email, website, and the header navigation links |
|
||||
| **Legal** | Impressum toggle and all legal fields |
|
||||
| **SEO** | Meta title, subpage title template, description, noindex, nofollow |
|
||||
| **Security & Protection** | Right-click and image-drag deterrents, lightbox watermark (text, position, opacity) |
|
||||
| **About** | Portrait asset, name, location, gear list, and the biography — written to `content/about.md` |
|
||||
|
||||
Theme presets, grid layouts, photo frames, hero styles and the Google search snippet are picked from **preview cards** rather than text fields, so the effect of a choice is visible before saving. A light/dark toggle previews both variants of a preset.
|
||||
|
||||
### Favicon
|
||||
|
||||
**General → Favicon** accepts an SVG, PNG, ICO or JPEG. It is stored in the writable `content/` volume and served through `/api/favicon` under a policy that stops an uploaded SVG from executing script. **Reset** restores the bundled default.
|
||||
|
||||
## Analytics
|
||||
|
||||
View counts per page and album, read from `content/analytics.json`. No cookies, no third party, nothing leaving your server. Switch the collection off entirely in **Settings → General**; the tracking endpoint then refuses to record.
|
||||
|
||||
## Album & Asset Pickers
|
||||
|
||||
**Album Picker** — a modal listing **all shared albums** from your Immich instance, not just the configured ones:
|
||||
|
||||
- **Search** by album name or UUID
|
||||
- **Photo count** shown for each album
|
||||
- **Photo count** per album
|
||||
- **"In use" badge** for albums already assigned somewhere
|
||||
- Albums are sorted: configured first, then alphabetically
|
||||
- Sorted configured-first, then alphabetically
|
||||
|
||||
**Asset Picker** — browses your full Immich library for hero images, album covers, journal photos and the about-page portrait. It uses `POST /search/metadata`, which is covered by the `asset.read` permission your API key already needs.
|
||||
|
||||
## Security
|
||||
|
||||
| Aspect | Implementation |
|
||||
| -------------- | ----------------------------------------------------------- |
|
||||
| Authentication | HMAC-signed session token in an `HttpOnly` cookie |
|
||||
| Session expiry | 24 hours (automatic logout) |
|
||||
| Password check | Constant-time comparison to prevent timing attacks |
|
||||
| Cookie flags | `HttpOnly`, `Secure` (in production), `SameSite=Strict` |
|
||||
| Rate limiting | Admin endpoints share the global rate limiter |
|
||||
| Robots | `/admin` is marked `noindex, nofollow` in metadata |
|
||||
| Aspect | Implementation |
|
||||
| -------------- | --------------------------------------------------------------------- |
|
||||
| Authentication | HMAC-signed session token in an `HttpOnly` cookie |
|
||||
| Signing key | Derived from `AUTH_SECRET` with scrypt |
|
||||
| Session expiry | 24 hours (automatic logout) |
|
||||
| Password check | Constant-time comparison to prevent timing attacks |
|
||||
| Cookie flags | `HttpOnly`, `Secure` (in production), `SameSite=Strict` |
|
||||
| Rate limiting | `POST /api/admin/auth` is capped at 5 attempts per minute per IP |
|
||||
| Authorization | Every `/api/admin/*` route re-checks the session, including analytics |
|
||||
| Robots | `/admin` is marked `noindex, nofollow` in metadata |
|
||||
|
||||
> [!NOTE]
|
||||
> The admin panel uses its own session management, completely separate from album password protection. Admin sessions use `AUTH_SECRET` for token signing.
|
||||
> The admin panel uses its own session management, completely separate from album password protection. Rotating `AUTH_SECRET` invalidates every admin session along with every unlocked gallery.
|
||||
|
||||
## Backups
|
||||
|
||||
Every time you save changes, the previous YAML file is automatically backed up to `content/.backups/`:
|
||||
Every time you save, the previous file is backed up automatically:
|
||||
|
||||
```
|
||||
content/.backups/
|
||||
├── gallery.yaml.2026-05-29T14-30-00-000Z.bak
|
||||
├── gallery.yaml.2026-05-29T15-45-00-000Z.bak
|
||||
├── settings.yaml.2026-05-29T14-30-00-000Z.bak
|
||||
└── ...
|
||||
content/journal/.backups/
|
||||
└── my-story.md.2026-05-29T14-30-00-000Z.bak
|
||||
```
|
||||
|
||||
- Up to **10 backups** per file are retained (oldest are pruned)
|
||||
- The **Backup Manager** lists them and restores any one with a single click
|
||||
- Before a restore, a `*.pre-restore.bak` snapshot is created
|
||||
- All writes are **atomic** (write to temp file, then rename) — no risk of corrupted YAML
|
||||
- All writes are **atomic** (write to temp file, then rename) — no risk of a half-written YAML
|
||||
|
||||
## Docker Usage
|
||||
|
||||
@@ -125,17 +178,24 @@ services:
|
||||
environment:
|
||||
- ADMIN_PASSWORD=your-secure-password
|
||||
volumes:
|
||||
- ./content:/app/content # Must be writable for admin panel
|
||||
- ./content:/app/content # Must be writable for admin panel
|
||||
```
|
||||
|
||||
> [!IMPORTANT]
|
||||
> When using the admin panel, the content volume must be **read-write** (not `:ro`). Remove the `:ro` flag if you had it set previously.
|
||||
> The content volume must be **read-write** (not `:ro`). The admin panel, the journal, the favicon upload, the setup wizard and the backup rotation all write into it.
|
||||
|
||||
### Reload Button
|
||||
|
||||
The admin header includes a **↻ Reload** button that:
|
||||
|
||||
1. Invalidates the in-memory config cache
|
||||
2. Clears the Immich album/asset cache
|
||||
3. Forces the next request to re-read all YAML files
|
||||
|
||||
This is useful after making external changes to config files or when Immich data has changed.
|
||||
|
||||
### Diagnostics
|
||||
|
||||
The panel reports the health of the installation: whether Immich answers, whether `gallery.yaml` and `settings.yaml` parse, the number of cached entries, and the backup count with the timestamp of the most recent one. `settings.yaml` is optional, so its absence is not a fault — only a file that exists and cannot be parsed counts as invalid. A check that could not run (an expired session, for example) is reported as such rather than as an outage.
|
||||
|
||||
Separately, while you are logged in, the public site shows a diagnostic banner on any album Immich returns empty — the case that otherwise looks like a broken page to you and to nobody else.
|
||||
|
||||
Reference in New Issue
Block a user