Files
craftvia/prisma/seed.ts
T
msolarczekandClaude Opus 5 8491c7f173 Fundament: ISMS-Module entfernt; Craftvia-Rollen, Module, Navigation, i18n-Split
- ISMS-Routen, Actions, Server-/Lib-Code, Komponenten, Prisma-Modelle, Seeds,
  Importer, Skripte und ISMS-Tests entfernt (Fundament bleibt: Auth, Identity,
  MFA/WebAuthn, RBAC, Audit, Mail, Storage, Backup/DSGVO, Plattform-Admin)
- Schema auf Fundament-Modelle reduziert; TenantSettings generisch (+phone/email)
- TENANT_MODELS (db.ts, backup/topology.ts) und PII-Felder ausgedünnt
- RBAC: Rollen tenant-admin/backoffice/team-lead/technician + Craftvia-Permissions
- Modul-Katalog (customers, sites, teams, work_orders, imports, field, reports,
  emergency, documents, notifications, lotse) + Navigation aus src/lib/nav.ts
- Modul-Routen mit requireModule-Layout und Platzhalterseite
- Message-Katalog je Namespace (messages/<locale>/<namespace>.json), fs-Loader
- check-module-guards: Modul-Key aus src/server/actions/<moduleKey>/
- Provisionierung, Admin-Konsole, Einstellungen, Files-Route, Mail entkoppelt
- Seed minimal (demo/demo2, Nutzer je Rolle); Fundament-Tests auf Role/
  NotificationPreference-Fixtures umgestellt

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 11:35:44 +02:00

105 lines
4.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. KEINE Fachdaten (Kunden/Aufträge liefern die Module).
*
* 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"] },
];
for (const u of demoUsers) await ensureMember(demo.id, u, passwordHash);
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)`);
}
main()
.then(() => prisma.$disconnect())
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});