2.4 KiB
2.4 KiB
status
| status |
|---|
| complete |
Quick Task 260707-lgh: Favoriten-Widget Icon-Proxy fuer Cross-Origin-Resource-Policy-Seiten
What shipped
icon-discovery.service.ts— extracted the shared SSRF-guarded redirect loop intofetchWithRedirectGuard(exportedisPublicHttpUrl), addedfetchIconBytes(): image-content-type gate, 1MB cap, dedicated timeout.discoverFavoriteIconUrlunchanged. 10 new vitest specs, all passing.favorites.service.ts/favorites.controller.ts—getIconBytes(id, userId)(same userId-only ownership check as update/remove; 404 on mismatch/no-icon), newGET /favorites/:id/iconstreaming endpoint (Cache-Control 86400s, 502 on upstream failure).favorites-widget.tsx— icon<img src>now points at/api-proxy/favorites/:id/icon(same pattern as the existing avatar image proxy) instead of hotlinking the external URL directly.
Bugs caught and fixed during manual testing (not in original plan)
- Cloudflare WAF blocking the byte-fetch itself. After wiring the proxy, chatgpt.com's icon returned 502. Root cause isolated via direct
fetch()comparison inside the API container: thetessera/1.0User-Agent was blocked (403) reproducibly (3/3), while a realistic Chrome UA succeeded (3/3) — a bareimage/*Accept header alone was a red herring from an earlier (cache-hit-masked) test. Fixed by parameterizingfetchWithRedirectGuard's User-Agent and overriding it only forfetchIconBytes(browser-realistic UA + full image Accept list); the HTML-discovery path keeps its originaltessera/1.0UA unchanged, per the plan's no-regression constraint.
Verification performed
vitest run icon-discovery.service.spec.ts— 10/10 passing.- API + web
type-check, APIbuild— all green. - Live end-to-end via Playwright against the running dev stack: both
ChatGPTandClaudefavorites now render their real logos (screenshot-confirmed) instead of the letter fallback; network tab shows both/api-proxy/favorites/:id/iconrequests returning 200 from Tessera's own origin.
Known limitations
- The realistic-browser-UA workaround is a pragmatic fix for one observed WAF behavior (Cloudflare on chatgpt.com); other sites' bot-mitigation could still reject the request for other reasons (rate limits, TLS fingerprinting, etc.) — those will still degrade gracefully to the letter fallback (502 ->
onErrorhides the<img>), just without a logo.