147 lines
4.8 KiB
Markdown
147 lines
4.8 KiB
Markdown
# Benachrichtigungen
|
|
|
|
Das Modul `apps/backend/src/notifications` stellt persoenliche In-App-
|
|
Benachrichtigungen bereit. Es gibt bewusst keinen E-Mail-Versand, keine Push
|
|
Notifications, keine WebSockets, keine Server-Sent Events, keine Queue, kein
|
|
Redis und keine Hintergrundjobs. Neue Benachrichtigungen werden beim Laden der
|
|
Anwendung und per Frontend-Polling abgerufen.
|
|
|
|
## Datenmodell
|
|
|
|
Die Entity `NotificationEntity` wird in der Tabelle `notifications` gespeichert.
|
|
Jede Benachrichtigung gehoert genau einem Benutzer.
|
|
|
|
Felder:
|
|
|
|
- `id`: UUID
|
|
- `userId`: Foreign Key auf `users.id`
|
|
- `type`: technischer Typ aus `NotificationType`
|
|
- `title`: kurzer Plain-Text-Titel, maximal 150 Zeichen
|
|
- `message`: Plain-Text-Nachricht, maximal 1000 Zeichen
|
|
- `link`: optionale interne Angular-Route, maximal 500 Zeichen
|
|
- `metadata`: optionale JSON-Metadaten, maximal 4096 Bytes serialisiert
|
|
- `readAt`: `null`, solange ungelesen
|
|
- `createdAt`: UTC-Erstellungszeit
|
|
- `deletedAt`: Soft Delete
|
|
|
|
Indizes:
|
|
|
|
- `user_id, created_at`
|
|
- `user_id, read_at`
|
|
- `user_id, deleted_at`
|
|
|
|
Die Migration liegt unter
|
|
`apps/backend/src/database/migrations/1720000001000-AddNotifications.ts` und wird
|
|
nicht automatisch beim App-Start ausgefuehrt.
|
|
|
|
## Typen
|
|
|
|
Benachrichtigungstypen sind Code, keine Datenbankdaten:
|
|
|
|
- `system`
|
|
- `item.created`
|
|
- `item.updated`
|
|
- `user.role-changed`
|
|
|
|
Neue Module erweitern `apps/backend/src/notifications/notification-types.ts`
|
|
und nutzen anschliessend `NotificationsService`.
|
|
|
|
## Permissions
|
|
|
|
- `notifications.readOwn`: eigene Benachrichtigungen lesen
|
|
- `notifications.updateOwn`: eigene Benachrichtigungen markieren oder loeschen
|
|
- `notifications.manage`: administrativ Benachrichtigungen erzeugen
|
|
|
|
Die Systemrolle `user` erhaelt `notifications.readOwn` und
|
|
`notifications.updateOwn`. Die Rolle `admin` erhaelt ueber `allPermissions`
|
|
zusaetzlich `notifications.manage`.
|
|
|
|
## API
|
|
|
|
Alle Endpunkte liegen unter dem bestehenden API-Praefix `/api`.
|
|
|
|
- `GET /notifications`: eigene Benachrichtigungen, mit `page`, `pageSize` und
|
|
`status=all|read|unread`
|
|
- `GET /notifications/unread-count`: Anzahl ungelesener eigener
|
|
Benachrichtigungen
|
|
- `PATCH /notifications/:id/read`: idempotent als gelesen markieren
|
|
- `PATCH /notifications/:id/unread`: idempotent als ungelesen markieren
|
|
- `PATCH /notifications/read-all`: alle eigenen Benachrichtigungen als gelesen
|
|
markieren
|
|
- `DELETE /notifications/:id`: Soft Delete einer eigenen Benachrichtigung
|
|
- `POST /admin/notifications`: administrative Erzeugung mit
|
|
`notifications.manage`
|
|
|
|
Normale Benutzer koennen keine fremde `userId` uebergeben. Der aktuelle Benutzer
|
|
wird serverseitig aus der Session bestimmt.
|
|
|
|
## Interner Service
|
|
|
|
Andere Backend-Module erzeugen Benachrichtigungen ueber
|
|
`NotificationsService`, nicht direkt ueber ein Repository:
|
|
|
|
```ts
|
|
await notifications.createForUser({
|
|
userId,
|
|
type: NotificationType.ItemCreated,
|
|
title: 'Neuer Eintrag',
|
|
message: 'Der Eintrag "Beispiel" wurde erstellt.',
|
|
link: '/items/123',
|
|
metadata: { itemId: '123' },
|
|
});
|
|
```
|
|
|
|
Verfuegbare Methoden:
|
|
|
|
- `createForUser`
|
|
- `createForUsers`
|
|
- `getForCurrentUser`
|
|
- `getUnreadCount`
|
|
- `markAsRead`
|
|
- `markAsUnread`
|
|
- `markAllAsRead`
|
|
- `softDelete`
|
|
|
|
`createForUsers` begrenzt Bulk-Erzeugung auf 100 Zielbenutzer.
|
|
|
|
## Integrationen
|
|
|
|
`ItemsService` benachrichtigt beim Erstellen eines Items den ersten aktiven
|
|
Administrator, sofern dieser nicht der Ersteller ist. Das ist eine bewusst kleine
|
|
Beispielregel und keine vollstaendige fachliche Eskalationslogik.
|
|
|
|
`UsersService` benachrichtigt betroffene Benutzer nach erfolgreicher
|
|
Rollenaenderung. Fehler beim Erzeugen werden geloggt; die bereits erfolgreiche
|
|
sicherheitsrelevante Rollenaenderung wird dadurch nicht unkontrolliert
|
|
zurueckgerollt.
|
|
|
|
Administrative Erzeugung wird im Audit-Log als `NOTIFICATION_CREATED`
|
|
protokolliert. Geloggt werden Actor, Zielbenutzer, Notification-ID, Typ und
|
|
Request-ID, nicht die vollstaendige Nachricht oder Metadata.
|
|
|
|
## Frontend
|
|
|
|
`NotificationStore` verwendet Angular Signals fuer lokalen State und RxJS fuer
|
|
HTTP und Polling. Header-Panel und Seite `/notifications` verwenden denselben
|
|
Store, damit keine doppelten Requests fuer dieselben Daten entstehen.
|
|
|
|
Polling:
|
|
|
|
- Standardintervall: 60 Sekunden ueber `NOTIFICATION_POLL_INTERVAL_MS`
|
|
- nur bei angemeldetem Benutzer
|
|
- pausiert bei unsichtbarem Tab
|
|
- aktualisiert sofort beim Sichtbarwerden
|
|
- verhindert ueberlappende Count-Requests
|
|
- stoppt und leert State beim Logout
|
|
|
|
Links werden im Frontend nur navigiert, wenn sie interne relative Routen sind.
|
|
Titel und Nachricht werden normal interpoliert und nicht per `innerHTML`
|
|
gerendert.
|
|
|
|
## Spaetere Echtzeitkommunikation
|
|
|
|
Wenn spaeter echte Echtzeitkommunikation noetig wird, sollte das als separate
|
|
Architekturentscheidung erfolgen. Dann waeren Transport, Skalierung,
|
|
Authentifizierung, Backpressure und Betrieb gemeinsam zu entscheiden, statt
|
|
WebSockets oder Queues nebenbei in das In-App-Modul einzubauen.
|