/** * Importiert eine Datenschutzerklärung aus einer JSON-Datei in die Datenbank. * * npm run import-policy -- data/policies/bauantrag.json * npm run import-policy -- data/policies/bauantrag.json --update * * Ohne --update wird ein vorhandener Datensatz mit gleichem Titel nicht * überschrieben, sondern der Import abgebrochen. Schlüssel mit führendem * Unterstrich (z. B. "_quelle") sind Kommentare und werden ignoriert. */ const fs = require("fs") const path = require("path") const { PrismaClient } = require("@prisma/client") const { REQUIRED_DEFAULTED_FIELDS, readDefaults, foldIntoDefaults, } = require("./lib/policy-defaults") const { resolveSlug } = require("./lib/slug") const prisma = new PrismaClient() // Felder, die die Erklärung selbst mitbringen muss const requiredFields = [ "title", "processingDescription", "processingPurposes", "legalBasis", "recipients", ] // Felder ohne Standard-Rückfall; leer bedeutet: Abschnitt entfällt. // storageDuration ist nach Art. 13 Abs. 2 lit. a zur Veröffentlichung Pflicht – // das Skript importiert trotzdem, warnt aber (siehe unten). const optionalFields = [ "storageDuration", "dataSource", "dataCategories", "legitimateInterests", ] async function importPolicy() { const file = process.argv.find((arg) => arg.endsWith(".json")) const allowUpdate = process.argv.includes("--update") if (!file) { console.error("Bitte eine JSON-Datei angeben, z. B.:") console.error(" npm run import-policy -- data/policies/bauantrag.json") process.exitCode = 1 return } const filePath = path.resolve(process.cwd(), file) if (!fs.existsSync(filePath)) { console.error(`Datei nicht gefunden: ${filePath}`) process.exitCode = 1 return } const raw = JSON.parse(fs.readFileSync(filePath, "utf8")) const missing = requiredFields.filter((field) => !String(raw[field] ?? "").trim()) if (missing.length > 0) { console.error(`Pflichtfelder fehlen oder sind leer: ${missing.join(", ")}`) process.exitCode = 1 return } // Bausteine, die weder in der Datei noch im Standard stehen, fehlen wirklich const defaults = await readDefaults(prisma) const missingBoilerplate = REQUIRED_DEFAULTED_FIELDS.filter( (field) => !String(raw[field] ?? "").trim() && !String(defaults[field] ?? "").trim() ) if (missingBoilerplate.length > 0) { console.error( `Weder in der Datei noch als Standardtext vorhanden: ${missingBoilerplate.join(", ")}` ) process.exitCode = 1 return } const data = {} for (const field of requiredFields) { data[field] = String(raw[field]).trim() } for (const field of optionalFields) { const value = String(raw[field] ?? "").trim() data[field] = value || null } data.isPublic = raw.isPublic !== undefined ? Boolean(raw.isPublic) : true // Bausteine übernehmen; was dem Standard entspricht, wird zu null for (const field of Object.keys(defaults)) { data[field] = raw[field] } const overrides = foldIntoDefaults(data, defaults) const existing = await prisma.privacyPolicy.findFirst({ where: { title: data.title }, select: { id: true, title: true, slug: true }, }) if (existing && !allowUpdate) { console.error( `Es existiert bereits eine Erklärung mit dem Titel „${data.title}“ (ID ${existing.id}).` ) console.error("Zum Überschreiben erneut mit --update aufrufen.") process.exitCode = 1 return } // Bezeichner für die öffentliche URL; bleibt bei unverändertem Titel gleich data.slug = await resolveSlug(prisma, data.title, existing) const policy = existing ? await prisma.privacyPolicy.update({ where: { id: existing.id }, data }) : await prisma.privacyPolicy.create({ data }) console.log( `${existing ? "Aktualisiert" : "Importiert"}: „${policy.title}“ (ID ${policy.id}, ${ policy.isPublic ? "öffentlich" : "privat" })` ) console.log(`Öffentliche Adresse: /erklaerung/${policy.slug}`) console.log( overrides.length > 0 ? `Weicht vom Standard ab: ${overrides.join(", ")}` : "Folgt durchgehend den Standardtexten" ) // Pflichtangaben, die zur Veröffentlichung fehlen. Der Import wird deshalb // nicht abgebrochen – die Angaben kennt nur die Fachabteilung. const luecken = [] if (!data.storageDuration) luecken.push("Speicherdauer (Art. 13 Abs. 2 lit. a)") if (data.dataSource && !data.dataCategories) { luecken.push("Kategorien der Daten (Art. 14 Abs. 1 lit. d)") } if (luecken.length > 0) { console.warn(`WARNUNG: fehlt zur Veröffentlichung – ${luecken.join(", ")}`) } if (raw._quelle) console.log(`Quelle: ${raw._quelle}`) } importPolicy() .catch((error) => { console.error("Fehler beim Import:", error) process.exitCode = 1 }) .finally(async () => { await prisma.$disconnect() })