generated from bastian/boilerplate
Initial commit
This commit is contained in:
146
docs/notifications.md
Normal file
146
docs/notifications.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user