6.0 KiB
6.0 KiB
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/metadatawithalbumIds, because v3 removedAlbumResponseDto.assets. Kept a v2 fallback (al.assetsif present) so the same code works against both server versions during transition. - Added a
searchMetadata()pagination helper (pages viaassets.nextPage, integerpage/size) rather than one-shot single-page fetches — album/person sets can exceed one page of 250. - Person assets moved to
POST /search/metadatawithpersonIds, keeping the legacyGET /people/:id/assetsas 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. visibilityhandling is still open — see Open Questions. Initially forcedvisibility:'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.jsis ~27KB, over the Giteaupdate_file~16KB silent-truncation limit. Uploader BAT verifies md5 +node --checkon the server before it will commit/push.
Technical Details
- Immich creds are set inline in
docker-compose.ymlunderenvironment:(IMMICH_URL,IMMICH_API_KEY), NOT in.env. Compose ALSO references anenv_file:(.env) that is gitignored and currently MISSING from~/frambeon hal-blackpearl — this makes everydocker composecommand 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) andprocess.env.IMMICH_URL(line 14). These match the composeenvironment:block. A debug test that printed$API_KEYshowed 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/metadatabody{"albumIds":["<uuid>"],"size":5}returnsassets.total:5— so the v3albumIdssearch 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, notcurl. Usewget -qO-for in-container HTTP tests. - Frambe app endpoint:
http://localhost:3030/api/albums/:id(host port 3030). - v3 endpoint/shape changes applied:
GET /albumslist:?shared=true→?isShared=true; added?isOwned=true.GET /albums/:id:AlbumResponseDto.assetsremoved → search/metadata.GET /assets/randomREMOVED →POST /search/random(body{size, visibility}, returns a bare array; code tolerates array or{assets:{items}}).POST /search/metadata:page/sizenow integers; omittedvisibilitynow 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-IDheader — NOT handled because Frambe only reads HTTP status codes, not error bodies. No change needed.
Code Changes
server.js— VERSION → 1.6.0; addednormDuration(),searchMetadata();mapAssetusesnormDuration;/api/albumsuses isShared/isOwned;/api/albums/:iduses search/metadata (v2 fallback);/api/people/:iduses personIds search (legacy fallback);/api/assets/randomuses search/random;/api/assets/favoritesrouted throughsearchMetadata. AddedlogErrto 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:
.envmissing in~/frambeon hal-blackpearl, so Compose aborts. Fix by either (a)cp .env.example .envand fill real values (TZ=Australia/Melbourne, admin password, Frambe API token), or (b) drop theenv_file:line fromdocker-compose.ymlsince the Immich creds already live in theenvironment: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-testcurl 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, dropvisibility:'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.ymlstill has the obsolete top-levelversion:attribute → harmless warning; remove when convenient.- Not yet done: CHANGELOG entry / README version bump — deferred to the
mainmerge. - Merge
dev→mainvia Gitea UI once frames are confirmed working.
Artifacts
devHEAD: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)