dd54ec5d42
- 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>
101 lines
4.8 KiB
Markdown
101 lines
4.8 KiB
Markdown
---
|
||
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.
|