feat(favicon): per-feed icon from the last sender's domain

Resolve each feed's most recent sender domain and serve its favicon at
GET /favicon/:feedId, falling back to the project icon. Icons are fetched
in the background on ingestion (direct /favicon.ico then a DuckDuckGo
fallback), cached base64 in KV keyed by domain with a 1-week TTL so the
fetch only fires when absent. Exposed via RSS <image> / Atom <icon>/<logo>
and rendered in the admin feed list, plus a landing-page feature card.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Julien Herr
2026-05-23 14:05:14 +02:00
parent d299c8891d
commit eb12f21894
19 changed files with 592 additions and 30 deletions
+6 -1
View File
@@ -29,7 +29,12 @@ class MockKV {
return type === "json" ? JSON.parse(value) : value;
}
async put(key: string, value: any) {
async put(
key: string,
value: any,
_options?: { expirationTtl?: number; expiration?: number },
) {
// TTL options are accepted for API parity but not simulated in tests.
this.store.set(
key,
typeof value === "string" ? value : JSON.stringify(value),