Sachbearbeiter-Cockpit aufsetzen
Ziel dieser Seite: ein lauffähiges Sachbearbeiter-Cockpit mit Anmeldung, App-Übersicht und einer (noch leeren) App.
Voraussetzungen
- ein React-19-Projekt mit den installierten Peer-Dependencies
- eine Keycloak-Instanz mit Realm und Client — und ein Benutzerkonto darin, mit dem du dich anmelden kannst
Der Anmelde-Schritt steckt fest in BaseCockpit: solange kein User angemeldet
ist, rendert das Sachbearbeiter-Cockpit ausschließlich den Login-Bildschirm — es gibt keinen
Schalter, der das überspringt. Der Mock-Modus ersetzt
nur die Datenabrufe, nicht die Anmeldung.
Vier Schritte:
authConfig.jsonbereitstellen- API-Proxy einrichten
- Apps definieren und
BaseCockpitrendern - Keycloak-Rollen setzen (nur wenn du die Rechteprüfung willst)
1. authConfig.json
Die Anmelde-Einstellungen kommen aus einer JSON-Datei, die das Sachbearbeiter-Cockpit beim Start lädt — sie steckt also nicht im Build und kann pro Umgebung anders sein.
Sie gehört im Projekt nach:
conf/authConfig.json
conf/ wird über publicDir: 'conf' zum öffentlichen Verzeichnis — deshalb
landet die Datei unter /authConfig.json, wo das Sachbearbeiter-Cockpit sie erwartet. Die
Einstellung steht im vite.config.ts-Block in Schritt 2.
{
"authority": "https://keycloak.example.com/realms/actanova",
"client_id": "cockpit",
"checkPermission": true
}
authorityundclient_idsind Pflicht. Fehlt eines, zeigt das Sachbearbeiter-Cockpit statt der App einen Fehlerbildschirm.checkPermission: trueblendet Apps aus, für die dem angemeldeten User die Rolle fehlt (siehe Schritt 4). Ohne das Feld sind alle Apps sichtbar.- Alle weiteren Felder (
redirect_uri,post_logout_redirect_uri,scope, …) gehen unverändert an die OIDC-Anmeldung. Ohneredirect_uriwird die Startadresse der App verwendet.
Liegt die Datei woanders, gib den Pfad mit:
<BaseCockpit configUrl="/config/auth.json" … />.
2. API-Proxy
Alle Services rufen relativ zur Base-URL /api/cockpit/an auf, ergänzt um
den Tenant-Pfad, z. B.:
GET /api/cockpit/an/Tenants/Items/<tenant>/Files/Items?$top=100
Diese Base-URL ist in der Library fest verdrahtet und existiert bei ActaNova
nicht. Der Proxy hat deshalb zwei Aufgaben: weiterleiten und das Präfix auf
den echten API-Pfad /api/main/v1 umschreiben.
Aufruf der App /api/cockpit/an/Tenants/Items/<tenant>/Files/Items?$top=100
^^^^^^^^^^^^^^^ dieses Präfix ersetzt der Proxy …
ActaNova https://actanova.example.com/api/main/v1/Tenants/Items/<tenant>/Files/Items?$top=100
^^^^^^^^^^^^ … durch diesen Pfad
Der Rest des Pfades bleibt unverändert, ebenso der Query-String ($top,
$filter, …) — die Services bauen ihre OData-Parameter selbst.
ActaNova braucht außerdem den Authorization-Header mit dem Bearer-Token, den
die Library bei jedem Aufruf setzt — der Proxy muss ihn also weiterreichen.
Lokale Entwicklung
Hier übernimmt das der Dev-Server. Bei Vite über die vite.config.ts — dieselbe
Datei, die mit publicDir auch die authConfig.json aus Schritt 1 ausliefert:
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
// conf/ statt public/ als öffentliches Verzeichnis:
// conf/authConfig.json wird dadurch als /authConfig.json ausgeliefert.
publicDir: 'conf',
plugins: [react(), tailwindcss()],
server: {
proxy: {
'/api/cockpit/an': {
// Das Ziel enthält den ActaNova-Pfad bereits …
target: 'https://actanova.example.com/api/main/v1',
// … deshalb wird das Präfix der App hier entfernt:
// /api/cockpit/an/Tenants/… → /api/main/v1/Tenants/…
rewrite: (path) => path.replace(/^\/api\/cockpit\/an/, ''),
changeOrigin: true,
// Nur nötig, wenn das Zielsystem ein selbst signiertes Zertifikat hat.
secure: false,
},
},
},
});
Den Authorization-Header schickt der Dev-Server-Proxy von selbst mit; du musst
ihn hier nicht setzen.
Betrieb
Die Weiterleitung muss der Webserver machen, der die gebaute App ausliefert.
Im Demo-Projekt ist das ein nginx, der /api/cockpit/an/ auf
/api/main/v1/ umschreibt und zugleich die authConfig.json ausliefert:
# /api/cockpit/an/<x> -> https://actanova.example.com/api/main/v1/<x>
# nginx ersetzt den gematchten Location-Prefix (/api/cockpit/an/) durch den
# URI-Teil aus proxy_pass (/api/main/v1/). Der Query-String bleibt erhalten.
location /api/cockpit/an/ {
proxy_pass https://actanova.example.com/api/main/v1/;
proxy_http_version 1.1;
proxy_ssl_server_name on; # SNI senden (nötig bei TLS-vHosts)
proxy_set_header Host $proxy_host;
}
# authConfig.json nie cachen (das Sachbearbeiter-Cockpit lädt sie mit `cache: no-store`)
location = /authConfig.json {
add_header Cache-Control "no-store";
try_files $uri =404;
}
Der abschließende Slash in proxy_pass ist entscheidend: nur mit URI-Teil
ersetzt nginx das Präfix. Ohne ihn (proxy_pass https://host;) wird der
Original-Pfad /api/cockpit/an/… unverändert durchgereicht und ActaNova
antwortet mit 404. Den Authorization-Header reicht nginx standardmäßig
weiter — überschreibe ihn hier nicht.
Oben steht sie fest im Block, damit das Muster erkennbar bleibt. Im Betrieb
willst du sie zur Laufzeit setzen, sonst brauchst du pro Umgebung ein eigenes
Image. Das Demo-Projekt liefert dafür einen fertigen Pfad: eine
NGINX-Vorlage mit ${ACTANOVA_BASE_URL}, die beim Containerstart gerendert
wird, plus die als Volume gemountete authConfig.json. Alles dazu — Dockerfile,
envsubst, OpenShift — steht unter Deployment.
Zum Ausprobieren brauchst du den Proxy nicht — im
Mock-Modus liefert die Library Beispieldaten.
Keycloak und die authConfig.json aus Schritt 1 brauchst du trotzdem.
3. Apps definieren und BaseCockpit rendern
// src/main.tsx
import { createRoot } from 'react-dom/client';
import {
BaseCockpit,
configureCockpit,
type CockpitApp,
} from '@gentics/cockpit/core';
import '@gentics/cockpit/style.css';
import { AktenApp } from './apps/akten/AktenApp';
configureCockpit({ mock: import.meta.env.VITE_COCKPIT_MOCK === 'true' });
const apps: CockpitApp[] = [
{
id: 'akten',
title: 'Akten',
description: 'Akten suchen, ansehen und zuweisen',
tenant: ['MeinMandant'],
icon: '📁',
component: AktenApp,
},
];
createRoot(document.getElementById('root')!).render(
<BaseCockpit apps={apps} branding={{ name: 'Mein Sachbearbeiter-Cockpit' }} />,
);
CockpitApp
| Feld | Typ | Bedeutung |
|---|---|---|
id | string | Eindeutige Kennung. Bestimmt den URL-Abschnitt (/<id>/…) und den Rollennamen (cockpit-<id>). |
title | string | Titel auf der Kachel |
description | string | Text auf der Kachel |
tenant | string[] | ActaNova-Mandanten dieser App. Genau einer → automatisch aktiv; mehrere → der User wählt. Leer → Fehler. Siehe Mandanten. |
icon | ReactNode | Optional: Emoji-String oder Icon-Element |
component | AppComponent | Die zu rendernde App-UI. Bekommt { app } als Props. |
BaseCockpit-Props
| Prop | Typ | Default | Bedeutung |
|---|---|---|---|
apps | CockpitApp[] | — | Pflicht. Welche Apps das Sachbearbeiter-Cockpit anbietet. |
configUrl | string | /authConfig.json | Pfad der Auth-Config |
router | "hash" | "browser" | "none" | "hash" | Wer den Router stellt — siehe Routing |
basename | string | — | Nur bei router="browser": Basis-Pfad |
branding | { name?, logo? } | — | Name und Logo in der Kopfzeile |
OverviewComponent | Komponente | Kachel-Grid | Slot: eigene App-Übersicht |
HeaderComponent | Komponente | Branding + Home + Profil-Menü | Slot: eigene Kopfzeile |
className / style | — | — | Werden auf den äußeren Container gelegt |
4. Rollen in Keycloak
Dieser Schritt ist nur nötig, wenn in der authConfig.json aus Schritt 1
checkPermission: true steht. Dann zeigt das Sachbearbeiter-Cockpit einer Person nur die Apps,
für die sie eine passende Realm-Rolle hat — der Name ergibt sich aus der
App-Id:
App-Id "akten" → Realm-Rolle "cockpit-akten"
In Keycloak sind das zwei Dinge:
- Im Realm eine Realm-Rolle
cockpit-<appId>pro App anlegen. - Diese Rolle den Benutzern zuweisen — direkt oder über eine Gruppe.
Ohne checkPermission sind alle Apps für jeden angemeldeten User sichtbar.
Mehr dazu unter Auth & Rechte.
