Architektur-Übersicht
Eviworx besteht aus 12 Containern mit jeweils eigener Aufgabe. Backend und Worker lassen sich mehrfach betreiben; Details unter Multi-Instance Setup.
Container-Übersicht
| Container | Technologie | Funktion | Ports |
|---|---|---|---|
| traefik | Traefik v3 | API-Gateway, TLS-Termination, Routing, Rate-Limiting | 80, 443 |
| frontend | React 19+ + NGINX | Single Page Application (SPA) | 80 |
| backend | Node.js + Express | REST API & Geschäftslogik | 3000 |
| job-worker | Node.js + BullMQ | CronJobs & geplante Aufgaben (28 Action-Types) | 3001 |
| email-worker | Node.js + BullMQ | E-Mail-Queue-Processing (IMAP/SMTP, Graph API) | 3005 |
| notification-worker | Node.js + BullMQ | Multi-Channel-Notifications (Teams Bot Framework, Webex), Attachment-Support, Quiet Hours | 3006 |
| workflow-engine | Node.js | Business-Process-Management (8 Node-Typen) | 3003 |
| av-worker | Node.js | Virus-Scan-Orchestrator (Zero-Trust) | 3007 |
| report-generator | Node.js + BullMQ | Custom Reports, CSV/PDF-Export (Read-Only DB) | 3004 |
| db | PostgreSQL 17 | Primäre Datenbank (150+ Modelle) | 5432 |
| redis | Redis 8 | Cache, Job-Queue & Distributed Locks | 6379 |
| clamav | ClamAV | Virus-Scanner-Daemon | 3310 |
Architektur-Diagramm
User (Browser)
|
| HTTPS (443) / HTTP (80)
v
+---------------------+
| TRAEFIK |
| (API Gateway v3) |
| TLS-Termination |
| Rate-Limiting |
+---------------------+
| |
/_app/* | | /api/*
/ (SPA) | |
v v
+-------------+ +-------------+
| FRONTEND | | BACKEND |
| (React SPA) | | (Express) |
| NGINX :80 | | :3000 |
+-------------+ +------+------+
|
+----------+-----------+----------+-----------+
| | | | |
v v v v v
+--------+ +--------+ +--------+ +--------+ +----------+
| Job | | Email | | Notif. | |Workfl. | | Report |
| Worker | | Worker | | Worker | | Engine | | Generator|
| :3001 | | :3005 | | :3006 | | :3003 | | :3004 |
+--------+ +--------+ +--------+ +--------+ +----------+
| | | | |
v v v v v
+--------+ +--------+ +--------+
| AV | | Redis | | DB |
| Worker | | :6379 | | :5432 |
| :3007 | +--------+ +--------+
+---+----+
|
v
+--------+
| ClamAV |
| :3310 |
+--------+
Komponenten-Details
Traefik (API-Gateway)
- Technologie: Traefik v3
- Aufgabe: API-Gateway, TLS-Termination, Routing, Rate-Limiting, CSP-Header
- Routing:
/api/*→ Backend (:3000)- Interne Schnittstellen zwischen den Diensten sind von außen nicht erreichbar (Traefik sperrt sie)
- Alles andere → Frontend (:80)
- Health-Check: Ping Entrypoint
- Datenbank-Zugriff: Keiner
- Externer Reverse Proxy: forwardedHeaders.trustedIPs in traefik.yml + TRUSTED_PROXIES in .env konfigurieren (Details)
Frontend (React 19+ + NGINX)
- Technologie: React 19.2.0, Vite, Tailwind CSS 4, shadcn/ui
- Aufgabe: Single Page Application (SPA)
- NGINX: Static-File-Serving, SPA-Routing; gehashte Build-Dateien liegen unter /_app/ (Cache-Regeln)
- Kommunikation: REST API → Backend (Port 3000)
Backend (Node.js + Express)
- Technologie: Node.js, Express, Prisma ORM, Zod Validation
- Aufgabe: Zentrale Geschäftslogik & API
- APIs:
- REST API (Public) – Für Frontend & externe Clients
- Internal API – Für Worker-Kommunikation (API-Key-geschützt)
- Datenbank-Zugriff: Vollzugriff (Benutzer:
helpdesk_user)
Job-Worker (CronJobs & Automation)
- Technologie: Node.js, BullMQ
- Aufgabe: Scheduled Jobs & Automation-Actions
- Action-Types (28, Auswahl):
- Webhook (HTTP-Aufrufe mit SSRF-Schutz)
- Assign Agent/Group
- Create/Update Ticket
- Report Schedule Check (geplante Reports)
- Asset Model Clustering
- SLA Monitor
- Attachment Cleanup, Retention Purge
- Workload Sync
- und weitere...
- Datenbank-Zugriff: Eingeschränkt (nur CronJob-Tabellen)
- Kommunikation: Internal API für alle Domain-Daten
- Multi-Instance: Redis Distributed Locks
Email-Worker
- Technologie: Node.js, BullMQ, Nodemailer
- Aufgabe: E-Mail-Queue-Processing & IMAP-Polling
- Eingehende E-Mails: IMAP-Polling / Graph API → Tickets erstellen
- Ausgehende E-Mails: SMTP / Graph API Versand via Queue
- Datenbank-Zugriff: Keiner (alles via Internal API)
Notification-Worker
- Technologie: Node.js, BullMQ
- Aufgabe: Multi-Channel-Notifications (Teams Bot Framework, Webex)
- Kanäle:
- Microsoft Teams (via Bot Framework)
- Webex (via REST API)
- E-Mail (delegiert an Email-Worker)
- Features: Attachment-Support, Quiet Hours
- Two-Tier-Routing: Backend → Notification-Worker → Channel-spezifischer Adapter
- Datenbank-Zugriff: Keiner (alles via Internal API)
Workflow-Engine
- Technologie: Node.js, BullMQ
- Aufgabe: Business-Process-Management
- Node-Typen (8):
- Manual Task – Manuelle Aufgaben für User
- Approval – Approval-Steps mit Auto-Approval
- Automated Action – API-Calls, Webhooks
- Notification – Benachrichtigungen versenden
- Parallel Gateway – Mehrere Pfade parallel
- Conditional Branch – If/Else-Verzweigung
- Data Collection – Daten sammeln (Forms)
- Timer Event – Delay oder Deadline
- Datenbank-Zugriff: Keiner (alles via Internal API)
- Multi-Instance: Redis Locks für Step-Processing
AV-Worker (Virus-Scan-Orchestrator)
- Technologie: Node.js
- Aufgabe: Koordiniert Virus-Scans (Zero-Trust)
- Zero-Trust: Der AV-Worker hat keinen Zugriff auf die Dateien.
- Ablauf:
- Backend legt File in
/app/uploads - AV-Worker übergibt ClamAV nur den Dateipfad (via TCP)
- ClamAV liest File vom eigenen Volume
- AV-Worker aktualisiert Status via Internal API
- Backend legt File in
- Quarantäne: Infizierte Dateien automatisch nach
/app/quarantine(7 Tage Retention)
Report-Generator
- Technologie: Node.js, BullMQ
- Aufgabe: Custom Reports, CSV/PDF-Export
- Datenbank-Zugriff: Read-Only (
helpdesk_readonly) - Health-Check:
GET /health/live(Port 3004)
PostgreSQL (Datenbank)
- Version: PostgreSQL 17 Alpine
- Datenmodell: 150+ Prisma-Modelle in ~50 Domains
- Benutzer:
helpdesk_user– Vollzugriff (Backend)helpdesk_jobworker– Eingeschränkt (nur CronJob-Tabellen)helpdesk_readonly– Read-Only (Report-Generator)
- Volumes:
postgres_data(Persistente Daten)
Redis (Cache & Queue)
- Version: Redis 8 Alpine
- Authentifizierung: Passwortgeschützt (--requirepass)
- Verwendung:
- Cache: RBAC-Permissions, Session-Cache, Audit-Chain-Fallback
- Job-Queue: BullMQ für alle Worker (Email, Notification, Jobs)
- Distributed Locks: Koordination zwischen Instanzen
- Persistenz: AOF (Append-Only-File) – Queue-Daten bleiben bei einem Neustart erhalten
ClamAV (Virus-Scanner)
- Version: ClamAV 1.5 (offizielles Image)
- Aufgabe: Virus-Scanning-Daemon
- Signature-Updates: 24x/Tag – stündlich (Freshclam, FRESHCLAM_CHECKS=24)
- Volume-Zugriff:
/app/uploads(read-only) - Ressourcen: 2 GB RAM Limit, 2 CPUs
Kommunikationsflüsse
1. User-Request (via Traefik)
User → Traefik (HTTPS :443) → Frontend (React SPA) OR Backend (REST API) → PostgreSQL
↓
Redis (Cache)
2. Worker-Kommunikation (Zero-Database-Access)
Worker → Backend (Internal API) → PostgreSQL
↓
Redis (Job Queue + Locks)
Wichtig: Die Worker Email, Notification, Workflow und AV haben keinen direkten Datenbank-Zugriff; sie rufen alle Daten über die Internal API ab. Der Job-Worker hat eingeschränkten DB-Zugriff (nur CronJob-Tabellen), der Report-Generator nur lesenden (helpdesk_readonly).
3. Notification-Flow (Two-Tier-Routing)
Backend → Redis (notification:enqueue Queue)
↓
Notification-Worker → fetches User/Template via Internal API
↓
+---------+---------+
v v v
Email Teams Webex
Worker Bot API
Framework
4. Virus-Scan-Flow (Zero-Trust)
Backend → Uploads file to /app/uploads → Creates DB entry (status: PENDING)
↓
AV-Worker (polling every 10s) → Fetches pending files via Internal API
↓
AV-Worker sends ONLY file path (NOT content!) to ClamAV via TCP
↓
ClamAV reads file from shared volume /app/uploads (read-only)
↓
ClamAV returns: CLEAN / INFECTED
↓
AV-Worker updates status via Internal API
↓
If INFECTED: Backend moves file to /app/quarantine
Security-Architektur
Principle of Least Privilege
| Container | Datenbank-Zugriff | Begründung |
|---|---|---|
| Traefik | None | Kein DB-Zugriff; sperrt interne Schnittstellen nach außen |
| Backend | Full (helpdesk_user) |
Zentrale Geschäftslogik benötigt vollen Zugriff |
| Job-Worker | Restricted (helpdesk_jobworker) |
Nur CronJob-Tabellen (CronJob, JobExecution, WorkerInstance) |
| Report-Generator | Read-Only (helpdesk_readonly) |
Nur lesender Zugriff für Reports/Analytics |
| Email-Worker | None | Daten via Internal API – kein DB-Zugriff nötig |
| Notification-Worker | None | Daten via Internal API |
| Workflow-Engine | None | Daten via Internal API |
| AV-Worker | None | Daten via Internal API – Zero-Trust |
Network-Isolation
- Alle Container im gleichen Docker-Netzwerk (isolated)
- Nur Traefik exposed (Ports 80/443)
- Backend/Worker/Frontend nicht direkt erreichbar von außen
- Inter-Container-Kommunikation via DNS (z.B.
http://backend:3000)
Multi-Instance Setup (High Availability)
Eviworx ist für Active-Active-Deployments konzipiert. Mehrere Instanzen können parallel laufen.
Distributed Locks (Redis-basiert)
- Job-Worker: Lock pro CronJob (nur eine Instanz führt Job aus)
- Workflow-Engine: Lock pro Workflow-Step
- Implementierung: Redlock-Algorithm
Worker-Heartbeat-Monitoring
- Jede Worker-Instanz trägt sich per Heartbeat in die Redis-Health-Registry ein
- Heartbeat alle 15 Sekunden, der Eintrag verfällt nach 30 Sekunden
- Instanzen ohne Heartbeat gelten als offline; ihre Ausführungs-Historie bleibt sichtbar
- Stuck Jobs werden neu verteilt (Recovery-Logik)
Load-Balancing
- Traefik: Zentraler Load-Balancer & API-Gateway
- Backend: Mehrere Instanzen hinter Traefik
- Worker: Automatisch via BullMQ (Pull-based)
Datenfluss-Beispiele
Beispiel 1: Ticket erstellen
1. User fills out ticket form (Frontend)
2. Frontend → POST /api/tickets (via Traefik → Backend)
3. Backend:
- Validates input (Zod schema)
- Checks permissions (RBAC)
- Creates ticket in PostgreSQL
- Creates audit-chain event
- Enqueues notification (Redis)
4. Notification-Worker (async):
- Fetches User/Template via Internal API
- Sends email/Teams/Webex
5. Frontend receives response (201 Created)
Beispiel 2: Scheduled Job (CronJob)
1. CronJob defined: "Every day at 9am: Generate Report"
2. Job-Worker (Scheduler):
- Checks all jobs (via DB)
- Finds job with cron expression "0 9 * * *"
- Acquired Distributed Lock (Redis)
3. Job-Worker runs action:
- Action: GenerateReport
- Fetches report data via Internal API
- Generates PDF
- Stores in /app/uploads
- Enqueues email notification
4. Email-Worker:
- Sends report by email
5. Job-Worker updated execution status (SUCCESS)
Beispiel 3: Workflow-Ausführung
1. User starts workflow (e.g. "Onboarding Workflow")
2. Backend:
- Creates workflow instance
- Enqueues start event
3. Workflow-Engine:
- Fetches Instance via Internal API
- Processes step by step
- Step 1: Manual Task → assignee notification
- Step 2: Approval → waits for user input
- Step 3: Automated Action → webhook call
- Step 4: Notification → Teams message
4. On each step:
- Acquired Lock (Redis)
- Updates step status via Internal API
- Release Lock
5. Workflow completed → status: COMPLETED
Volumes & Persistenz
| Volume | Verwendet von | Zweck |
|---|---|---|
postgres_data |
db | Persistente Datenbankdaten |
redis_data |
redis | AOF-Persistenz für Job-Queue |
uploads |
backend, clamav | Hochgeladene Dateien (Attachments) |
quarantine |
backend | Infizierte Dateien (7 Tage Aufbewahrung) |
clamav_data |
clamav | Virus-Signaturen |
sourcemaps |
frontend (rw), backend (ro) | JS-Sourcemaps für Error-Tracking, die fünf neuesten Releases (Frontend schreibt, Backend liest) |
Ressourcen-Anforderungen
Minimum (Development)
- CPU: 4 Kerne
- RAM: 8 GB
- Speicher: 20 GB
Empfohlen (Production)
- CPU: 8 Kerne
- RAM: 16 GB
- Speicher: 100 GB (abhängig von Attachments)
Pro-Container-Limits (Konfigurierbar)
| Container | CPU Limit | Memory Limit |
|---|---|---|
| traefik | 1.0 | 256 MB |
| frontend | 0.5 | 256 MB |
| backend | 2.0 | 2 GB |
| db | 2.0 | 2 GB |
| redis | 1.0 | 1 GB |
| job-worker | 1.0 | 1 GB |
| email-worker | 1.0 | 512 MB |
| notification-worker | 1.0 | 512 MB |
| workflow-engine | 1.0 | 512 MB |
| av-worker | 1.0 | 1.5 GB |
| report-generator | 1.0 | 1.5 GB |
| clamav | 2.0 | 2 GB |
Monitoring & Observability
Health-Checks
- Traefik: Ping Entrypoint (integrierter Health-Check)
- Backend:
GET /api/health/live(Liveness-Probe; die Bereitschaft prüft /api/health/ready) - Workflow-Engine:
GET /health/live(Liveness-Probe)GET /health/ready(Readiness-Probe, prüft Redis)
- Report-Generator:
GET /health/live(Port 3004) - Job-Worker: Health-Endpoint (Port 3001)
- Email-Worker: Health-Endpoint (Port 3005)
- Notification-Worker: Health-Endpoint (Port 3006)
- AV-Worker: Health-Endpoint (Port 3007)
System-Status (Admin-UI + Health-API)
Der Live-Zustand des gesamten Stacks ist im Admin-Bereich unter Admin-Center → System → System-Status sichtbar (Route /admin/system-status). Der Tab zeigt jeden Dienst als aufklappbare Zeile mit seinen Instanzen — je Instanz Status, Version, Laufzeit, Zeitpunkt des letzten Heartbeats, Speicherverbrauch, Port und die Einzelprüfungen (Datenbank, Redis) samt Antwortzeit. Suche und Status-Filter grenzen die Liste ein. Beeinträchtigte Einheiten — etwa ein Postfach ohne Abrufversuch oder ein Cron-Job im Fehlerzustand — stehen in einer eigenen Tabelle, Infrastruktur und Krypto-Konfiguration (z.B. FIPS-Modus) in einer Detailkarte. Datengrundlage ist die Health-API des Backends:
| Endpoint | Auth | Zweck |
|---|---|---|
GET /api/health | Keine | Basis-Check (200 healthy / 503 unhealthy) — für Monitoring-Systeme |
GET /api/health/live | Keine | Liveness-Probe (Prozess läuft) |
GET /api/health/ready | Keine | Readiness-Probe (DB + Redis erreichbar) |
GET /api/health/services | settings.editGeneral (nur angemeldete Benutzer) | Status je Dienst und Instanz (Version, Laufzeit, Speicherverbrauch, Port, Einzelprüfungen), beeinträchtigte Einheiten sowie Infrastruktur- und Krypto-Konfiguration; Datenquelle des Tabs System-Status. |
Hinweis: Die Service-Health stammt aus einer Redis-Registry: jede Instanz trägt sich alle 15 Sekunden mit einem Heartbeat ein, der Eintrag verfällt nach 30 Sekunden. Ein Dienst ohne Heartbeat erscheint als offline. /api/health/services erfordert settings.editGeneral und ist angemeldeten Benutzern vorbehalten — über einen API-Schlüssel sind Infrastruktur-Topologie, Ports, Instanz-IDs und Krypto-Konfiguration nicht abrufbar.
Structured Logging (JSON)
Alle Eviworx-eigenen Container (Backend, Job-Worker, Email-Worker, Notification-Worker, Workflow-Engine, AV-Worker, Report-Generator) loggen im strukturierten JSON-Format via Pino. Externe Container (Redis, PostgreSQL, ClamAV) verwenden ihr natives Log-Format.
- Log-Library: Pino v10 (alle eigenen Container)
- Felder pro Log-Eintrag:
level,time,service,msg– Standard-FeldertraceId,spanId– Distributed Tracing (OpenTelemetry-kompatibel)correlationId,requestId– Request-Korrelation über Container-GrenzensourceService– Zeigt welcher Service den Request ausgelöst hat (z.B. av-worker, traefik)
- Integration: Direkt kompatibel mit Elasticsearch/ELK, Loki, Datadog, Splunk und anderen JSON-Log-Aggregatoren
- Traefik Access-Logs: Ebenfalls im JSON-Format mit X-Request-ID und User-Agent
- Rotation: max. 10 MB pro Datei, 3 Dateien (ClamAV, AV-Worker)
- Zugriff:
docker compose logs -f [service-name]
// Beispiel: Backend HTTP-Request Log{
"level": 30,
"time": 1773740483502,
"service": "backend",
"traceId": "c5d1c321ad743f0abd05d55967adf67d",
"spanId": "3fcb07029f32d740",
"correlationId": "c5d1c321ad743f0abd05d55967adf67d",
"requestId": "b36f4176-0e5b-42d7-8e17-772c0929e4d8",
"sourceService": "traefik",
"method": "GET",
"url": "/api/health/live",
"status": 200,
"durationMs": 1,
"msg": "HTTP request"
}
- ✓ Principle of Least Privilege (DB-Zugriff)
- ✓ Zero-Trust-Virenscan (AV-Worker ohne Dateizugriff)
- ✓ Network-Isolation (Docker-Netzwerk)
- ✓ SHA-256-verkettetes Audit-Log (Manipulationen werden erkennbar)
- ✓ Automatisches PII-Scrubbing in Audit-Logs
- ✓ SSRF-Protection (IP-Range + DNS-Blocking)
- ✓ Gehärtete Container (schreibgeschützte Dateisysteme, reduzierte Linux-Capabilities)
- ✓ FIPS 140-2 kompatible Algorithmen (PBKDF2-SHA512, AES-256-GCM)
- ✓ MFA/TOTP Authentifizierung
- ✓ Redis Passwort-Authentifizierung
- ✓ Traefik API-Gateway (Rate-Limiting, CSP)
Details zu jedem Container
API-Module mit Endpoints & Beispielen
Zero-Trust, Audit-Chain, RBAC
Multi-Instance Setup