fix(seed): prevent cascade-deletion of tenant_symbols on container restart
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 12m23s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 12m23s
This commit is contained in:
@@ -231,6 +231,65 @@ async function migrate() {
|
|||||||
console.log(' tenant_symbols table skipped:', e.message)
|
console.log(' tenant_symbols table skipped:', e.message)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Step 13: Create symbol_templates table ───
|
||||||
|
console.log(' [13] Creating symbol_templates table...')
|
||||||
|
try {
|
||||||
|
await prisma.$executeRawUnsafe(`
|
||||||
|
CREATE TABLE IF NOT EXISTS symbol_templates (
|
||||||
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
"fileKey" TEXT NOT NULL,
|
||||||
|
"originalFilename" TEXT NOT NULL,
|
||||||
|
"displayName" TEXT,
|
||||||
|
"categoryName" TEXT,
|
||||||
|
"svgPath" TEXT,
|
||||||
|
"metadata" JSONB NOT NULL DEFAULT '{}',
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
UNIQUE("fileKey")
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
console.log(' symbol_templates table created (or already exists)')
|
||||||
|
} catch (e) {
|
||||||
|
console.log(' symbol_templates table skipped:', e.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Step 14: Create tenant_categories table ───
|
||||||
|
console.log(' [14] Creating tenant_categories table...')
|
||||||
|
try {
|
||||||
|
await prisma.$executeRawUnsafe(`
|
||||||
|
CREATE TABLE IF NOT EXISTS tenant_categories (
|
||||||
|
id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||||
|
"name" TEXT NOT NULL,
|
||||||
|
"description" TEXT,
|
||||||
|
"sortOrder" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
"isActive" BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
"tenantId" TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE
|
||||||
|
)
|
||||||
|
`)
|
||||||
|
console.log(' tenant_categories table created (or already exists)')
|
||||||
|
} catch (e) {
|
||||||
|
console.log(' tenant_categories table skipped:', e.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Step 15: Extend tenant_symbols with Phase 1 columns ───
|
||||||
|
console.log(' [15] Extending tenant_symbols with Phase 1 columns...')
|
||||||
|
const tenantSymbolColumns = [
|
||||||
|
`ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "name" TEXT`,
|
||||||
|
`ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "svgPath" TEXT`,
|
||||||
|
`ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "isUploaded" BOOLEAN NOT NULL DEFAULT false`,
|
||||||
|
`ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "categoryId" TEXT REFERENCES tenant_categories(id) ON DELETE SET NULL`,
|
||||||
|
`ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "migratedFromIconId" TEXT`,
|
||||||
|
`ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP`,
|
||||||
|
`ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP`,
|
||||||
|
]
|
||||||
|
let tsAdded = 0
|
||||||
|
for (const sql of tenantSymbolColumns) {
|
||||||
|
try { await prisma.$executeRawUnsafe(sql); tsAdded++ } catch (e) { /* ignore */ }
|
||||||
|
}
|
||||||
|
console.log(` ${tsAdded}/${tenantSymbolColumns.length} tenant_symbol columns added`)
|
||||||
|
|
||||||
console.log('✅ Database migrations complete')
|
console.log('✅ Database migrations complete')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,11 +44,10 @@ async function main() {
|
|||||||
patterns: ['Massstab', 'Nordrichtung', 'Windrichtung'] },
|
patterns: ['Massstab', 'Nordrichtung', 'Windrichtung'] },
|
||||||
]
|
]
|
||||||
|
|
||||||
// Delete ALL old system icons (regardless of fileKey pattern)
|
// NOTE: We intentionally do NOT delete old system icons here.
|
||||||
const deleted = await prisma.iconAsset.deleteMany({
|
// TenantSymbol rows reference IconAsset.id via foreign key.
|
||||||
where: { isSystem: true },
|
// Deleting would either break references (tenant symbols become 404s)
|
||||||
})
|
// or cascade-delete tenant symbols. Instead we upsert by fileKey.
|
||||||
console.log(`🗑️ ${deleted.count} old system icons removed`)
|
|
||||||
|
|
||||||
// Upsert global categories (preserves tenant categories)
|
// Upsert global categories (preserves tenant categories)
|
||||||
const catMap = {}
|
const catMap = {}
|
||||||
@@ -90,6 +89,7 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let created = 0
|
let created = 0
|
||||||
|
let updated = 0
|
||||||
for (const file of svgFiles) {
|
for (const file of svgFiles) {
|
||||||
let name = file.replace('.svg', '')
|
let name = file.replace('.svg', '')
|
||||||
name = name.replace(/_de$/i, '').replace(/_DE$/i, '').replace(/-de$/i, '')
|
name = name.replace(/_de$/i, '').replace(/_DE$/i, '').replace(/-de$/i, '')
|
||||||
@@ -99,7 +99,21 @@ async function main() {
|
|||||||
const category = findCategory(file)
|
const category = findCategory(file)
|
||||||
|
|
||||||
const existing = await prisma.iconAsset.findFirst({ where: { fileKey } })
|
const existing = await prisma.iconAsset.findFirst({ where: { fileKey } })
|
||||||
if (!existing) {
|
if (existing) {
|
||||||
|
await prisma.iconAsset.update({
|
||||||
|
where: { id: existing.id },
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
categoryId: category.id,
|
||||||
|
mimeType: 'image/svg+xml',
|
||||||
|
isSystem: true,
|
||||||
|
isActive: true,
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
updated++
|
||||||
|
} else {
|
||||||
await prisma.iconAsset.create({
|
await prisma.iconAsset.create({
|
||||||
data: {
|
data: {
|
||||||
name,
|
name,
|
||||||
@@ -115,7 +129,7 @@ async function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`✅ FKS Signaturen: ${created} new SVG icons created (${svgFiles.length} total)`)
|
console.log(`✅ FKS Signaturen: ${created} created, ${updated} updated (${svgFiles.length} total)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -110,11 +110,10 @@ async function main() {
|
|||||||
patterns: ['Massstab', 'Nordrichtung', 'Windrichtung'] },
|
patterns: ['Massstab', 'Nordrichtung', 'Windrichtung'] },
|
||||||
]
|
]
|
||||||
|
|
||||||
// Delete ALL old system icons (regardless of fileKey pattern)
|
// NOTE: We intentionally do NOT delete old system icons here.
|
||||||
const deletedIcons = await prisma.iconAsset.deleteMany({
|
// TenantSymbol rows reference IconAsset.id via foreign key.
|
||||||
where: { isSystem: true },
|
// Deleting would either break references (tenant symbols become 404s)
|
||||||
})
|
// or cascade-delete tenant symbols. Instead we upsert by fileKey.
|
||||||
console.log(`🗑️ ${deletedIcons.count} old system icons removed`)
|
|
||||||
|
|
||||||
// Clean up empty global categories
|
// Clean up empty global categories
|
||||||
const oldGlobalCats = await prisma.iconCategory.findMany({ where: { tenantId: null } })
|
const oldGlobalCats = await prisma.iconCategory.findMany({ where: { tenantId: null } })
|
||||||
@@ -163,6 +162,7 @@ async function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let created = 0
|
let created = 0
|
||||||
|
let updated = 0
|
||||||
for (const file of svgFiles) {
|
for (const file of svgFiles) {
|
||||||
// Clean name: remove .svg, remove _de/_DE suffix
|
// Clean name: remove .svg, remove _de/_DE suffix
|
||||||
let name = file.replace('.svg', '')
|
let name = file.replace('.svg', '')
|
||||||
@@ -173,7 +173,21 @@ async function main() {
|
|||||||
const category = findCategory(file)
|
const category = findCategory(file)
|
||||||
|
|
||||||
const existing = await prisma.iconAsset.findFirst({ where: { fileKey } })
|
const existing = await prisma.iconAsset.findFirst({ where: { fileKey } })
|
||||||
if (!existing) {
|
if (existing) {
|
||||||
|
await prisma.iconAsset.update({
|
||||||
|
where: { id: existing.id },
|
||||||
|
data: {
|
||||||
|
name,
|
||||||
|
categoryId: category.id,
|
||||||
|
mimeType: 'image/svg+xml',
|
||||||
|
isSystem: true,
|
||||||
|
isActive: true,
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
updated++
|
||||||
|
} else {
|
||||||
await prisma.iconAsset.create({
|
await prisma.iconAsset.create({
|
||||||
data: {
|
data: {
|
||||||
name,
|
name,
|
||||||
@@ -189,7 +203,7 @@ async function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`✅ FKS Signaturen: ${created} new icons created (${svgFiles.length} total SVGs)`)
|
console.log(`✅ FKS Signaturen: ${created} created, ${updated} updated (${svgFiles.length} total SVGs)`)
|
||||||
|
|
||||||
// Create a demo project
|
// Create a demo project
|
||||||
const demoProject = await prisma.project.upsert({
|
const demoProject = await prisma.project.upsert({
|
||||||
|
|||||||
Reference in New Issue
Block a user