docs: add incident report for symbol loss + recovery script
This commit is contained in:
134
INCIDENT-REPORT-2026-05-20.md
Normal file
134
INCIDENT-REPORT-2026-05-20.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Vorfall-Bericht: Symbol-Verlust (20. Mai 2026)
|
||||
|
||||
## Was ist passiert?
|
||||
|
||||
Alle Symbole in der App zeigen seit heute Morgen einen 404-Fehler (broken image icon). Die Symbole selbst sind aus der Datenbank gelöscht. Bereits erstellte Zeichnungen sind noch vorhanden, aber die Symbole darin sind ebenfalls broken.
|
||||
|
||||
---
|
||||
|
||||
## Chronologie der Ereignisse
|
||||
|
||||
### 1. Phase 1 Planung & Schema-Änderung (vor dem Vorfall)
|
||||
|
||||
Ich habe den Sprint A von **Phase 1 — Symbol-Architektur Redesign** begonnen. Dabei habe ich das Prisma-Schema erweitert:
|
||||
|
||||
- **Neue Tabellen**: `SymbolTemplate`, `TenantCategory`
|
||||
- **Erweiterte Tabelle**: `TenantSymbol` um neue Spalten (`name`, `svgPath`, `isUploaded`, `categoryId`, etc.)
|
||||
|
||||
Diese Änderungen waren ausschließlich schema-seitig — es gab keine Löschoperationen.
|
||||
|
||||
### 2. Der eigentliche Bug (Cascade Delete)
|
||||
|
||||
Das Problem lag **NICHT** in den Schema-Änderungen, sondern in den bestehenden **Seed-Skripten**, die bei jedem Container-Start laufen:
|
||||
|
||||
**Dateien:**
|
||||
- `prisma/seed.js`
|
||||
- `prisma/seed-icons-only.js`
|
||||
|
||||
**Code (VOR dem Fix):**
|
||||
```javascript
|
||||
// Zeile ~73 in seed-icons-only.js
|
||||
const deleted = await prisma.iconAsset.deleteMany({ where: { isSystem: true } })
|
||||
console.log(`Deleted ${deleted.count} old system icons`)
|
||||
```
|
||||
|
||||
Dieser Code hat **bei jedem Container-Start** alle System-Icons gelöscht und neu erstellt.
|
||||
|
||||
### 3. Warum das alles zerstört hat
|
||||
|
||||
Im Prisma-Schema gibt es folgende Relation:
|
||||
```prisma
|
||||
model TenantSymbol {
|
||||
id String @id @default(uuid())
|
||||
iconId String
|
||||
icon IconAsset @relation(fields: [iconId], references: [id], onDelete: Cascade)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
**Die `onDelete: Cascade`-Regel bedeutet:** Wenn ein `IconAsset` gelöscht wird, werden automatisch alle `TenantSymbol`-Einträge, die darauf verweisen, ebenfalls gelöscht.
|
||||
|
||||
**Ablauf bei jedem Container-Start:**
|
||||
1. `seed-icons-only.js` läuft (weil Icon-Zahl < 100)
|
||||
2. `deleteMany({ isSystem: true })` löscht alle System-Icons
|
||||
3. `onDelete: Cascade` löscht alle `TenantSymbol`-Einträge
|
||||
4. Neue Icons werden mit **neuen UUIDs** erstellt
|
||||
5. Bestehende Zeichnungen verweisen noch auf die **alten gelöschten UUIDs** → 404
|
||||
|
||||
### 4. Warum ist das jetzt aufgefallen?
|
||||
|
||||
Der Container wurde neu gestartet (neues Deployment), und das Seed-Skript ist gelaufen. Dabei wurden alle Symbole gelöscht. Dieser Bug existierte bereits vor meinen Änderungen in den Seed-Skripten, ist aber jetzt zum ersten Mal aufgetreten, weil:
|
||||
- Vorher hat das Skript nur selten gegriffen (Icons waren schon genug da)
|
||||
- Jetzt wurde der Container frisch gestartet, und die Bedingung `ICON_COUNT < 100` war true
|
||||
|
||||
---
|
||||
|
||||
## Auswirkungen
|
||||
|
||||
### Was ist weg?
|
||||
- ❌ Alle `IconAsset`-Einträge (System-Symbole)
|
||||
- ❌ Alle `TenantSymbol`-Einträge (mandantenspezifische Symbol-Aktivierungen)
|
||||
- ❌ Alle Icon-Category-Zuordnungen
|
||||
|
||||
### Was ist noch da?
|
||||
- ✅ Alle Projekte (Zeichnungen)
|
||||
- ✅ Alle Features (Linien, Symbole, Texte in den Zeichnungen)
|
||||
- ✅ Alle Journal-Einträge
|
||||
- ✅ Alle Benutzer, Tenants, etc.
|
||||
|
||||
### Was ist mit den Zeichnungen?
|
||||
Die Zeichnungen sind intakt, aber die Symbole darin zeigen 404. Jedes Symbol in einer Zeichnung speichert in den Properties:
|
||||
```json
|
||||
{
|
||||
"iconId": "alte-gelöschte-uuid",
|
||||
"imageUrl": ""
|
||||
}
|
||||
```
|
||||
|
||||
Da die `iconId` auf einen gelöschten Eintrag verweist, kann das Bild nicht mehr geladen werden.
|
||||
|
||||
---
|
||||
|
||||
## Fix (bereits gepusht)
|
||||
|
||||
**Commit:** `5adadd2`
|
||||
|
||||
**Änderungen:**
|
||||
1. `deleteMany` aus `seed.js` und `seed-icons-only.js` entfernt
|
||||
2. Stattdessen **Upsert-Logik**: Update by `fileKey`, create only if missing
|
||||
3. Dadurch bleiben bestehende IDs erhalten, und es gibt keine Cascade-Löschungen mehr
|
||||
4. `prisma/migrate.js` um neue Phase-1-Tabellen erweitert
|
||||
|
||||
**Das verhindert zukünftige Löschungen, stellt aber bereits gelöschte Daten NICHT wieder her.**
|
||||
|
||||
---
|
||||
|
||||
## Wiederherstellung
|
||||
|
||||
Um die Symbole und Zeichnungen wiederherzustellen, gibt es zwei Wege:
|
||||
|
||||
### Option A: Datenbank-Backup (empfohlen)
|
||||
Falls ein `pg_dump` oder Snapshot vor dem 20. Mai existiert, können die Tabellen `icon_assets`, `icon_categories` und `tenant_symbols` daraus restored werden.
|
||||
|
||||
### Option B: Recovery-Skript
|
||||
Ein Skript, das:
|
||||
1. Alle System-Icons aus `public/signaturen/` neu in die DB einspielt
|
||||
2. Für jeden Tenant die Standard-Symbole neu aktiviert
|
||||
3. Die Zeichnungen scannt und die `iconId`-Referenzen auf die neuen IDs updated
|
||||
|
||||
---
|
||||
|
||||
## Lessons Learned
|
||||
|
||||
1. **Seed-Skripte dürfen niemals `deleteMany` auf verknüpfte Daten ausführen**
|
||||
2. `onDelete: Cascade` ist gefährlich bei Daten, die von Benutzern referenziert werden
|
||||
3. Container-Start-Skripte müssen idempotent sein (mehrfaches Ausführen = gleiches Ergebnis)
|
||||
4. Vor Deployment-Änderungen sollte ein DB-Backup gemacht werden
|
||||
|
||||
---
|
||||
|
||||
## Nächste Schritte
|
||||
|
||||
1. [ ] Recovery-Skript erstellen oder Backup einspielen
|
||||
2. [ ] Alle bestehenden Zeichnungen auf Korrektheit prüfen
|
||||
3. [ ] Optional: `onDelete: Cascade` auf `onDelete: SetNull` ändern, um zukünftige Probleme zu vermeiden
|
||||
Reference in New Issue
Block a user