/** * Daten für die Screenshot-Instanz. * * Alles hier ist frei erfunden. Diese Datei darf niemals gegen eine * Datenbank mit echten Daten laufen — deshalb bricht sie ab, wenn * DEMO_MODUS nicht ausdrücklich auf true steht. * * Aufruf: über npm run screenshots, nicht von Hand. */ import 'dotenv/config' import { PrismaPg } from '@prisma/adapter-pg' import bcrypt from 'bcryptjs' import { PrismaClient } from '../src/generated/prisma/client.js' if (process.env.DEMO_MODUS !== 'true') { console.error( 'Der Demo-Seed läuft nur mit DEMO_MODUS=true. Das ist Absicht: er würde ' + 'sonst erfundene Daten in eine echte Datenbank schreiben.', ) process.exit(1) } const verbindung = process.env.DATABASE_URL if (!verbindung) { console.error('DATABASE_URL ist nicht gesetzt.') process.exit(1) } const db = new PrismaClient({ adapter: new PrismaPg({ connectionString: verbindung }) }) export const DEMO_EMAIL = 'martina.beispiel@planer.invalid' export const DEMO_PASSWORT = 'demo-instanz-nur-fuer-bilder' const BEREICHE = [ { name: 'Firma', farbe: '#2563eb', reihenfolge: 1, arbeitsfensterVon: 450, arbeitsfensterBis: 990, kapazitaetMo: 360, kapazitaetDi: 360, kapazitaetMi: 360, kapazitaetDo: 360, kapazitaetFr: 240, kapazitaetSa: 0, kapazitaetSo: 0, }, { name: 'Gewerbe', farbe: '#7c3aed', reihenfolge: 2, arbeitsfensterVon: 1020, arbeitsfensterBis: 1260, kapazitaetMo: 90, kapazitaetDi: 90, kapazitaetMi: 90, kapazitaetDo: 90, kapazitaetFr: 60, kapazitaetSa: 180, kapazitaetSo: 0, }, { name: 'Privat', farbe: '#16a34a', reihenfolge: 3, arbeitsfensterVon: 1020, arbeitsfensterBis: 1320, kapazitaetMo: 60, kapazitaetDi: 60, kapazitaetMi: 60, kapazitaetDo: 60, kapazitaetFr: 120, kapazitaetSa: 240, kapazitaetSo: 240, }, { name: 'Homelab', farbe: '#ea580c', reihenfolge: 4, arbeitsfensterVon: 1140, arbeitsfensterBis: 1380, kapazitaetMo: 0, kapazitaetDi: 60, kapazitaetMi: 0, kapazitaetDo: 60, kapazitaetFr: 0, kapazitaetSa: 120, kapazitaetSo: 60, }, ] /** Erfundene Protokolleinträge, damit die Protokollseite nicht leer wirkt. */ const PROTOKOLL = [ { aktion: 'bereich.angelegt', objektTyp: 'Bereich', details: { name: 'Homelab' } }, { aktion: 'bereich.geaendert', objektTyp: 'Bereich', details: { vorher: 'Nebenerwerb', nachher: 'Gewerbe' }, }, { aktion: 'passkey.angelegt', objektTyp: 'Passkey', details: { bezeichnung: 'Diensthandy' }, }, ] async function main(): Promise { console.log('Demo-Daten einspielen.') for (const bereich of BEREICHE) { await db.bereich.upsert({ where: { name: bereich.name }, update: bereich, create: bereich, }) } const benutzer = await db.benutzer.upsert({ where: { email: DEMO_EMAIL }, update: { rolle: 'ADMIN', name: 'Martina Beispiel' }, create: { email: DEMO_EMAIL, name: 'Martina Beispiel', rolle: 'ADMIN', passwortHash: await bcrypt.hash(DEMO_PASSWORT, 10), }, }) await db.auditEintrag.deleteMany({}) // Feste Zeitpunkte, damit die Bilder zwischen zwei Läufen gleich bleiben. let versatz = 0 for (const eintrag of PROTOKOLL) { versatz += 37 await db.auditEintrag.create({ data: { zeitpunkt: new Date(Date.UTC(2026, 2, 17, 9, versatz % 60)), benutzerId: benutzer.id, benutzerEmail: benutzer.email, aktion: eintrag.aktion, objektTyp: eintrag.objektTyp, details: eintrag.details, instanz: 'daheim', }, }) } await db.passkey.deleteMany({ where: { benutzerId: benutzer.id } }) await db.passkey.create({ data: { benutzerId: benutzer.id, credentialId: 'demo-passkey-diensthandy', publicKey: Buffer.from([0]), bezeichnung: 'Diensthandy', gesichert: true, erstelltAm: new Date(Date.UTC(2026, 1, 3, 8, 15)), zuletztBenutzt: new Date(Date.UTC(2026, 2, 17, 7, 42)), }, }) console.log(`Demo-Daten stehen bereit. Anmeldung: ${DEMO_EMAIL}`) } main() .then(async () => { await db.$disconnect() }) .catch(async (fehler: unknown) => { console.error(fehler) await db.$disconnect() process.exit(1) })