--- quick_id: 260929-lh3 type: quick wave: 1 autonomous: true --- # Quick 260929-lh3: Favoriten — eigene Symbol-Adresse wirkt nicht ## User reports (29.09.2026, alpha 8c644de) 1. Favorite with URL https://docuvita.ctl.local/server/services/web/ shows a black circle with a white "V" instead of the page's favicon (visible in the browser tab). 2. Setting an explicit icon URL ("Symbol-Adresse", field `iconUrl`) to https://nextcloud.com/c/uploads/2025/10/Nextcloud_01-standard-logo.png on a favorite does not change the shown icon. ## Measured facts (orchestrator, from inside the alpha api container) - docuvita.ctl.local resolves (172.16.0.46). Server-side GET of the page returns **400** but the HTML contains ``. Server-side GET of that icon returns **404 text/html** (also with a Chrome User-Agent). Root /favicon.ico → 404. → docuvita refuses the files to the server; the browser can load them (user sees the icon in the tab). - Discovery (`apps/api/src/favorites/icon-discovery.service.ts` `discoverFavoriteIconUrl`) ignores non-2xx HTML (`fetchHtml` returns null) → falls back to `{origin}/favicon.ico`; proxy fails → browser direct `{origin}/favicon.ico` shows the "V" (a real icon served to browsers at the root). - Explicit `iconUrl` that the server cannot fetch → API answers 422 `iconUrlUnreachable` (quick 260923-lrr), so the user cannot set the docuvita icon URL at all. ## Task 1: Explicit icon URL change must show immediately (bug 2) - files: apps/api/src/favorites/*, apps/web/src/components/dashboard/widgets/favorites-widget.tsx, apps/web/src/lib/favorites-api.ts (+ tests) - action: Reproduce locally (admin/admin123, favorites widget): set/change `iconUrl` to a reachable PNG (e.g. the Nextcloud URL, and a second different one). Find why the tile keeps the old image — likely the icon proxy URL (`/favorites/:id/icon?...`) does not change when `iconUrl` changes (browser/HTTP cache, Cache-Control on the proxy response, `iconVersion` only bumped on upload, or the server returns a cached/discovered icon instead of the explicit one). Fix at the root: the explicit `iconUrl` wins over discovery, and any change of `iconUrl` changes the image URL (e.g. cache-buster from `iconVersion` bumped on every iconUrl change, or a hash of iconUrl). Add regression tests (API: PATCH iconUrl bumps version / proxy serves new bytes; web: tile src changes when iconUrl changes). - verify: api + web tests for favorites green. - done: commit `fix(favorites): geaenderte Symbol-Adresse wird sofort angezeigt`. ## Task 2: Accept icon URLs the server cannot fetch; browser loads them directly (bug 1) - action: - API: an explicit `iconUrl` that is a valid http/https URL is stored even if the server cannot fetch it (no more 422 for "unreachable"; keep validation of scheme/length and keep rejecting non-image responses only when the server DID get a response with a non-image content type — decide and document). Keep SSRF guard for server-side fetches unchanged. - Web tile: chain for an explicit `iconUrl`: proxy image → on error the browser loads `iconUrl` directly (`` with referrerPolicy="no-referrer", only http/https) → letter fallback. Existing chain for discovered icons unchanged. - Discovery improvement (small, safe): if the page answers non-2xx but returns HTML with a ``, still use that icon URL (so docuvita-like servers yield `/webclient/.../favicon.ico`, which the browser can then load directly). - Remove/adjust the now-unused `iconUrlUnreachable` error text (de/en) if no longer reachable. - verify: api + web favorites tests green; type-check/lint green; biome web ≤ 55, api ≤ 82. - done: commit `fix(favorites): Symbol-Adresse auch speichern, wenn nur der Browser sie laden kann`. ## Task 3: CHANGELOG + rebuild - CHANGELOG `## Unveröffentlicht` → `### Behoben`: two plain-German bullets (Sie-Form) for both fixes. - `docker compose up -d --build web api`. - Commit `docs(changelog): Favoriten-Symbole`. ## Constraints - Commit locally only, NEVER git push. Commits end with `Co-Authored-By: Claude Opus 5.5 (1M context) `. - Commit with explicit paths only. - Browser check is done by the orchestrator.