docs: add 2026-09-23 Immich v3 migration session log
This commit is contained in:
1 parent
dcadf34aa3
commit
f64c178233
1 file changed
+98
@@ -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":["<uuid>"],"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/<uuid>` — 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)
|
||||||
Reference in new issue
Block a user