"use server"; import { join } from "node:path"; import { revalidatePath } from "next/cache"; import { redirect } from "next/navigation"; import { z } from "zod"; import { platformAuth } from "@/server/platform-auth"; import { prisma } from "@/server/db"; import { provisionTenant } from "@/server/provision"; /** Nur Plattform-Admins über die getrennte Plattform-Session (kein Mandantenkontext). */ async function requirePlatformAdmin() { const session = await platformAuth(); if (!session?.user?.id) throw new Error("Nur für Plattform-Admins."); return session; } const str = (v: FormDataEntryValue | null) => (v ? String(v).trim() : ""); const slugify = (s: string) => s.toLowerCase().normalize("NFKD").replace(/[^\p{L}\p{N}]+/gu, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "kunde"; export async function createTenant(formData: FormData) { const session = await requirePlatformAdmin(); const name = z.string().trim().min(2).parse(formData.get("name")); const slug = str(formData.get("slug")) || slugify(name); const adminEmail = z.string().trim().email().parse(formData.get("adminEmail")); const adminName = str(formData.get("adminName")) || "Administrator"; const adminPassword = z.string().min(8).parse(formData.get("adminPassword")); const tisaxLevel = str(formData.get("tisaxLevel")) === "AL3" ? "AL3" : "AL2"; const seedPolicies = formData.get("seedPolicies") === "on"; const existing = await prisma.tenant.findUnique({ where: { slug } }); if (existing) throw new Error(`Kürzel „${slug}" ist bereits vergeben.`); const tenant = await provisionTenant(prisma, { name, slug, short: str(formData.get("short")) || undefined, sector: str(formData.get("sector")) || undefined, admin: { email: adminEmail, name: adminName, password: adminPassword }, tisaxLevel, seedPoliciesDir: seedPolicies ? join(process.cwd(), "seed", "isms-vorlagenpaket-v2") : undefined, actorId: session.user.id, }); revalidatePath("/admin"); redirect(`/admin?created=${tenant.id}`); } export async function setTenantStatus(tenantId: string, status: "ACTIVE" | "SUSPENDED" | "ARCHIVED") { const session = await requirePlatformAdmin(); await prisma.tenant.update({ where: { id: tenantId }, data: { status } }); await prisma.auditLog.create({ data: { tenantId, scope: "platform", actorId: session.user.id, action: "update", entity: "tenant", entityId: tenantId, after: { status } }, }); revalidatePath("/admin"); revalidatePath(`/admin/${tenantId}`); } export async function toggleTenantModule(tenantId: string, moduleKey: string, enabled: boolean) { const session = await requirePlatformAdmin(); await prisma.tenantModule.upsert({ where: { tenantId_moduleKey: { tenantId, moduleKey } }, update: { enabled }, create: { tenantId, moduleKey, enabled }, }); await prisma.auditLog.create({ data: { tenantId, scope: "platform", actorId: session.user.id, action: "update", entity: "tenant_module", entityId: moduleKey, after: { enabled } }, }); revalidatePath(`/admin/${tenantId}`); }