Files
tessera-ctl/.planning/todos/completed/2026-09-22-dashboard-image-data-spalte-entfernen.md
T
schalli dd54ec5d42 feat(260924-m4n): Bilderrahmen Stufe 2 - alte Bildspalte data entfernt, storagePath Pflicht
- Migration 20260924120000_dashboard_image_drop_data: Schutzpruefung
  (bricht ab, solange eine Zeile ohne storagePath existiert; row_security
  aus, damit ein Eigentuemer ohne BYPASSRLS nicht still 0 Zeilen sieht),
  dann NOT NULL, DROP COLUMN data, DROP POLICY system_read_policy
- Dienst: Bootstrap-Umzug samt forSystem() und Selbstheilung aus data
  entfernt; Upload vergibt die UUID selbst, Zeile gleich mit Pfad
- FORSYSTEM_ALLOWED_CALL_SITES, Tests, Zugriffsklassifikation (per
  Gate-Schleife gemessen: 61/213/6) nachgezogen
- Betriebshandbuch Kap. 4: Hinweis und Wiederherstellungsweg bei Abbruch
- Todo 2026-09-22 nach completed/

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-24 16:09:36 +02:00

4.8 KiB
Raw Blame History

created, title, area, severity, trigger, relates_to
created title area severity trigger relates_to
2026-09-22 DashboardImage — Spalte "data" entfernen und "storagePath" auf NOT NULL setzen (Stufe 2 der Umstellung aus quick-260922-hk4) apps/api/prisma cleanup erst wenn alpha UND live je einmal mit einer Version >= der Freigabe nach 1.3.0 gelaufen sind — dann hat der Bootstrap-Umzug auf beiden Servern gearbeitet und die Bytes liegen im Dateibereich. quick-260922-hk4 (.planning/quick/260922-hk4-bilderrahmen-bilder-auf-die-festplatte/)

Worum es geht

Die Bilder des Bilderrahmen-Widgets sind mit quick-260922-hk4 aus der Datenbank in den Dateibereich gezogen (user-files/dashboard-images/ <userId>/<id>.<ext>, Pfad in der Spalte storagePath). Die Umstellung ist BEWUSST ZWEISTUFIG:

  • Stufe 1, ausgeliefert (Migration 20260922120000_dashboard_image_to_disk): storagePath dazu (NULLbar), data wird NULLbar — aber NICHT gelöscht. Der Umzug der vorhandenen Zeilen passiert beim ersten Start automatisch (DashboardImagesService.onApplicationBootstrap()).
  • Stufe 2, dieser Zettel: data löschen, storagePath auf NOT NULL.

Warum nicht sofort: prisma migrate deploy läuft VOR dem Anwendungsstart. Ein sofortiges DROP COLUMN "data" hätte die Bytes vernichtet, bevor der Umzug beim Start sie lesen konnte (T-HK4-03).

Was zu tun ist

  1. Vorbedingung prüfen (auf BEIDEN Servern, alpha und live):

    SELECT count(*) FROM "DashboardImage" WHERE "storagePath" IS NULL;
    

    Muss überall 0 sein. Ist sie es nicht, ist der Umzug dort noch nicht gelaufen (Server noch auf einer älteren Version) — dann NICHT ausliefern.

  2. Neue Migration 20260922120100_dashboard_image_drop_data:

    ALTER TABLE "DashboardImage" ALTER COLUMN "storagePath" SET NOT NULL;
    ALTER TABLE "DashboardImage" DROP COLUMN "data";
    
  3. Schema apps/api/prisma/schema.prisma: Feld data Bytes? entfernen, storagePath String? → storagePath String.

  4. Dienst apps/api/src/dashboard/dashboard-images.service.ts: onApplicationBootstrap() samt forSystem()-Aufruf entfernt sich damit — der Umzug hat seine Arbeit getan. Danach:

    • Eintrag apps/api/src/dashboard/dashboard-images.service.ts aus FORSYSTEM_ALLOWED_CALL_SITES in apps/api/src/prisma/rls-access-inventory.spec.ts wieder ENTFERNEN (die Liste ist ein „genau", ein veralteter Eintrag macht die Spec rot).
    • In docs/mandantentrennung-zugriffsklassifikation.md den Stand der Zeile dashboard-images.service.ts/dashboardImage von system-gebunden zurück auf gebunden setzen und die Zahlen der Bereichszeile dashboard sowie die Summe neu messen (Gate-Schleife, nicht abschreiben).
    • Die Tests 18, 21, 22 und 23 der Dienst-Spec (Zeile ohne storagePath, Bootstrap-Umzug) entfallen mit dem Umzug.
    • Die Regel system_read_policy auf "DashboardImage" (angelegt in 20260922120000) kann bleiben oder mit DROP POLICY fallen — bleibt sie, gehört sie in der Klassifikation erwähnt; fällt sie, ist die Aufzählung „fünf/sechs Tabellen" dort nachzuziehen.
  5. Prüfen, dass die Datenbank kleiner wird:

    SELECT pg_size_pretty(pg_total_relation_size('"DashboardImage"'));
    

    (Nach dem DROP zusätzlich VACUUM FULL "DashboardImage";, sonst gibt PostgreSQL den Platz nicht ans Dateisystem zurück.)

Was passiert, wenn es liegen bleibt

Nichts Schlimmes: die Spalte steht leer herum und kostet je neuer Zeile nichts. Der Gewinn der Umstellung (kleiner pg_dump) ist bereits da, weil neue Uploads keine Bytes mehr in die Zeile schreiben. Nur die Bytes der ALTEN Bilder bleiben bis dahin doppelt vorhanden — einmal in der Datei, einmal in der Spalte.

Erledigt in quick-260924-m4n (24.09.2026)

  • Migration heißt 20260924120000_dashboard_image_drop_data (nicht 20260922120100: sie muss hinter allen vorhandenen Migrationen liegen). Sie prüft zuerst, dass keine Zeile ohne storagePath existiert, und bricht sonst mit Meldung ab, bevor sie etwas ändert; row_security ist für die Prüfung aus, damit ein Eigentümer ohne BYPASSRLS nicht still 0 Zeilen sieht (lokal nachgewiesen). Danach storagePath NOT NULL, DROP COLUMN "data", DROP POLICY IF EXISTS system_read_policy ON "DashboardImage".
  • Dienst: Bootstrap-Umzug, forSystem() und die Selbstheilung aus data entfernt; der Upload vergibt die UUID selbst und legt die Zeile gleich mit Pfad an. Erlaubnisliste, Tests (10b, 10c, 18, 21–23 entfallen) und Zugriffsklassifikation nachgezogen (Zahlen mit der Gate-Schleife gemessen).
  • Wiederherstellungsweg nach einem Abbruch (fehlgeschlagene Migration als zurückgenommen vermerken, 1.3.1 laufen lassen, erneut einspielen) in docs/anleitung-betrieb.md Kapitel 4, in einer Wegwerf-Datenbank durchgespielt.