chore(seed/docs): production safety, scripts audit, documentation
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 17m48s

This commit is contained in:
Pepe Ziberi
2026-05-20 17:32:28 +02:00
parent ecc9003a73
commit 08a66095e6
2 changed files with 251 additions and 1 deletions

247
docs/backup-und-seeding.md Normal file
View File

@@ -0,0 +1,247 @@
# Backup & Seeding — Betriebsnotiz Lageplan
> Stand: 2026-05-20
> Autor: Automatisch generiert nach Produktionsvorfall
## Inhalt
1. [Wichtige Regeln](#wichtige-regeln)
2. [Backup-Strategie](#backup-strategie)
3. [Seed-Skripte](#seed-skripte)
4. [Post-Deploy Prüfung](#post-deploy-prüfung)
5. [Notfall-Recovery](#notfall-recovery)
6. [Docker-Build Hinweise](#docker-build-hinweise)
---
## Wichtige Regeln
### Nie löschen
- **Keine** `deleteMany()`, `DELETE FROM`, `TRUNCATE` oder Cascade-Resets in Production
- **Nie** direkt in `icon_assets`, `icon_categories`, `tenant_symbols`, `features`, `projects` löschen
- Alle Seed-Skripte müssen **idempotent** sein (`upsert` statt `delete` + `create`)
- Vor jedem Seed/Repair in Production: **Databasus-Backup erstellen**
### Databasus Backup
- Databasus ist als GUI-Backup eingerichtet
- Backups werden gespeichert unter: `/volume1/docker/backups/databasus`
- **Nie** das Docker-Volume unter `/volume1/@docker` als einzige Backup-Strategie verwenden
### Container-Informationen
| Service | Container-Name |
|---------|---------------|
| PostgreSQL DB | `lageplan-db-1` |
| Web App | `lageplan-web-1` |
| DB-Name | `lageplan` |
| DB-User | `lageplan` |
---
## Backup-Strategie
### Automatisch (Databasus)
1. Databasus GUI öffnen
2. Datenbank `lageplan` auswählen
3. "Backup Now" klicken
4. Backup wird unter `/volume1/docker/backups/databasus` abgelegt
### Manuell (pg_dump)
```bash
# In der NAS-Shell (oder im DB-Container)
docker exec lageplan-db-1 pg_dump -U lageplan -d lageplan \
--no-owner --no-privileges \
> /volume1/docker/backups/databasus/lageplan-manual-$(date +%Y%m%d-%H%M%S).sql
```
### Wiederherstellen
```bash
# 1. Backup-Datei prüfen
ls -la /volume1/docker/backups/databasus/
# 2. App stoppen
docker stop lageplan-web-1
# 3. DB zurückspielen (ACHTUNG: überschreibt aktuelle Daten!)
docker exec -i lageplan-db-1 psql -U lageplan -d lageplan \
< /volume1/docker/backups/databasus/lageplan-backup-YYYYmmdd-HHMMSS.sql
# 4. App starten
docker start lageplan-web-1
```
---
## Seed-Skripte
### Übersicht
| Skript | Zweck | Sicherheit | Aufruf |
|--------|-------|-----------|--------|
| `prisma/seed.js` | Vollständiger Seed (Tenant, Users, Icons, HoseTypes, Demo-Projekt) | ✅ Idempotent, nur `upsert` | `npm run db:seed` oder `node prisma/seed.js` |
| `prisma/seed-icons-only.js` | Nur Icons aus `public/signaturen/*.svg` | ✅ Idempotent, nur `upsert` | `npm run db:seed:icons` oder `node prisma/seed-icons-only.js` |
| `prisma/seed.ts` | Legacy-TypeScript-Seed (korrigiert, aber nicht der Primary) | ✅ Korrigiert | Nicht direkt aufrufen |
| `prisma/seed-symbol-templates.ts` | Erstellt `SymbolTemplate`-Einträge | ✅ Nur `findFirst` + `create` (skip if exists) | `npx tsx prisma/seed-symbol-templates.ts` |
| `prisma/migrate-tenant-symbols.ts` | Migriert bestehende `TenantSymbol` zu neuem Schema | ✅ Nur `findFirst`, `create`, `update` | `npx tsx prisma/migrate-tenant-symbols.ts` |
| `prisma/recover-symbols.js` | Recovery: Icons + TenantSymbols wiederherstellen | ✅ Dry-Run/Apply Modus | `npm run recover:symbols:dry-run` / `npm run recover:symbols:apply` |
| `prisma/repair-features.js` | Repariert kaputte `iconId`-Referenzen in Features | ✅ Dry-Run/Apply Modus + Backup | `npm run repair:features:dry-run` / `npm run repair:features:apply` |
| `prisma/migrate.js` | DB-Migrationen (Raw SQL, idempotent) | ✅ Keine Deletes auf User-Daten | Wird automatisch beim Container-Start ausgeführt |
### Empfohlene Seed-Strategie
1. **Neue Umgebung** (erstmalig):
```bash
node prisma/seed.js
```
2. **Nur Icons aktualisieren** (z.B. nach SVG-Update):
```bash
node prisma/seed-icons-only.js
```
3. **Recovery nach Icon-Verlust**:
```bash
# 1. Erst Databasus-Backup erstellen!
# 2. Dann:
node prisma/recover-symbols.js --dry-run # Analyse
node prisma/recover-symbols.js --apply # Anwenden
```
---
## Post-Deploy Prüfung
### 1. SVG-Dateien im Container
```bash
docker exec -it lageplan-web-1 sh -c 'find /app/public/signaturen -type f -name "*.svg" | wc -l'
```
**Erwartung:** ca. 117
### 2. Icons seeden
```bash
docker exec -it lageplan-web-1 node prisma/seed-icons-only.js
```
**Erwartung:**
```
✅ 10 icon categories upserted
✅ FKS Signaturen: X created, Y updated (117 total SVGs)
```
### 3. Datenbank-Prüfung
```bash
docker exec -it lageplan-db-1 psql -U lageplan -d lageplan -c "
SELECT 'icon_categories' AS table_name, COUNT(*) AS count FROM icon_categories
UNION ALL
SELECT 'icon_assets' AS table_name, COUNT(*) AS count FROM icon_assets
UNION ALL
SELECT 'tenant_symbols' AS table_name, COUNT(*) AS count FROM tenant_symbols
UNION ALL
SELECT 'features' AS table_name, COUNT(*) AS count FROM features
UNION ALL
SELECT 'projects' AS table_name, COUNT(*) AS count FROM projects;
"
```
### 4. Logs prüfen
```bash
docker logs --tail=100 lageplan-web-1
```
**Keine** der folgenden Fehler sollen auftreten:
- `404` für `/signaturen/*.svg`
- `Prisma error` wegen `tenant_symbols.categoryId`
- `Seed failed`
---
## Notfall-Recovery
### Szenario: Symbole sind verschwunden (404)
**Ursache:** `IconAsset`-Einträge wurden gelöscht (z.B. durch altes Seed-Skript mit `deleteMany`)
**Lösung:**
```bash
# 1. Backup erstellen (Databasus GUI oder pg_dump)
# 2. Container-Status prüfen
docker exec -it lageplan-web-1 sh -c 'ls -la /app/public/signaturen/ | head -5'
# → Sollte SVG-Dateien anzeigen
# 3. Icons neu seeden (idempotent, sicher)
docker exec -it lageplan-web-1 node prisma/seed-icons-only.js
# 4. Tenant-Symbols wiederherstellen (falls auch gelöscht)
docker exec -it lageplan-web-1 node prisma/recover-symbols.js --dry-run
docker exec -it lageplan-web-1 node prisma/recover-symbols.js --apply
# 5. Prüfen
docker exec -it lageplan-web-1 node -e "
const {PrismaClient} = require('@prisma/client');
const p = new PrismaClient();
p.iconAsset.count({where:{isSystem:true}}).then(c=>console.log('System-Icons:',c));
p.tenantSymbol.count().then(c=>console.log('TenantSymbols:',c));
"
```
### Szenario: Features haben kaputte iconId-Referenzen
**Ursache:** Alte `iconId`-UUIDs in `Feature.properties` zeigen auf gelöschte `IconAsset`s
**Lösung:**
```bash
# 1. Dry-Run: Analyse
docker exec -it lageplan-web-1 node prisma/repair-features.js --dry-run
# 2. Apply: Reparatur (erstellt automatisch JSON-Backup)
docker exec -it lageplan-web-1 node prisma/repair-features.js --apply
```
---
## Docker-Build Hinweise
### public/signaturen muss im Image sein
- `public/signaturen/*.svg` muss im Git-Index sein (`.gitignore` erlaubt es explizit)
- `.dockerignore` darf `public/signaturen/` **nicht** ausschliessen
- `Dockerfile` kopiert `public/` in die Runtime-Stage:
```dockerfile
COPY --from=builder /app/public ./public
```
### Next.js standalone
- `next.config.js` hat `output: 'standalone'`
- Der Build erzeugt `.next/standalone/` (minimaler Server)
- `public/` wird explizit in die Runtime-Stage kopiert
### Multi-Stage Build
1. **deps**: `npm ci` (nur Dependencies)
2. **builder**: `npm run build` (inkl. Prisma Generate)
3. **runner**: Runtime-Image mit `public/`, `.next/standalone/`, Prisma-Client
---
## Änderungshistorie
| Datum | Änderung |
|-------|----------|
| 2026-05-20 | `.gitignore` korrigiert: `Signaturen/` → `/Signaturen/` (Root-only), `public/signaturen/*.svg` erlaubt |
| 2026-05-20 | 117 SVG-Dateien force-added zum Git-Index |
| 2026-05-20 | `.dockerignore` korrigiert: `Signaturen` → `/Signaturen/` |
| 2026-05-20 | `package.json`: `db:seed` zeigt jetzt auf `prisma/seed.js` statt broken `seed.ts` |
| 2026-05-20 | `seed.ts` korrigiert: Role-Enums + stabile IDs für `iconCategory.upsert` |
| 2026-05-20 | Alle Seed-Skripte auf `deleteMany`-Freiheit geprüft |
| 2026-05-20 | Dokumentation `docs/backup-und-seeding.md` erstellt |

View File

@@ -15,9 +15,12 @@
"db:seed": "node prisma/seed.js", "db:seed": "node prisma/seed.js",
"db:studio": "prisma studio", "db:studio": "prisma studio",
"postinstall": "prisma generate", "postinstall": "prisma generate",
"db:seed:icons": "node prisma/seed-icons-only.js",
"db:seed:safe": "node prisma/seed.js",
"repair:features:dry-run": "node prisma/repair-features.js --dry-run", "repair:features:dry-run": "node prisma/repair-features.js --dry-run",
"repair:features:apply": "node prisma/repair-features.js --apply", "repair:features:apply": "node prisma/repair-features.js --apply",
"recover:symbols": "node prisma/recover-symbols.js" "recover:symbols:dry-run": "node prisma/recover-symbols.js --dry-run",
"recover:symbols:apply": "node prisma/recover-symbols.js --apply"
}, },
"dependencies": { "dependencies": {
"@dnd-kit/core": "^6.1.0", "@dnd-kit/core": "^6.1.0",