Bug fix - notifications audience
* Performers had their notifications publicaly published. * refacto admin group name
This commit is contained in:
parent
d192705f29
commit
d476c08046
36 changed files with 421 additions and 74 deletions
|
|
@ -16,6 +16,7 @@ La racine de l'architecture est [Architecture.md](Architecture.md).
|
|||
| [architecture/offres-frontmatter.md](architecture/offres-frontmatter.md) | Offre fournisseur, ClasseFormulaire, ClasseDevis, parsing frontmatter |
|
||||
| [architecture/postit-oidc.md](architecture/postit-oidc.md) | Client desktop PostIt, custom URI scheme, silent refresh |
|
||||
| [architecture/postit.md](architecture/postit.md) | PostIt — topologie des projets, ViewLocator custo, navigation, DI, conventions de binding |
|
||||
| [architecture/notifications.md](architecture/notifications.md) | Notifications persistées, messages Web et événements temps réel côté serveur, Web et PostIt |
|
||||
| [architecture/decoupage-organisation.md](architecture/decoupage-organisation.md) | Découpage des projets .NET (Abstract, Server, Org, Api, Blogs, Web, Org.Tests) |
|
||||
| [testing.md](testing.md) | Stratégie de test : conventions des dossiers, EF Core in-memory, auth stubs, scaffold partagé |
|
||||
| [onboarding-agents.md](onboarding-agents.md) | Parcours pas-à-pas pour prise en main agents IA + architecture + tests |
|
||||
|
|
|
|||
229
doc/architecture/notifications.md
Normal file
229
doc/architecture/notifications.md
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
# Notifications Yavsc : serveur, Web et PostIt
|
||||
|
||||
Ce document décrit les mécanismes de notification actuellement présents
|
||||
dans le dépôt. Le terme « notification » recouvre plusieurs flux distincts :
|
||||
|
||||
1. les **annonces persistées** en base, affichées dans le site Web et sur
|
||||
l'accueil de PostIt ;
|
||||
2. les **messages temporaires de l'interface Web**, attachés à une réponse
|
||||
MVC par `ViewBag.Notify` ;
|
||||
3. les **événements temps réel** du chat et les événements métier diffusés par
|
||||
SignalR.
|
||||
|
||||
Ces flux n'ont ni le même stockage, ni le même cycle de vie, ni les mêmes
|
||||
clients.
|
||||
|
||||
## Vue d'ensemble
|
||||
|
||||
| Flux | Serveur / transport | Web | PostIt |
|
||||
|---|---|---|---|
|
||||
| Annonce persistée | Entité `Notification` en base, API HTTP | Affichée dans le layout et masquable | Liste sur `HomePage`, chargée par HTTP et masquable |
|
||||
| Message d'interface | Liste de portée requête dans `ViewBag.Notify` | Affichée comme alerte dans le layout | Non consommé |
|
||||
| Événement de chat | Hub SignalR authentifié à `/chathub` | Le site Web n'en consomme pas dans ses vues MVC | `notifyUser` et `notifyRoom` alimentent les journaux du chat |
|
||||
| Événement métier `push` | SignalR vers les connexions actives, plus e-mail | Pas de consommateur identifié dans les vues Web | Pas de gestionnaire `push` dans le client PostIt actuel |
|
||||
|
||||
Les notifications persistées sont donc différentes d'une notification système
|
||||
native (notification du navigateur ou du système d'exploitation). Le client
|
||||
PostIt affiche une liste dans sa page d'accueil ; il ne demande pas de
|
||||
permission de notification native.
|
||||
|
||||
## 1. Annonces persistées
|
||||
|
||||
### Modèle et stockage
|
||||
|
||||
L'entité partagée [`Notification`](../../src/Yavsc.Abstract/Messaging/Notification.cs)
|
||||
est enregistrée dans la table `Notification` via `ApplicationDbContext`.
|
||||
Elle contient :
|
||||
|
||||
- `Id`, identifiant généré ;
|
||||
- `title` et `body`, obligatoires, limités respectivement à 1 024 et 512
|
||||
caractères ;
|
||||
- `icon`, `sound`, `tag`, `color`, propriétés optionnelles ;
|
||||
- `click_action`, obligatoire, utilisé comme libellé d'action dans les
|
||||
interfaces ;
|
||||
- `Target`, chaîne de ciblage. Les valeurs documentées par le modèle sont
|
||||
`null` (tout le monde), `user/{UserId}`, `pub/{topic}` et
|
||||
`administration`.
|
||||
|
||||
Le masquage d'une annonce pour un utilisateur connecté est stocké dans
|
||||
`DismissClicked`, avec une clé composée de l'identifiant utilisateur et de
|
||||
l'identifiant de notification. Le même avis peut ainsi être masqué
|
||||
indépendamment par chaque utilisateur.
|
||||
|
||||
### API
|
||||
|
||||
[`NotificationsApiController`](../../src/Yavsc.Api/Controllers/Administration/NotificationsApiController.cs)
|
||||
expose les deux routes GET suivantes, sous le préfixe `/api/v1` :
|
||||
|
||||
| Route | Accès | Résultat |
|
||||
|---|---|---|
|
||||
| `GET /api/v1/notifications` | Anonyme ou authentifié | Notifications visibles à l'appelant, non masquées |
|
||||
| `GET /api/v1/notifications/me` | Anonyme ou authentifié | Même action et mêmes règles que la route sans suffixe |
|
||||
|
||||
La réponse est une liste JSON triée par `Id` décroissant. L'API exclut les
|
||||
notifications masquées par l'utilisateur authentifié, puis applique les
|
||||
règles d'audience : cible vide, cible `pub/…`, cible correspondant à
|
||||
`user/{identifiant courant}`, ou cible `administration` pour un administrateur.
|
||||
Une requête anonyme n'a ni identifiant utilisateur ni rôle administrateur.
|
||||
|
||||
[`DismissClicksApiController`](../../src/Yavsc.Api/Controllers/Administration/DismissClicksApiController.cs)
|
||||
est réservé aux utilisateurs authentifiés :
|
||||
|
||||
| Route | Effet |
|
||||
|---|---|
|
||||
| `GET /api/v1/dismiss` | Liste les identifiants masqués par l'utilisateur |
|
||||
| `POST /api/v1/dismiss/click/{notificationId}` | Masque l'avis pour l'utilisateur ; l'opération est idempotente |
|
||||
| `DELETE /api/v1/dismiss/click/{notificationId}` | Rétablit l'avis s'il était masqué ; renvoie `404` sinon |
|
||||
|
||||
Le POST renvoie `404` si l'identifiant de notification n'existe pas. Le modèle
|
||||
de réponse est [`DismissedNotification`](../../src/Yavsc.Abstract/Messaging/DismissedNotification.cs).
|
||||
|
||||
### Création et notifications métier
|
||||
|
||||
Le contrôleur MVC
|
||||
[`NotificationsController`](../../src/Yavsc.Org/Controllers/Communicating/NotificationsController.cs)
|
||||
permet la gestion CRUD des enregistrements de notification.
|
||||
|
||||
Le service [`YavscMessageSender`](../../src/Yavsc.Server/Services/YavscMessageSender.cs)
|
||||
crée aussi une notification persistée, ciblée sur `user/{id}`, pour les
|
||||
événements `RdvQueryEvent` et `ClientEstimateSignedEvent`. L'enregistrement
|
||||
est effectué avant l'envoi d'e-mail. Le même service tente ensuite d'envoyer
|
||||
un e-mail si l'adresse est disponible et confirmée, puis diffuse l'événement
|
||||
`push` aux connexions SignalR actives de l'utilisateur.
|
||||
|
||||
Cette diffusion `push` est conditionnée par la présence de connexions actives ;
|
||||
l'avis stocké en base reste, lui, disponible lors d'une récupération ultérieure
|
||||
par l'API.
|
||||
|
||||
## 2. Site Web
|
||||
|
||||
### Annonces persistées sur l'accueil
|
||||
|
||||
[`HomeController.Index`](../../src/Yavsc.Org/Controllers/HomeController.cs)
|
||||
charge les notifications non masquées et les place dans `ViewBag.Notify`.
|
||||
[`_Layout.cshtml`](../../src/Yavsc.Org/Views/Shared/_Layout.cshtml) les rend
|
||||
comme des alertes Bootstrap avec icône, titre, corps et action. Le titre et le
|
||||
corps passent par les composants Markdown du site.
|
||||
|
||||
Le clic sur l'action appelle `POST /Home/DismissNotification/{id}` via
|
||||
[`site.js`](../../src/Yavsc.Org/wwwroot/js/site.js) :
|
||||
|
||||
- pour un utilisateur connecté, le masquage est enregistré dans
|
||||
`DismissClicked` ;
|
||||
- pour un visiteur anonyme, les identifiants sont conservés dans la session
|
||||
Web (`clicked`).
|
||||
|
||||
La route renvoie `404` si l'avis n'existe pas et `204` après le masquage.
|
||||
Comme l'API, l'accueil Web filtre `Target` : les cibles `user/{id}` ne sont
|
||||
visibles qu'à l'utilisateur correspondant, et la cible `administration`
|
||||
uniquement aux administrateurs. Un visiteur anonyme ne reçoit que les
|
||||
notifications publiques (`Target` vide ou préfixe `pub/`).
|
||||
|
||||
### Messages temporaires par requête
|
||||
|
||||
Les helpers [`NotifyInfo`, `NotifyWarning` et `Notify`](../../src/Yavsc.Org/Helpers/ControllerHelpers.cs)
|
||||
ajoutent des objets à `ViewBag.Notify`. Ce mécanisme n'écrit pas en base :
|
||||
il ne vit que dans la réponse MVC qui rend le layout. Les méthodes
|
||||
`NotifyInfo` et `NotifyWarning` partagent actuellement le même rendu générique
|
||||
`alert-info`.
|
||||
|
||||
## 3. PostIt
|
||||
|
||||
[`NotificationsApiClient`](../../src/Yavsc.Api.Client/NotificationsApiClient.cs)
|
||||
construit les routes à partir de la base d'API de l'hôte métier configuré.
|
||||
Il fournit les méthodes de lecture publique et authentifiée, ainsi que les
|
||||
opérations de lecture, masquage et restauration.
|
||||
|
||||
Sur [`HomePage`](../../src/PostIt/PostIt/Views/Layout/HomePage.axaml),
|
||||
[`HomePageViewModel`](../../src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs)
|
||||
charge la liste :
|
||||
|
||||
- sans session valide, il appelle la route publique
|
||||
`GET /api/v1/notifications` ;
|
||||
- avec une session valide, il appelle
|
||||
`GET /api/v1/notifications/me` avec le client HTTP authentifié.
|
||||
|
||||
Le chargement est lancé à l'ouverture de la page et relancé lorsque l'état de
|
||||
connexion change. Un compteur de version ignore les réponses obsolètes si un
|
||||
chargement plus récent a déjà commencé. L'interface distingue l'état de
|
||||
chargement, la liste vide et l'erreur de chargement ; l'utilisateur peut aussi
|
||||
actualiser manuellement.
|
||||
|
||||
Le bouton d'une notification utilise `click_action` comme libellé et masque
|
||||
l'élément. En session authentifiée, PostIt appelle
|
||||
`POST /api/v1/dismiss/click/{id}` avant de retirer l'élément de la liste. Si
|
||||
l'appel échoue, l'élément reste affiché et une erreur est présentée. Sans
|
||||
session, l'élément est retiré de la liste locale, sans persistance du
|
||||
masquage côté serveur.
|
||||
|
||||
## 4. Événements temps réel
|
||||
|
||||
### Hub du chat
|
||||
|
||||
Le hub [`ChatHub`](../../src/Yavsc.Server/Hubs/ChatHub.cs) est publié par
|
||||
Yavsc.Api à la racine sous `/chathub` (et non sous `/api/v1`) et porte
|
||||
`[Authorize]`. Il s'appuie sur SignalR et sur `IConnexionManager` pour suivre
|
||||
les utilisateurs et les salons.
|
||||
|
||||
Les événements de notification émis par le hub comprennent notamment :
|
||||
|
||||
| Événement SignalR | Données | Utilisation PostIt |
|
||||
|---|---|---|
|
||||
| `notifyUser` | `type`, identifiant ou nom cible, message | Ajout au journal système |
|
||||
| `notifyRoom` | `type`, salon, utilisateur ou détail | Ajout au journal du salon ; met à jour la liste d'utilisateurs sur `UserJoin` |
|
||||
| `joint` | `ChatRoomInfo` | Initialise la vue du salon rejoint |
|
||||
| `ReceiveMessage` | message avec nom, salon et corps | Ajoute le message à l'historique du salon |
|
||||
| `notifyUserInRoom` | `type`, salon, message | Pas de gestionnaire correspondant dans `ChatViewModel` actuellement |
|
||||
|
||||
Les constantes de types du chat sont déclarées dans
|
||||
[`NotificationTypes`](../../src/Yavsc.Abstract/Messaging/NotificationTypes.cs).
|
||||
Les messages `notifyUser` et `notifyRoom` ne sont pas enregistrés en base :
|
||||
ils décrivent l'état temps réel de connexion, de salon ou les erreurs de
|
||||
conversation.
|
||||
|
||||
Le [`ChatViewModel`](../../src/PostIt/PostIt/ViewModels/Chat/ChatViewModel.cs)
|
||||
ouvre le hub avec le jeton d'accès PostIt, tente un renouvellement silencieux
|
||||
du jeton, et active la reconnexion automatique. Après reconnexion, il rejoint
|
||||
à nouveau le salon sélectionné, car la connexion SignalR précédente et ses
|
||||
groupes ne sont plus actifs. Les callbacks mettent à jour les collections sur
|
||||
le thread UI d'Avalonia.
|
||||
|
||||
### Événement métier `push`
|
||||
|
||||
En plus des événements du chat, `YavscMessageSender` utilise le même contexte
|
||||
SignalR pour émettre `push` avec le topic et une charge utile sérialisée. Ce
|
||||
nom d'événement est distinct de `notifyUser` et `notifyRoom`. Aucun
|
||||
gestionnaire `push` n'est présent dans le client PostIt actuel : les annonces
|
||||
métier persistées sont visibles dans PostIt lors du prochain chargement ou
|
||||
rafraîchissement HTTP.
|
||||
|
||||
## 5. Points d'attention pour les évolutions
|
||||
|
||||
- Conserver les règles d'audience alignées entre l'API et le site Web ; en
|
||||
particulier, appliquer un filtrage `Target` côté Web avant d'exposer les
|
||||
avis dans le layout.
|
||||
- Ne pas confondre masquage utilisateur (`DismissClicked`) et suppression
|
||||
globale de la notification : le masquage API est par utilisateur.
|
||||
- Les sessions anonymes du site Web et PostIt n'ont pas le même comportement :
|
||||
le site Web conserve le masquage en session, tandis que PostIt ne le garde
|
||||
que dans la liste affichée jusqu'au prochain rechargement.
|
||||
- Ajouter un nouveau type de cible ou un nouvel événement temps réel implique
|
||||
d'examiner séparément son filtrage, son transport et les clients qui
|
||||
l'écoutent.
|
||||
- Les références à GCM et le nom historique de `YavscMessageSender` ne
|
||||
signifient pas que PostIt reçoit une notification native hors ligne : le
|
||||
chemin observé ici est l'e-mail, l'événement SignalR temps réel et la
|
||||
notification persistée récupérée par HTTP.
|
||||
|
||||
## Références d'implémentation
|
||||
|
||||
- [`Notification`](../../src/Yavsc.Abstract/Messaging/Notification.cs)
|
||||
- [`NotificationsApiController`](../../src/Yavsc.Api/Controllers/Administration/NotificationsApiController.cs)
|
||||
- [`DismissClicksApiController`](../../src/Yavsc.Api/Controllers/Administration/DismissClicksApiController.cs)
|
||||
- [`NotificationsController`](../../src/Yavsc.Org/Controllers/Communicating/NotificationsController.cs)
|
||||
- [`HomeController`](../../src/Yavsc.Org/Controllers/HomeController.cs)
|
||||
- [`YavscMessageSender`](../../src/Yavsc.Server/Services/YavscMessageSender.cs)
|
||||
- [`ChatHub`](../../src/Yavsc.Server/Hubs/ChatHub.cs)
|
||||
- [`NotificationsApiClient`](../../src/Yavsc.Api.Client/NotificationsApiClient.cs)
|
||||
- [`HomePageViewModel`](../../src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs)
|
||||
- [`ChatViewModel`](../../src/PostIt/PostIt/ViewModels/Chat/ChatViewModel.cs)
|
||||
Loading…
Reference in a new issue