feat(logos): Provider-Kette, lokaler Cache und Markenfarben

- simple-icons als kompakter Index im Repository (3.459 Marken, 2 MB gzip),
  erzeugt von scripts/vendor_simple_icons.py bzw. `make vendor-icons`
- logo.dev und Brandfetch als optionale Adapter, ohne Schlüssel übersprungen
- Favicon-Fallback und generierter Buchstaben-Avatar als Garantie
- Bei eindeutigem Offline-Treffer unterbleiben Anfragen nach außen komplett
- Cache im Dateisystem nach SHA-256, Auslieferung nur über /api/logos/{id}
  mit immutable-Header und ETag
- Markenfarbe aus SVG-Fills bzw. per k-Means (k=4) über 64x64 Pixel, dazu eine
  aufgehellte Variante mit mindestens 4,5:1 Kontrast auf dunklem Grund
- Kandidatensuche mit Vorauswahl, Auswahl, Upload und Zurücksetzen
- Bildtyp wird nur noch am Inhalt bestimmt, nicht an der gemeldeten Kopfzeile
- 60 neue Tests, insgesamt 208 grün

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
This commit is contained in:
moneyfy
2026-09-09 13:54:21 +02:00
co-authored by Claude Opus 5
parent b586d27b77
commit 8edb69fe6a
16 changed files with 2039 additions and 7 deletions
+27 -2
View File
@@ -22,7 +22,7 @@ _Platzhalter werden ergänzt, sobald die Oberfläche steht._
- Verträge mit Mindestlaufzeit, Kündigungsfrist und automatischer Verlängerung.
- Ratenzahlungen mit Restschuld- und Restratenberechnung.
- Rücklagenbildung für nicht-monatliche Posten.
- Firmenlogos und Markenfarben, lokal zwischengespeichert.
- Firmenlogos und Markenfarben, lokal zwischengespeichert (siehe unten).
- Auswertungen: Monatsübersicht, Cashflow-Kalender, 12-Monats-Forecast,
Kategorien, Abo-Übersicht, Jahresvergleich, Budgets, Sparziele.
- Benachrichtigungen per SMTP und Apprise.
@@ -69,12 +69,37 @@ vollständige, kommentierte Liste. Die wichtigsten:
| `TIMEZONE` | `Europe/Berlin` | Zeitzone der gesamten Anwendung |
| `HOLIDAY_REGION` | `DE-NW` | Feiertagsregion für Werktagsverschiebungen |
| `LOGO_STORAGE_DIR` | `/data/logos` | Verzeichnis des Logo-Caches |
| `LOGODEV_API_KEY` / `BRANDFETCH_API_KEY` | leer | Optionale Logo-Provider |
| `LOGODEV_API_KEY` / `BRANDFETCH_API_KEY` | leer | Optionale Logo-Provider, ohne Schlüssel übersprungen |
| `SMTP_*` | leer | Mailversand für Benachrichtigungen |
| `APPRISE_URLS` | leer | Komma-separierte Apprise-Ziele |
| `COOKIE_SECURE` | `true` | Hinter HTTPS `true`, für lokales HTTP `false` |
| `SCHEDULER_ENABLED` | `true` | Täglicher Benachrichtigungslauf um 07:00 |
## Logos und Markenfarben
Jede Firma bekommt ein Bild. Die Provider-Kette arbeitet der Reihe nach:
1. **simple-icons** rund 3.500 Marken, im Repository unter
`backend/app/assets/simple_icons.json.gz` mitgeliefert. Kein Schlüssel, kein
Netzzugriff. Neu erzeugen mit `make vendor-icons`.
2. **logo.dev** nur mit `LOGODEV_API_KEY`.
3. **Brandfetch** nur mit `BRANDFETCH_API_KEY` und hinterlegter Domain.
4. **Favicon** über den Google-Dienst, wenn eine Domain bekannt oder aus dem
Namen ableitbar ist.
5. **Generierter Avatar** Buchstaben-Monogramm mit fester Farbe aus dem
Namens-Hash. Schlägt nie fehl.
Findet die Kette einen eindeutigen Treffer im lokalen Katalog, unterbleiben die
Anfragen nach außen vollständig die Firmenliste verlässt den Server nicht. Im
Auswahldialog (`POST /api/merchants/{id}/logo/search`) läuft dagegen bewusst die
ganze Kette, damit Alternativen zur Wahl stehen.
Ausgewählte Logos landen unter `LOGO_STORAGE_DIR`, benannt nach ihrem
SHA-256-Hash, und werden nur über `GET /api/logos/{id}` ausgeliefert. Beim
Seitenaufruf entsteht dadurch kein Zugriff auf fremde Dienste. Ein Upload oder
eine bewusste Auswahl setzt den Status auf `manual` und wird von der Automatik
nie überschrieben.
## Projektstruktur
```