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:
123
prisma/seed-symbol-templates.ts
Normal file
123
prisma/seed-symbol-templates.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* Seed-Skript: Erstellt SymbolTemplate-Einträge aus bestehenden IconAsset (isSystem=true)
|
||||
* oder aus dem Dateisystem (public/signaturen/*.svg).
|
||||
*
|
||||
* Ausführen:
|
||||
* npx ts-node prisma/seed-symbol-templates.ts
|
||||
* oder als Teil des Deployments via npx prisma db seed
|
||||
*
|
||||
* Idempotent: bereits existierende Templates werden übersprungen.
|
||||
*/
|
||||
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
import { readdirSync, statSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
const SIGNATUREN_DIR = join(process.cwd(), 'public', 'signaturen')
|
||||
const PACKAGE_ID = 'feuerwehr-ch'
|
||||
const PACKAGE_NAME = 'Feuerwehr Schweiz'
|
||||
|
||||
/**
|
||||
* Versucht Kategorie-Namen aus bestehenden IconAsset / IconCategory abzuleiten.
|
||||
* Fallback: "Sonstiges"
|
||||
*/
|
||||
async function getCategoryMapping(): Promise<Map<string, string>> {
|
||||
const mapping = new Map<string, string>()
|
||||
|
||||
const iconAssets = await (prisma as any).iconAsset.findMany({
|
||||
where: { isSystem: true },
|
||||
include: { category: { select: { name: true } } },
|
||||
})
|
||||
|
||||
for (const asset of iconAssets) {
|
||||
const fileName = asset.fileKey.replace(/^signaturen\//, '').replace(/\.svg$/i, '')
|
||||
const categoryName = asset.category?.name || 'Sonstiges'
|
||||
mapping.set(fileName.toLowerCase(), categoryName)
|
||||
}
|
||||
|
||||
return mapping
|
||||
}
|
||||
|
||||
/**
|
||||
* Liest alle .svg Dateien aus public/signaturen/
|
||||
*/
|
||||
function getSvgFiles(dir: string): string[] {
|
||||
const files: string[] = []
|
||||
try {
|
||||
const entries = readdirSync(dir)
|
||||
for (const entry of entries) {
|
||||
const fullPath = join(dir, entry)
|
||||
const stat = statSync(fullPath)
|
||||
if (stat.isFile() && entry.endsWith('.svg')) {
|
||||
files.push(entry)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('Could not read signaturen directory:', (err as Error).message)
|
||||
}
|
||||
return files.sort()
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('🌱 Seeding SymbolTemplate...')
|
||||
|
||||
const categoryMapping = await getCategoryMapping()
|
||||
const svgFiles = getSvgFiles(SIGNATUREN_DIR)
|
||||
|
||||
console.log(`Found ${svgFiles.length} SVG files in public/signaturen/`)
|
||||
console.log(`IconAsset mapping covers ${categoryMapping.size} entries`)
|
||||
|
||||
let created = 0
|
||||
let skipped = 0
|
||||
|
||||
for (let i = 0; i < svgFiles.length; i++) {
|
||||
const fileName = svgFiles[i]
|
||||
const nameWithoutExt = fileName.replace(/\.svg$/i, '')
|
||||
const displayName = nameWithoutExt
|
||||
.replace(/_/g, ' ')
|
||||
.replace(/-/g, ' ')
|
||||
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
||||
|
||||
const fileKey = `signaturen/${fileName}`
|
||||
|
||||
// Check if already exists
|
||||
const existing = await (prisma as any).symbolTemplate.findFirst({
|
||||
where: { svgPath: fileKey },
|
||||
})
|
||||
|
||||
if (existing) {
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
|
||||
const categoryName =
|
||||
categoryMapping.get(nameWithoutExt.toLowerCase()) || 'Sonstiges'
|
||||
|
||||
await (prisma as any).symbolTemplate.create({
|
||||
data: {
|
||||
packageId: PACKAGE_ID,
|
||||
packageName: PACKAGE_NAME,
|
||||
categoryName,
|
||||
name: displayName,
|
||||
svgPath: fileKey,
|
||||
tags: [nameWithoutExt.toLowerCase(), categoryName.toLowerCase()],
|
||||
sortOrder: i,
|
||||
},
|
||||
})
|
||||
|
||||
created++
|
||||
}
|
||||
|
||||
console.log(`✅ Done: ${created} created, ${skipped} skipped`)
|
||||
}
|
||||
|
||||
main()
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
process.exit(1)
|
||||
})
|
||||
.finally(async () => {
|
||||
await prisma.$disconnect()
|
||||
})
|
||||
Reference in New Issue
Block a user