Files
craftvia/prisma/seed.ts
T

138 lines
6.3 KiB
TypeScript

import "dotenv/config";
import { PrismaClient } from "@prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import { hashPassword } from "../src/server/password";
import { PERMISSIONS, type RoleKey } from "../src/server/rbac";
import { provisionTenant } from "../src/server/provision";
/**
* Craftvia-Demo-Seed (Fundament): Plattform-Admin + Mandant `demo` mit je einem Nutzer
* pro Standardrolle + zweiter Mandant `demo2` (Isolations-/Tenant-Switch-Tests) +
* Multi-Membership-Fixture. Fachliche Demo-Daten (Teams, Kunden, Objekte, Aufträge, Berichte,
* Notdienst) kommen aus scripts/lib/demo-seed.ts — nur mit SEED_DEMO=true oder außerhalb von
* Production (SEED_DEMO=false schaltet sie ab).
*
* Idempotent (upserts) — beliebig oft ausführbar. Passwort: SEED_PASSWORD oder Default.
*/
const prisma = new PrismaClient({
adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
});
const DEMO_PASSWORD = process.env.SEED_PASSWORD || "Demo1234!";
async function ensureMember(tenantId: string, u: { email: string; name: string; roles: RoleKey[] }, passwordHash: string) {
// Globale Identity (Anmeldung) + Mitgliedschaft (User) im Mandanten.
const identity = await prisma.identity.upsert({
where: { email: u.email },
update: {},
create: { email: u.email, passwordHash },
});
const user = await prisma.user.upsert({
where: { tenantId_email: { tenantId, email: u.email } },
update: { identityId: identity.id, name: u.name },
create: { tenantId, identityId: identity.id, email: u.email, name: u.name },
});
const roles = await prisma.role.findMany({ where: { tenantId, key: { in: u.roles } } });
for (const r of roles) {
await prisma.userRole.upsert({
where: { userId_roleId: { userId: user.id, roleId: r.id } },
update: {},
create: { userId: user.id, roleId: r.id },
});
}
return user;
}
async function main() {
const passwordHash = await hashPassword(DEMO_PASSWORD);
// 1. Mandant „demo" (provisioniert Permissions, Rollen, Einstellungen, Module, Admin)
const demo = await provisionTenant(prisma, {
name: "Musterbau Haustechnik GmbH",
slug: "demo",
short: "Musterbau",
sector: "Sanitär, Heizung, Klima",
admin: { email: "admin@demo.example", name: "Anna Admin", password: DEMO_PASSWORD },
});
await prisma.tenantSettings.update({
where: { tenantId: demo.id },
data: {
address: "Hafenstraße 12, 20457 Hamburg",
phone: "+49 40 123456-0",
email: "info@musterbau.example",
},
});
const demoUsers: { email: string; name: string; roles: RoleKey[] }[] = [
{ email: "admin@demo.example", name: "Anna Admin", roles: ["tenant-admin"] },
{ email: "backoffice@demo.example", name: "Bernd Backoffice", roles: ["backoffice"] },
{ email: "teamleiter@demo.example", name: "Tina Teamleiter", roles: ["team-lead"] },
{ email: "monteur@demo.example", name: "Max Monteur", roles: ["technician"] },
// L10a Demo-Teams: Team Nord (Tina + Max + Nora), Team Süd (Sven + Paul)
{ email: "teamleiter2@demo.example", name: "Sven Südfeld", roles: ["team-lead"] },
{ email: "monteur2@demo.example", name: "Nora Nordmann", roles: ["technician"] },
{ email: "monteur3@demo.example", name: "Paul Petersen", roles: ["technician"] },
];
const members: Record<string, string> = {};
for (const u of demoUsers) members[u.email] = (await ensureMember(demo.id, u, passwordHash)).id;
console.log(`✔ Mandant "demo" (${PERMISSIONS.length} Permissions) mit ${demoUsers.length} Nutzern (Passwort: ${DEMO_PASSWORD})`);
// 2. Zweiter Mandant „demo2" — Grundlage für Isolations- und Tenant-Switch-Tests.
const demo2 = await provisionTenant(prisma, {
name: "Nordlicht Elektro GmbH",
slug: "demo2",
short: "Nordlicht",
sector: "Elektro",
admin: { email: "admin2@demo.example", name: "Zoe Zweitadmin", password: DEMO_PASSWORD },
});
console.log(`✔ Mandant "demo2" mit Admin admin2@demo.example`);
// 3. Multi-Membership-Fixture: eine Identity in ZWEI Mandanten mit je anderer Rolle.
const multi = { email: "multi@demo.example", name: "Mika Multi" };
await ensureMember(demo.id, { ...multi, roles: ["technician"] }, passwordHash);
await ensureMember(demo2.id, { ...multi, roles: ["tenant-admin"] }, passwordHash);
console.log(`✔ Multi-Membership-Fixture multi@demo.example (demo: Monteur, demo2: Mandantenadministrator)`);
// 4. Plattform-Admin (getrennter Store, Login unter /platform/login).
await prisma.platformAdmin.upsert({
where: { email: "platform@demo.example" },
update: { name: "Paula Plattform", status: "ACTIVE" },
create: { email: "platform@demo.example", name: "Paula Plattform", passwordHash, status: "ACTIVE" },
});
console.log(`✔ Plattform-Admin platform@demo.example (/platform/login)`);
// 5. L10a: fachliche Demo-Daten (Teams, Kunden, Objekte, 20 Aufträge, Berichte, Notdienst).
// Nur mit SEED_DEMO=true oder außerhalb von Production; SEED_DEMO=false schaltet sie ab.
const demoData = process.env.SEED_DEMO === "true" || (process.env.NODE_ENV !== "production" && process.env.SEED_DEMO !== "false");
if (demoData) {
const { seedDemoTenant, seedDemo2Tenant, closeDemoSeed } = await import("../scripts/lib/demo-seed");
const res = await seedDemoTenant(demo.id, {
admin: members["admin@demo.example"],
backoffice: members["backoffice@demo.example"],
lead: members["teamleiter@demo.example"],
lead2: members["teamleiter2@demo.example"],
tech: members["monteur@demo.example"],
tech2: members["monteur2@demo.example"],
tech3: members["monteur3@demo.example"],
});
const admin2 = await prisma.user.findUniqueOrThrow({ where: { tenantId_email: { tenantId: demo2.id, email: "admin2@demo.example" } } });
await seedDemo2Tenant(demo2.id, admin2.id);
await closeDemoSeed();
console.log(`✔ Demo-Fachdaten: ${res.orders} Demo-Aufträge in "demo" (${res.created} neu angelegt), 1 Auftrag in "demo2"`);
} else {
console.log("• Demo-Fachdaten übersprungen (SEED_DEMO=false bzw. Production ohne SEED_DEMO=true)");
}
}
main()
.then(async () => {
await prisma.$disconnect();
// Services may leave queue/redis handles open (events, image jobs) — end the seed explicitly.
process.exit(0);
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});