feat(kunden): Kundenverwaltung, Hostzuweisung und Token-Verwaltung (#006)

Kunden und Standorte pflegen, nicht zugeordnete Hosts in einem Schritt zuweisen,
Enrollment- und Host-Tokens verwalten, Audit-Log. Dashboard, Kundenseiten und
globale Suche laufen jetzt auf der Datenbank. 45 Tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-17 21:02:44 +02:00
co-authored by Claude Opus 5
parent f591b45aac
commit f9a5ad2daa
40 changed files with 2060 additions and 415 deletions
+14 -9
View File
@@ -3,28 +3,33 @@ import { PrismaClient } from "@/generated/prisma/client";
/**
* Prisma-Client als Singleton, der erst beim ersten Zugriff verbindet.
* Das ist wichtig, weil Next.js beim Bauen Module lädt, ohne dass `DATABASE_URL` gesetzt sein muss.
* Im Entwicklungsmodus verhindert der globale Zwischenspeicher, dass jeder Hot-Reload
* einen neuen Verbindungspool öffnet.
* Verzögert, weil Next.js beim Bauen Module lädt, ohne dass `DATABASE_URL` gesetzt sein muss.
* Der Client wird im Modul festgehalten – sonst entstünde bei jedem Zugriff ein neuer
* Verbindungspool – und im Entwicklungsmodus zusätzlich global, damit Hot-Reloads ihn wiederverwenden.
*/
declare global {
var proxledgerPrisma: PrismaClient | undefined;
}
function erzeugeClient(): PrismaClient {
let client: PrismaClient | undefined;
export function getPrisma(): PrismaClient {
if (client) return client;
if (globalThis.proxledgerPrisma) {
client = globalThis.proxledgerPrisma;
return client;
}
const connectionString = process.env.DATABASE_URL;
if (!connectionString) {
throw new Error("DATABASE_URL ist nicht gesetzt. Beispielwerte stehen in .env.example.");
}
const client = new PrismaClient({ adapter: new PrismaPg({ connectionString }) });
client = new PrismaClient({ adapter: new PrismaPg({ connectionString, max: 10 }) });
if (process.env.NODE_ENV !== "production") globalThis.proxledgerPrisma = client;
return client;
}
export function getPrisma(): PrismaClient {
return globalThis.proxledgerPrisma ?? erzeugeClient();
}
export const prisma: PrismaClient = new Proxy({} as PrismaClient, {
get(_ziel, eigenschaft, empfaenger) {
return Reflect.get(getPrisma(), eigenschaft, empfaenger);