feat: Ein Markenlogo je Vertrag

Am Logo findet man den Vertrag schneller als am Namen: Netflix erkennt man am
roten N, nicht am Wort – und erst recht nicht an einem Zahnrad aus der
Symbolauswahl. Die bleibt als Rueckfall fuer alles, wozu es kein Logo gibt.

Hochgeladen wird, was das Telefon hergibt; gespeichert ein PNG von hoechstens
128 Pixeln Kantenlaenge. Aus drei Megabyte werden ein paar Kilobyte, und das
Seitenverhaeltnis bleibt – ein verzerrtes Logo sieht falscher aus als ein
kleines.

Immer PNG, egal was hereinkam. Das erspart der Anzeige jede
Fallunterscheidung und schliesst SVG aus: Eine SVG-Datei kann Skript
enthalten, und ein Bild, das der Nutzer hochlaedt und der Browser ausfuehrt,
ist genau die Art Falle, die man sich nicht in ein Finanzprogramm legt.

Eigene Tabelle, nicht eine Spalte an den Vertraegen: Sonst zoege jede
Listenabfrage saemtliche Bilder mit heraus, obwohl die Liste sie einzeln
nachlaedt.

ZWEI FEHLER IM SICHERN, die dabei aufgefallen sind:

Binaeres liess sich gar nicht sichern – die Ausgabe scheiterte an den Bytes,
und zwar erst beim Schreiben, also nachdem der Nutzer glaubt, ein Backup zu
haben. Jetzt Base64.

Und "Ergaenzen" legte Zeilen von Tabellen mit natuerlichem Schluessel ein
zweites Mal an, statt sie wiederzuerkennen. Beim Logo kippte damit die ganze
Wiederherstellung. Betraf bisher keine Tabelle, weil noch keine solche einen
Vergleichsschluessel hatte.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-07 19:35:37 +02:00
co-authored by Claude Opus 5
parent 0a790fd45a
commit dd2c33725f
12 changed files with 484 additions and 12 deletions
+25
View File
@@ -990,6 +990,31 @@ class Subscription(Base):
return round(self.amount_cents / n)
class SubscriptionLogo(Base):
"""Das Markenlogo eines Vertrags.
Eigene Tabelle und nicht eine Spalte an ``subscriptions``: Sonst zoege
jede Listenabfrage saemtliche Bilder mit aus der Datenbank, obwohl die
Liste sie einzeln nachlaedt. Ein Blob in einer Zeile, die man oft und
vollstaendig liest, ist teuer.
Gespeichert wird immer PNG, klein gerechnet – hochgeladen wird, was das
Telefon hergibt, und das sind schnell drei Megabyte fuer ein Bildchen von
dreissig Pixeln Kantenlaenge.
"""
__tablename__ = "subscription_logos"
subscription_id: Mapped[int] = mapped_column(
ForeignKey("subscriptions.id", ondelete="CASCADE"), primary_key=True
)
content: Mapped[bytes] = mapped_column(LargeBinary, nullable=False)
size_bytes: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=_now, onupdate=_now
)
class BankApp(Base):
"""Die bei Enable Banking registrierte Anwendung dieser Installation.