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

101 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
created: 2026-09-22
title: DashboardImage — Spalte "data" entfernen und "storagePath" auf NOT NULL setzen (Stufe 2 der Umstellung aus quick-260922-hk4)
area: apps/api/prisma
severity: cleanup
trigger: 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.
relates_to: 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):
```sql
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`:
```sql
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:
```sql
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.