feat(schema): Phase 1 Symbol Architecture — SymbolTemplate, TenantCategory, TenantSymbol refactor + seed + migration scripts
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 14m40s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 14m40s
This commit is contained in:
112
prisma/migrate-tenant-symbols.ts
Normal file
112
prisma/migrate-tenant-symbols.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* Post-Migration Script: Migrate existing TenantSymbols to new architecture.
|
||||
*
|
||||
* Run AFTER applying the schema migration (20260520_symbol_architecture).
|
||||
*
|
||||
* Steps:
|
||||
* 1. Create a default "Meine Symbole" TenantCategory for each tenant that has TenantSymbols
|
||||
* 2. Migrate existing TenantSymbols: set name, svgPath, categoryId, migratedFromIconId
|
||||
* 3. Verify consistency
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node prisma/migrate-tenant-symbols.ts
|
||||
*/
|
||||
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
async function main() {
|
||||
console.log('🔧 Migrating existing TenantSymbols...')
|
||||
|
||||
// Step 1: Get all tenants that have existing tenantSymbols
|
||||
const tenantsWithSymbols = await (prisma as any).tenantSymbol.groupBy({
|
||||
by: ['tenantId'],
|
||||
_count: { id: true },
|
||||
})
|
||||
|
||||
console.log(`Found ${tenantsWithSymbols.length} tenants with existing symbols`)
|
||||
|
||||
let categoriesCreated = 0
|
||||
let symbolsMigrated = 0
|
||||
|
||||
for (const group of tenantsWithSymbols) {
|
||||
const tenantId = group.tenantId
|
||||
|
||||
// Step 2: Create default category for this tenant
|
||||
let defaultCategory = await (prisma as any).tenantCategory.findFirst({
|
||||
where: { tenantId, name: 'Meine Symbole' },
|
||||
})
|
||||
|
||||
if (!defaultCategory) {
|
||||
defaultCategory = await (prisma as any).tenantCategory.create({
|
||||
data: {
|
||||
tenantId,
|
||||
name: 'Meine Symbole',
|
||||
sortOrder: 0,
|
||||
},
|
||||
})
|
||||
categoriesCreated++
|
||||
}
|
||||
|
||||
// Step 3: Migrate all tenantSymbols for this tenant
|
||||
const tenantSymbols = await (prisma as any).tenantSymbol.findMany({
|
||||
where: { tenantId },
|
||||
include: { icon: true },
|
||||
})
|
||||
|
||||
for (const ts of tenantSymbols) {
|
||||
const updates: any = {}
|
||||
|
||||
// name = customName || icon.name
|
||||
if (!ts.name) {
|
||||
updates.name = ts.customName || ts.icon?.name || 'Unbenannt'
|
||||
}
|
||||
|
||||
// svgPath = icon.fileKey
|
||||
if (!ts.svgPath && ts.icon?.fileKey) {
|
||||
updates.svgPath = ts.icon.fileKey
|
||||
}
|
||||
|
||||
// categoryId = default category
|
||||
if (!ts.categoryId) {
|
||||
updates.categoryId = defaultCategory.id
|
||||
}
|
||||
|
||||
// migratedFromIconId = current iconId
|
||||
if (!ts.migratedFromIconId) {
|
||||
updates.migratedFromIconId = ts.iconId
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
await (prisma as any).tenantSymbol.update({
|
||||
where: { id: ts.id },
|
||||
data: updates,
|
||||
})
|
||||
symbolsMigrated++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ Done: ${categoriesCreated} categories created, ${symbolsMigrated} symbols migrated`)
|
||||
|
||||
// Verification
|
||||
const unmappedCount = await (prisma as any).tenantSymbol.count({
|
||||
where: { categoryId: null },
|
||||
})
|
||||
|
||||
if (unmappedCount > 0) {
|
||||
console.warn(`⚠️ ${unmappedCount} tenantSymbols still have no categoryId!`)
|
||||
} else {
|
||||
console.log('✅ All tenantSymbols have a category assigned')
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect()
|
||||
})
|
||||
Reference in New Issue
Block a user