- 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>
105 lines
4.3 KiB
TypeScript
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);
|
|
});
|