import { drizzle } from "drizzle-orm/node-postgres"; import { Pool } from "pg"; import * as schema from "@/db/schema"; import type { Tx } from "@/lib/audit"; import { MERKMALE } from "./data/merkmale"; import { VEHICLE_TEMPLATES } from "./data/vehicle-templates"; import { EQUIPMENT_CATEGORIES } from "./data/equipment-categories"; import { upsertMerkmal, upsertVehicleTemplate, upsertTemplateMerkmal, upsertTemplateAlias, upsertEquipmentCategory, upsertCategoryMerkmal, pruneTemplateAliasse, } from "./upsert"; /** * Katalog-Seed (Workstream 9): füllt Merkmale, Enum-Optionen, Fahrzeug-Vorlagen, * deren Pflichtmerkmale + Aliasse sowie Geräte-Kategorien aus dem NÖ-Katalog. * * Idempotent (Querschnittsstandard 7): ausschließlich Upserts auf Natural Keys; * mehrfaches Ausführen ändert keine Counts. Läuft in EINER Transaktion in der * Reihenfolge Merkmale → Optionen → Vorlagen → Vorlagen-Merkmale → Aliasse → * Kategorien (sequenzielle Awaits, Slug→ID-Map). * * Liest `DATABASE_URL` direkt aus der Umgebung (keine Next.js-Env-Validierung, * wie scripts/migrate.ts und scripts/seed-auth.ts). */ /** Reine Seed-Logik gegen eine bestehende Transaktion (für Tests injizierbar). */ export async function seedCatalog(tx: Tx): Promise { // 1. Merkmale (+ Optionen) → Slug→ID-Map. const merkmalIdBySlug = new Map(); for (const m of MERKMALE) { const id = await upsertMerkmal(tx, m); merkmalIdBySlug.set(m.slug, id); } // 2. Vorlagen → Pflichtmerkmale → Aliasse. for (let i = 0; i < VEHICLE_TEMPLATES.length; i++) { const t = VEHICLE_TEMPLATES[i]!; const templateId = await upsertVehicleTemplate(tx, t, i); for (let j = 0; j < t.merkmale.length; j++) { const tm = t.merkmale[j]!; const merkmalId = merkmalIdBySlug.get(tm.slug); if (!merkmalId) { throw new Error( `Vorlage ${t.code} referenziert unbekanntes Merkmal '${tm.slug}'`, ); } await upsertTemplateMerkmal(tx, templateId, merkmalId, tm, j); } for (const a of t.aliasse) { await upsertTemplateAlias(tx, templateId, a.alias, a.bestaetigt); } await pruneTemplateAliasse( tx, templateId, t.aliasse.map((a) => a.alias), ); } // 3. Geräte-Kategorien (+ optionale Merkmal-Verknüpfungen). for (const c of EQUIPMENT_CATEGORIES) { const categoryId = await upsertEquipmentCategory(tx, c); const slugs = c.merkmalSlugs ?? []; for (let k = 0; k < slugs.length; k++) { const merkmalId = merkmalIdBySlug.get(slugs[k]!); if (!merkmalId) { throw new Error( `Kategorie ${c.name} referenziert unbekanntes Merkmal '${slugs[k]}'`, ); } await upsertCategoryMerkmal(tx, categoryId, merkmalId, k); } } } /** Standalone-Runner (npm run db:seed). */ export async function main(): Promise { const connectionString = process.env.DATABASE_URL; if (!connectionString) { throw new Error("DATABASE_URL ist nicht gesetzt."); } const pool = new Pool({ connectionString, max: 1 }); const db = drizzle(pool, { schema }); try { await db.transaction(async (tx) => { await seedCatalog(tx as Tx); }); console.log("Katalog-Seed erfolgreich (idempotent)."); console.log(` Merkmale: ${MERKMALE.length}`); console.log(` Fahrzeug-Vorlagen: ${VEHICLE_TEMPLATES.length}`); console.log(` Geräte-Kategorien: ${EQUIPMENT_CATEGORIES.length}`); } finally { await pool.end(); } } // Nur ausführen, wenn direkt gestartet (nicht beim Import in Tests). const isMain = typeof process !== "undefined" && process.argv[1] !== undefined && import.meta.url === `file://${process.argv[1]}`; if (isMain) { main().catch((err: unknown) => { console.error("Katalog-Seed fehlgeschlagen:", err); process.exit(1); }); }