/** * Recovery-Skript für Zeichnungen (Features) nach Symbol-Verlust * * Problem: Features verweisen auf gelöschte iconId-UUIDs. * Die imageUrl ist "/api/icons/{alte-uuid}/image" → 404. * * Lösung: * Die alte UUID→SVG-Zuordnung ist verloren. ABER: * - Alle bestehenden IconAssets wurden per Upsert neu erstellt (gleiche fileKeys) * - Jedes Feature hat noch die alte iconId in properties * - Die Sidebar liefert jetzt NEUE iconIds * * Dieses Skript: * 1. Listet alle broken Symbol-Features (iconId zeigt auf gelöschtes Icon) * 2. Für Features wo imageUrl auf /signaturen/ zeigt: direkt fixbar * 3. Für Features mit /api/icons/{uuid}/image: UUID ist verloren → Renderer zeigt ⚠️ * 4. Zählt und listet betroffene Projekte * * Der Renderer wurde angepasst (map-view.tsx): broken Symbole zeigen jetzt * ein ⚠️-Platzhalter statt nichts. User können das Symbol manuell ersetzen. * * Ausführung: * DATABASE_URL=... node prisma/recover-features.js [--dry-run] */ const { PrismaClient } = require('@prisma/client') const prisma = new PrismaClient() const DRY_RUN = process.argv.includes('--dry-run') async function main() { console.log('🔧 FEATURE RECOVERY — Analyse & Reparatur') console.log(` Mode: ${DRY_RUN ? '🔍 DRY RUN' : '⚡ LIVE'}`) console.log('') // ─── 1. Lade alle aktuellen IconAssets ─── const allIcons = await prisma.iconAsset.findMany({ select: { id: true, name: true, fileKey: true }, }) console.log(`📦 ${allIcons.length} IconAssets in der DB`) const iconIdSet = new Set(allIcons.map(i => i.id)) const fileKeyToIcon = {} for (const icon of allIcons) { fileKeyToIcon[icon.fileKey] = icon } // ─── 2. Lade alle Symbol-Features ─── const features = await prisma.feature.findMany({ where: { type: 'symbol' }, include: { project: { select: { id: true, name: true, tenantId: true } } }, }) console.log(`🖼️ ${features.length} Symbol-Features gefunden`) console.log('') let ok = 0 let fixedViaFileKey = 0 let broken = 0 const brokenByProject = {} for (const feature of features) { const props = feature.properties || {} const iconId = props.iconId || '' const imageUrl = props.imageUrl || '' // ─── Check 1: iconId still exists? ─── if (iconId && iconIdSet.has(iconId)) { ok++ continue } // ─── Check 2: imageUrl points to static /signaturen/ file? ─── const sigMatch = imageUrl.match(/\/signaturen\/(.+\.svg)/) if (sigMatch) { const fileKey = `signaturen/${sigMatch[1]}` const matchingIcon = fileKeyToIcon[fileKey] if (matchingIcon) { // Fix: Update iconId to new valid ID const newProps = { ...props, iconId: matchingIcon.id, imageUrl: `/api/icons/${matchingIcon.id}/image`, _recovered: true, _oldIconId: iconId, } if (!DRY_RUN) { await prisma.feature.update({ where: { id: feature.id }, data: { properties: newProps }, }) } fixedViaFileKey++ console.log(` ✅ ${feature.id} → ${matchingIcon.name} (via fileKey)`) continue } } // ─── Check 3: imageUrl is /api/icons/{uuid}/image? ─── // The uuid in the URL = old iconId = deleted → can't resolve // Mark as broken broken++ const projName = feature.project?.name || feature.projectId if (!brokenByProject[projName]) brokenByProject[projName] = [] brokenByProject[projName].push({ featureId: feature.id, iconId, imageUrl, }) } console.log('') console.log('═══════════════════════════════════════════') console.log(` ✅ OK (iconId existiert): ${ok}`) console.log(` 🔧 Gefixt (via fileKey): ${fixedViaFileKey}`) console.log(` ❌ Broken (UUID verloren): ${broken}`) console.log(` 📊 Total: ${features.length}`) console.log('═══════════════════════════════════════════') if (broken > 0) { console.log('') console.log('❌ BROKEN FEATURES pro Projekt:') for (const [projName, items] of Object.entries(brokenByProject)) { console.log(` 📄 "${projName}": ${items.length} Symbole`) for (const item of items) { console.log(` - Feature ${item.featureId} (iconId: ${item.iconId.substring(0, 8)}...)`) } } console.log('') console.log('💡 Diese Symbole zeigen jetzt ein ⚠️ auf der Karte.') console.log(' User müssen das Symbol manuell löschen und neu platzieren.') console.log(' (Select-Mode → Symbol anklicken → DEL → neues Symbol aus Sidebar ziehen)') } if (broken === 0 && fixedViaFileKey === 0) { console.log('') console.log('🎉 Alle Symbol-Features sind OK! Keine Reparatur nötig.') } if (DRY_RUN) { console.log('') console.log('🔍 DRY RUN — Keine Änderungen geschrieben.') } } main() .then(async () => { await prisma.$disconnect() }) .catch(async (e) => { console.error('❌ Fehler:', e) await prisma.$disconnect() process.exit(1) })