⚠️ Veraltet (Stand 2026-04-30)
Diese Dokumentation beschreibt das alte System vor dem Process-Engine-Refactor (Phase 4.7). Die zugrundeliegenden Tabellen (cascade_*, concept_instances/bausteine, crisis_scenarios/events/tasks, workflow_*) wurden gedroppt. Die hier dokumentierten Routen und Models existieren so nicht mehr.
Aktuelle Architektur: Process-Engine API — eine Engine, mehrere Apps (flow.*, concept.*, crisis.*).
Diese Datei bleibt als historische Referenz erhalten.
Krisenmanagement — Technische Architektur
Plugin-Einordnung
Das Krisenmanagement ist als reines Plugin im Prilog-Modulsystem implementiert. Es folgt der 3-Stufen-Aktivierung (Registry → Installation → Aktivierung) und nutzt die bestehende Infrastruktur.
prilog-backend-api/src/modules/crisis-management/
├── prilog-module.json # Manifest (featureFlag: crisis-management)
├── index.ts # register/unregister/cleanup + Fastify-Plugin
├── types.ts # TypeScript-Typen + DTOs
├── crisis.router.ts # Szenarien + Workflow Endpoints
├── n8n-callback.router.ts # n8n Callback-Endpoints
├── n8n-workflow.service.ts # n8n-Integration (ersetzt WorkflowService)
├── n8n-client.ts # HTTP-Client fuer n8n API
├── matrix-crisis.service.ts # Matrix-Raum-Erstellung + Nachrichten
├── report-pdf.service.ts # PDF-Generierung (pdfkit)
├── school-trip.router.ts # Schulreise-Endpoints
├── child-protection.router.ts # Kinderschutz-Endpoints
├── integrations.router.ts # Export-Endpoints
└── integrations/
├── index.ts # Adapter-Initialisierung
├── types.ts # ExportAdapter Interface
├── adapter-registry.ts # Zentrale Registry
├── pdf-a.adapter.ts # PDF/A-Langzeitarchivierung
├── youth-office.adapter.ts # Jugendamt §8a SGB VIII
├── police.adapter.ts # Polizei-Berichte
└── data-protection.adapter.ts # DSGVO Art. 33Datenmodell
Entity-Relationship
CrisisScenario (1) ──→ (*) CrisisEvent
├──→ (*) CrisisTask
├──→ (1) CrisisReport
└──→ (*) CrisisAuditEntry
SchoolTripEvent (standalone, tenantId-scoped)
ChildProtectionCase (standalone, tenantId-scoped)Prisma-Models
CrisisScenario
Versioniertes Konfigurationsobjekt. Jede Bearbeitung erstellt eine neue Version; die alte wird deaktiviert (isActive: false). Ohne Freigabe (approvedBy) nicht im Workflow-Tab sichtbar.
| Feld | Typ | Beschreibung |
|---|---|---|
| id | String (cuid) | Primaerschluessel |
| tenantId | String | Multi-Tenancy-Scope |
| name | String | Anzeigename |
| nameSlug | String | URL-freundlicher Name |
| severity | String | CRITICAL / HIGH / MEDIUM |
| type | String | GENERAL / CHILD_PROTECTION / SCHOOL_TRIP |
| targetResponseMs | Int | Soll-Reaktionszeit in ms |
| version | Int | Versionsnummer (auto-increment) |
| isActive | Boolean | Nur die neueste Version ist aktiv |
| approvedBy | String? | User-ID des Freigebenden |
| checklistItems | Json | ChecklistItem[] |
| externalContacts | Json | ExternalContact[] |
| escalationRules | Json | EscalationRule[] |
Unique Constraint: (tenantId, nameSlug, version)
CrisisEvent
Repraesentiert eine aktive oder abgeschlossene Krise.
| Feld | Typ | Beschreibung |
|---|---|---|
| status | String | ACTIVE → RESOLVED / FALSE_ALARM / ARCHIVED |
| matrixRoomId | String? | ID des automatisch erstellten Matrix-Raums |
| activationNote | String? | Freitext bei Aktivierung |
CrisisTask
Einzelne Checklisten-Aufgabe, erstellt bei Krisenaktivierung.
| Feld | Typ | Beschreibung |
|---|---|---|
| status | String | OPEN → IN_PROGRESS → DONE / ESCALATED |
| escalationLevel | Int | Aktuelle Eskalationsstufe (0 = keine) |
| checklistItemId | String | Verknuepfung zum Template |
CrisisAuditEntry
Lueckenlose Protokollierung aller Aktionen.
| Feld | Typ | Beschreibung |
|---|---|---|
| action | String | ACTIVATE / DEACTIVATE / FALSE_ALARM / TASK_DONE / ESCALATE / EXPORT / CONFIG_CHANGE |
| actorId | String | User-ID oder "system" |
| metadata | Json? | Zusaetzliche Kontextdaten |
JSON-Feld-Typen
interface ChecklistItem {
id: string;
order: number;
title: string;
description?: string;
assignedRole: string;
escalateAfterMs: number;
isMandatory: boolean;
}
interface ExternalContact {
label: string;
phone: string;
address?: string;
notes?: string;
}
interface EscalationRule {
level: number;
afterMs: number;
escalateTo: string[];
message: string;
}Services
N8nWorkflowService
Ersetzt den frueheren WorkflowService. Orchestriert Prisma-Transaktionen und delegiert Workflow-Logik (Aufgabenerstellung, Eskalation, Benachrichtigungen) an n8n.
Methoden:
activate(dto, actorId, tenantId)— Idempotenzpruefung → Event erstellen → n8n-Workflow triggerndeactivate(eventId, actorId, tenantId)— Status RESOLVED → Nachbericht → n8n benachrichtigenmarkFalseAlarm(eventId, actorId, tenantId, note)— Status FALSE_ALARM → Matrix-EntwarnungupdateTaskStatus(eventId, taskId, status, actorId, tenantId)— Status setzen + Audit + Matrix-UpdategenerateReport(eventId, actorId, tenantId)— ReportContent aus Event + Tasks + Audit zusammenbauen
n8n-Integration: Bei Aktivierung wird der verknuepfte n8n-Workflow via n8n-client.ts getriggert. Der Workflow uebernimmt Aufgabenerstellung, Eskalationsueberwachung und Benachrichtigungen ueber Callback-Endpoints.
Idempotenz: assertNoDuplicate() prueft ob fuer das gleiche Szenario bereits ein ACTIVE Event existiert. Gibt 409 zurueck.
MatrixCrisisService
Erstellt Krisenraeume und postet Nachrichten via synapseAdminFetch().
Methoden:
createCrisisRoom(tenantId, eventId, scenario, activatedAt)— Raum erstellen, Checkliste anpinnen, Kontakte postenpostTaskUpdate(tenantId, roomId, task, actorName)— "Erledigt: [Aufgabe]"postEscalationNotice(tenantId, roomId, task, level)— "Eskalation Stufe X"postFalseAlarmNotice(tenantId, roomId, note)— "ENTWARNUNG"postDeactivationNotice(tenantId, roomId, actorId)— "Krise beendet"
Matrix-Zugang: Nutzt ServerOrder.synapseAdminToken mit Auto-Refresh bei 401. Die Synapse-URL wird aus order.subdomain konstruiert: http://prilog-{subdomain}:8008.
n8n-Integration
Die Eskalationslogik wurde vom Backend in n8n-Workflows ausgelagert. Die frueheren Dateien escalation.service.ts und workflow.service.ts sind entfernt.
Komponenten:
- n8n-client.ts — HTTP-Client fuer die n8n REST API. Triggert Workflows und uebergibt Event-Kontext (eventId, tenantId, scenarioId).
- n8n-callback.router.ts — Callback-Endpoints die vom n8n-Workflow aufgerufen werden (Aufgaben erstellen, Matrix-Raum erstellen, Eskalation, Benachrichtigung, Report-Generierung).
- N8nWorkflowService — Ersetzt
WorkflowService. Erstellt Events in der DB und delegiert die Workflow-Ausfuehrung an n8n.
Ablauf:
- Bei Krisenaktivierung wird der n8n-Workflow getriggert
- n8n ruft Callback-Endpoints auf um Aufgaben zu erstellen, Matrix-Raeume zu oeffnen, etc.
- n8n ueberwacht Zeitlimits und ruft den Eskalations-Callback bei Ueberschreitung
- Bei Krisenbeendigung wird n8n ueber einen Webhook informiert
Szenarien verknuepfen: Jedes CrisisScenario kann ein n8nWorkflowId enthalten, das auf den zugehoerigen n8n-Workflow verweist.
Adapter-Architektur
ExportAdapter Interface
interface ExportAdapter {
id: string;
type: AdapterType;
name: string;
description: string;
validate(config: AdapterConfig): { valid: boolean; errors: string[] };
exportCrisisReport(content: unknown, context: ExportContext): Promise<ExportResult>;
exportChildProtectionCase?(data: unknown, context: ExportContext): Promise<ExportResult>;
}Registry-Pattern
Adapter registrieren sich beim Modulstart via registerAdapter(). Die initializeIntegrations() Funktion wird in onStartup() aufgerufen.
Adapter-Kaskade
Die Behoerden-Adapter (Jugendamt, Polizei, Datenschutz) nutzen intern den PDF/A-Adapter fuer die Dokumenten-Generierung und fuegen strukturierte JSON-Metadaten hinzu. So entsteht ein duales Format: lesbares PDF + maschinenlesbares JSON.
police.adapter → pdfAAdapter.exportCrisisReport() + strukturiertes JSON
↓
ExportResult { data: Buffer(PDF), metadata: { structuredReport: {...} } }Erweiterbarkeit
Neue Adapter koennen hinzugefuegt werden durch:
- Datei in
integrations/erstellen dieExportAdapterimplementiert - In
integrations/index.tsimportieren undregisterAdapter()aufrufen - Keine Aenderungen an Routern oder UI noetig — der Adapter erscheint automatisch
Sicherheit
Zugriffskontrolle
| Bereich | Erlaubte Rollen |
|---|---|
| Workflow (Aktivierung) | SCHOOL_PRINCIPAL, SCHOOL_ADMIN, SCHOOL_SECRETARY, CUSTODIAN |
| Workflow (Deaktivierung) | SCHOOL_PRINCIPAL, SCHOOL_ADMIN |
| Szenario-Manager | SCHOOL_PRINCIPAL, SCHOOL_ADMIN |
| Kinderschutz | SCHOOL_PRINCIPAL, SCHOOL_ADMIN |
| Audit-Log | SCHOOL_PRINCIPAL, SCHOOL_ADMIN |
Tenant-Isolation
Alle Queries sind auf tenantId gefiltert (aus JWT). Cross-Tenant-Zugriff ist auf Datenbankebene ausgeschlossen.
Kinderschutz-Besonderheiten
- Jeder Zugriff auf einen Fall wird im
accessLogprotokolliert - Meldungen ans Jugendamt erfordern Vier-Augen-Prinzip (2 verschiedene User)
- Aufbewahrungsfrist: 10 Jahre nach Schliessung (
retainUntil) - ChildProtectionCase wird bei Modul-Deaktivierung nicht automatisch geloescht
Portal-UI
Komponenten-Struktur
prilog-portal/src/components/spaces/crisis/
├── CrisisModule.tsx # 5-Tab-Controller
├── crisis-api.ts # API-Client (alle Endpoints)
├── store.ts # Zustand Store
├── workflow/ # Workflow-Tab
├── szenarien/ # Szenario-Manager
├── school-trips/ # Schulreise-Tab
├── child-protection/ # Kinderschutz-Tab
└── integrations/ # Integrationen-TabState Management
Zustand Store (store.ts) mit:
- Szenarien-Laden (mit
approved-Filter) - Active Events Polling (10s)
- Task-Aktualisierung
- Aktivierung/Deaktivierung
- Archiv-Laden
Polling
- Aktive Events: 10s Intervall im WorkflowTab
- Tasks: 10s Intervall im CrisisDetail
- Beide Polls stoppen beim Unmount via
useEffectCleanup