From f64c17823307bbb889f43700925212e5955168b4 Mon Sep 17 00:00:00 2001 From: jessikitty Date: Wed, 23 Sep 2026 20:13:39 +1000 Subject: [PATCH] docs: add 2026-09-23 Immich v3 migration session log --- .../2026-09-23-immich-v3-migration.md | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 .claude-context/sessions/2026-09-23-immich-v3-migration.md diff --git a/.claude-context/sessions/2026-09-23-immich-v3-migration.md b/.claude-context/sessions/2026-09-23-immich-v3-migration.md new file mode 100644 index 0000000..6fdef07 --- /dev/null +++ b/.claude-context/sessions/2026-09-23-immich-v3-migration.md @@ -0,0 +1,98 @@ +# Immich v3 API Migration & Deploy Debugging + +**Date:** 2026-09-23 +**Project:** frambe +**Status:** ongoing +**Tags:** immich-v3, api-migration, docker-compose, env-vars, deployment, search-metadata + +## Context +Immich released v3 (1 July 2026) with a large set of breaking API changes. Frambe +called several endpoints that were removed, renamed, or reshaped, so albums/people/ +random/favorites all needed updating. After shipping the code fixes (v1.6.0 on `dev`), +the "Start slideshow" button flashed "no photos found" despite albums showing a +nonzero count — which turned out to be a deploy/env problem, not a code problem. + +## Key Decisions +- Bumped **1.5.2 → 1.6.0** (minor). Rationale: substantial multi-endpoint + compatibility fix pack for a breaking upstream change, but no new user-facing + features. Not a major (no feature pack), not a patch (more than a small fix). +- Album assets now fetched via **`POST /search/metadata` with `albumIds`**, because + v3 removed `AlbumResponseDto.assets`. Kept a v2 fallback (`al.assets` if present) + so the same code works against both server versions during transition. +- Added a **`searchMetadata()` pagination helper** (pages via `assets.nextPage`, + integer `page`/`size`) rather than one-shot single-page fetches — album/person + sets can exceed one page of 250. +- Person assets moved to `POST /search/metadata` with `personIds`, keeping the + legacy `GET /people/:id/assets` as a try/catch fallback. Considered leaving the + old endpoint alone (not listed as removed in the migration doc) but chose the + canonical v3 path with fallback for durability. +- **`visibility` handling is still open** — see Open Questions. Initially forced + `visibility:'timeline'` everywhere to keep archived/hidden assets off frames; + this is the leading suspect for empty album results and may need to be dropped + for album/person queries. +- Deploy stays **scp + ssh**, not the Gitea API: `server.js` is ~27KB, over the + Gitea `update_file` ~16KB silent-truncation limit. Uploader BAT verifies md5 + + `node --check` on the server before it will commit/push. + +## Technical Details +- Immich creds are set **inline in `docker-compose.yml`** under `environment:` + (`IMMICH_URL`, `IMMICH_API_KEY`), NOT in `.env`. Compose ALSO references an + `env_file:` (`.env`) that is gitignored and currently MISSING from `~/frambe` + on hal-blackpearl — this makes every `docker compose` command abort before it + does anything (rebuild, exec, up all fail with "env file ... not found"). +- App reads `process.env.IMMICH_API_KEY` (server.js line 15) and + `process.env.IMMICH_URL` (line 14). These match the compose `environment:` block. + A debug test that printed `$API_KEY` showed empty — that was the WRONG var name + in the test shell, a red herring, not an app bug. +- Proven working in-container (with a valid key): `POST $IMMICH_URL/api/search/metadata` + body `{"albumIds":[""],"size":5}` returns `assets.total:5` — so the v3 + `albumIds` search logic is correct. The empty result in the UI is a runtime + deploy/env issue, not the query. +- Server on hal-blackpearl uses Alpine node image: has `wget`, not `curl`. Use + `wget -qO-` for in-container HTTP tests. +- Frambe app endpoint: `http://localhost:3030/api/albums/:id` (host port 3030). +- v3 endpoint/shape changes applied: + - `GET /albums` list: `?shared=true` → `?isShared=true`; added `?isOwned=true`. + - `GET /albums/:id`: `AlbumResponseDto.assets` removed → search/metadata. + - `GET /assets/random` REMOVED → `POST /search/random` (body `{size, visibility}`, + returns a bare array; code tolerates array or `{assets:{items}}`). + - `POST /search/metadata`: `page`/`size` now integers; omitted `visibility` now + defaults to "any" (was "timeline"). + - `AssetResponseDto.duration`: now a number in **milliseconds** (was + "H:MM:SS.mmm" string) → `normDuration()` normalizes both to ms. + - Error bodies moved to Zod shape + `X-Correlation-ID` header — NOT handled + because Frambe only reads HTTP status codes, not error bodies. No change needed. + +## Code Changes +- `server.js` — VERSION → 1.6.0; added `normDuration()`, `searchMetadata()`; + `mapAsset` uses `normDuration`; `/api/albums` uses isShared/isOwned; + `/api/albums/:id` uses search/metadata (v2 fallback); `/api/people/:id` uses + personIds search (legacy fallback); `/api/assets/random` uses search/random; + `/api/assets/favorites` routed through `searchMetadata`. Added `logErr` to more + catch blocks. +- `package.json` — version 1.4.1 → 1.6.0 (was stale). +- `public/js/app.js` — no changes (consumes the server's normalized shape). + +## Open Questions / Next Steps +- **Immediate blocker:** `.env` missing in `~/frambe` on hal-blackpearl, so Compose + aborts. Fix by either (a) `cp .env.example .env` and fill real values + (`TZ=Australia/Melbourne`, admin password, Frambe API token), or (b) drop the + `env_file:` line from `docker-compose.yml` since the Immich creds already live in + the `environment:` block. Recommendation leaning toward (b) — removes dependence + on a gitignored file that keeps disappearing. +- Once Compose runs again: `docker compose up -d --build --force-recreate`, then + re-test `curl http://localhost:3030/api/albums/` — expected to return + photos now that v1.6.0 code + valid key are both live. +- **Decide on `visibility`:** if albums still come back empty after the container is + confirmed current, drop `visibility:'timeline'` from the album/person searches + (keep it only where wanted). Album assets aren't guaranteed timeline-visible, so + forcing timeline can zero out results. Possibly make it a config toggle. +- `docker-compose.yml` still has the obsolete top-level `version:` attribute → + harmless warning; remove when convenient. +- Not yet done: CHANGELOG entry / README version bump — deferred to the `main` merge. +- Merge `dev` → `main` via Gitea UI once frames are confirmed working. + +## Artifacts +- `dev` HEAD: `55538e5` "v1.6 Updates for Immich V3 API Changes" +- Immich v3 migration doc: https://immich.app (Migrating to v3, 1 July 2026) +- Repo: https://gitea.hideawaygaming.com.au/jessikitty/frambe (branch: dev)