feat(admin): pousse le monitoring en WebSocket au lieu du polling client #35

Merged
kyuno merged 1 commit from feat/admin-monitoring-websocket into fix/admin-monitoring-spa-route-collision 2026-09-05 15:36:55 +02:00
Owner

Empilée sur #33 (encore non mergée) — voir stack-branches-on-unreviewed-pr.

Contexte

Le tableau de bord /admin/monitoring fonctionnait en polling côté client (15 s) déclenchant une sonde active côté serveur à chaque appel — deux onglets admin ouverts = deux séries de sondes indépendantes et redondantes.

Décisions actées avec vous (AskUserQuestion, voir le plan approuvé) :

  • L'endpoint REST disparaît entièrement — 100 % WebSocket, pas de repli REST.
  • La sonde tourne en continu dès le démarrage du serveur, indépendamment de la présence d'un admin connecté.

Backend

  • ExpressServer (packages/express-tools) expose désormais son http.Server sous-jacent — socket.io s'attache au serveur HTTP lui-même, pas à Express/un port.
  • MonitoringBroadcastService (nouveau) : sonde immédiate au démarrage puis toutes les 60 s (réutilise getMonitoring() tel quel), diffuse à la room "monitoring", rejoue le dernier résultat en cache à tout nouvel admin qui rejoint (pas d'attente jusqu'à une minute pour un onglet fraîchement ouvert).
  • requireAdminSocket (nouveau) : équivalent WebSocket de requireAdmin, même vérification factorisée dans resolveAdminFromToken.
  • GET /admin/api/monitoring supprimée.
  • server.ts (pas app.ts) instancie socket.io — les tests supertest n'ont pas besoin d'un serveur WebSocket.

Frontend

  • MonitoringSocketClient (nouveau) : wrapper socket.io-client, interface volontairement petite et injectable dans MonitoringPage pour rester testable sans serveur réel.
  • MonitoringPage : data et connectionState séparés — une coupure de connexion après un premier chargement affiche un badge "reconnexion en cours" sans jamais effacer le dernier board connu. Seul le tout premier chargement affiche l'état plein-écran chargement/erreur.

Tests

  • Nouveau admin-monitoring-socket.test.ts : vraie connexion socket.io-client contre un serveur réel démarré dans le test, authentification avec un admin seedé + cookie signé, vérifie la réception de monitoring:update et le rejet sans cookie admin valide.
  • Les tests heartbeat de l'ancien admin-monitoring.test.ts (route /internal/tech-steps/heartbeat, sans rapport avec ce changement) sont déplacés vers tech-step-worker.routes.test.ts, leur router d'origine.
  • Nouveau MonitoringPage.cy.tsx (mode composant, client factice) : rendu des cartes, badge de reconnexion sans perte du board, états chargement/erreur. Pas de test e2e Gherkin contre un vrai WebSocket — aucun mécanisme pour ça dans ce repo, ce serait fragile ; le test Mocha ci-dessus est la preuve bout-en-bout côté serveur.
  • L'ancien admin-monitoring.cy.ts (REST) est supprimé, remplacé par le test composant.

Vérification

  • pnpm lint : clean
  • pnpm build (tout le monorepo) : clean
  • Suite Cypress e2e complète : 27/27 fichiers, 114/114 tests verts
  • Suite Cypress composant complète : 5/5 fichiers, 54/54 tests verts (dont les 4 nouveaux)
  • ⚠️ Suite Mocha apps/api non exécutable cette session (Postgres/Docker injoignable, même limitation que pour #33) — le nouveau test socket.io n'a donc pas pu être run-vérifié, seulement relu attentivement et passé au lint. À faire tourner pour de vrai dès qu'un accès DB est disponible avant de considérer ce point définitivement clos.

🤖 Generated with Claude Code

Empilée sur #33 (encore non mergée) — voir [[stack-branches-on-unreviewed-pr]]. ## Contexte Le tableau de bord `/admin/monitoring` fonctionnait en polling **côté client** (15 s) déclenchant une sonde active côté serveur à chaque appel — deux onglets admin ouverts = deux séries de sondes indépendantes et redondantes. Décisions actées avec vous (AskUserQuestion, voir le plan approuvé) : - L'endpoint REST disparaît entièrement — **100 % WebSocket**, pas de repli REST. - La sonde tourne **en continu dès le démarrage** du serveur, indépendamment de la présence d'un admin connecté. ## Backend - `ExpressServer` ([packages/express-tools](packages/express-tools/src/express-server.ts)) expose désormais son `http.Server` sous-jacent — `socket.io` s'attache au serveur HTTP lui-même, pas à Express/un port. - [MonitoringBroadcastService](apps/api/src/lib/monitoring-broadcast.service.ts) (nouveau) : sonde immédiate au démarrage puis toutes les 60 s (réutilise `getMonitoring()` tel quel), diffuse à la room `"monitoring"`, rejoue le dernier résultat en cache à tout nouvel admin qui rejoint (pas d'attente jusqu'à une minute pour un onglet fraîchement ouvert). - [requireAdminSocket](apps/api/src/middlewares/require-admin-socket.ts) (nouveau) : équivalent WebSocket de `requireAdmin`, même vérification factorisée dans `resolveAdminFromToken`. - `GET /admin/api/monitoring` supprimée. - `server.ts` (pas `app.ts`) instancie `socket.io` — les tests supertest n'ont pas besoin d'un serveur WebSocket. ## Frontend - [MonitoringSocketClient](apps/web/src/api/monitoring-socket.ts) (nouveau) : wrapper `socket.io-client`, interface volontairement petite et **injectable** dans `MonitoringPage` pour rester testable sans serveur réel. - `MonitoringPage` : `data` et `connectionState` séparés — une coupure de connexion après un premier chargement affiche un badge "reconnexion en cours" **sans jamais effacer le dernier board connu**. Seul le tout premier chargement affiche l'état plein-écran chargement/erreur. ## Tests - Nouveau [admin-monitoring-socket.test.ts](apps/api/test/admin-monitoring-socket.test.ts) : vraie connexion `socket.io-client` contre un serveur réel démarré dans le test, authentification avec un admin seedé + cookie signé, vérifie la réception de `monitoring:update` et le rejet sans cookie admin valide. - Les tests heartbeat de l'ancien `admin-monitoring.test.ts` (route `/internal/tech-steps/heartbeat`, sans rapport avec ce changement) sont déplacés vers `tech-step-worker.routes.test.ts`, leur router d'origine. - Nouveau [MonitoringPage.cy.tsx](apps/web/cypress/component/MonitoringPage.cy.tsx) (mode composant, client factice) : rendu des cartes, badge de reconnexion sans perte du board, états chargement/erreur. Pas de test e2e Gherkin contre un vrai WebSocket — aucun mécanisme pour ça dans ce repo, ce serait fragile ; le test Mocha ci-dessus est la preuve bout-en-bout côté serveur. - L'ancien `admin-monitoring.cy.ts` (REST) est supprimé, remplacé par le test composant. ## Vérification - `pnpm lint` : clean - `pnpm build` (tout le monorepo) : clean - Suite Cypress e2e complète : **27/27 fichiers, 114/114 tests verts** - Suite Cypress composant complète : **5/5 fichiers, 54/54 tests verts** (dont les 4 nouveaux) - ⚠️ Suite Mocha `apps/api` **non exécutable cette session** (Postgres/Docker injoignable, même limitation que pour #33) — le nouveau test socket.io n'a donc pas pu être run-vérifié, seulement relu attentivement et passé au lint. À faire tourner pour de vrai dès qu'un accès DB est disponible avant de considérer ce point définitivement clos. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
kyuno added 1 commit 2026-09-05 14:45:17 +02:00
feat(admin): pousse le monitoring en WebSocket au lieu du polling client
Some checks failed
CI / lint (push) Successful in 2m1s
CI / build (push) Successful in 1m21s
CI / intent-service-test (push) Successful in 4m25s
CI / e2e (push) Successful in 6m12s
CI / test (push) Failing after 3h13m30s
13d92f8fa8
Le tableau de bord /admin/monitoring fonctionnait en polling côté client
(15 s) déclenchant une sonde active côté serveur à chaque appel — deux
onglets admin ouverts = deux séries de sondes redondantes.

Inverse le mécanisme : le serveur sonde une fois par minute et pousse le
résultat aux clients connectés via WebSocket (socket.io, room
"monitoring"). Décisions actées avec l'utilisateur : l'endpoint REST
disparaît entièrement (plus de repli, 100% WebSocket) ; la sonde tourne
en continu dès le démarrage, sans lien avec la présence d'un admin
connecté (le plus simple des deux, coût des sondes négligeable).

Backend :
- ExpressServer (packages/express-tools) expose désormais son
  http.Server sous-jacent (créé explicitement, plus dans .listen()) —
  socket.io s'attache au serveur HTTP lui-même, pas à Express/un port.
- MonitoringBroadcastService (nouveau) : sonde immédiate au démarrage
  puis toutes les 60 s (réutilise getMonitoring() tel quel, aucune
  duplication), diffuse à la room "monitoring", rejoue le dernier
  résultat en cache à tout nouvel admin qui rejoint (pas d'attente
  jusqu'à une minute pour un onglet fraîchement ouvert).
- requireAdminSocket (nouveau) : middleware de handshake socket.io,
  équivalent WebSocket de requireAdmin — même vérification (cookie +
  JWT + tokenVersion), factorisée dans resolveAdminFromToken
  (require-admin.ts) puisque les deux ne diffèrent que par où ils lisent
  le cookie brut (un handshake socket.io ne passe pas par
  cookie-parser).
- GET /admin/api/monitoring supprimée (route + service de test associés).
- server.ts (pas app.ts) instancie socket.io — les tests supertest n'ont
  pas besoin d'un serveur WebSocket.

Frontend :
- MonitoringSocketClient (nouveau, api/monitoring-socket.ts) : wrapper
  socket.io-client, interface MonitoringSocketClientLike volontairement
  petite et injectable dans MonitoringPage (prop optionnelle) pour rester
  testable sans serveur réel.
- MonitoringPage : data et connectionState séparés — une coupure de
  connexion après un premier chargement affiche un badge "reconnexion en
  cours" sans jamais effacer le dernier board connu (repose sur la
  reconnexion automatique de socket.io-client). Seul le tout premier
  chargement affiche l'état plein-écran chargement/erreur.
- adminApiClient.getMonitoring() supprimée (plus utilisée).

Tests :
- Nouveau apps/api/test/admin-monitoring-socket.test.ts : vraie connexion
  socket.io-client contre un serveur réel démarré dans le test (port 0),
  authentification avec un admin seedé + cookie signé, vérifie la
  réception de monitoring:update et le rejet sans cookie admin valide.
  Les tests heartbeat de l'ancien admin-monitoring.test.ts (route
  /internal/tech-steps/heartbeat, sans rapport avec ce changement) sont
  déplacés vers tech-step-worker.routes.test.ts, leur router d'origine.
- Nouveau cypress/component/MonitoringPage.cy.tsx : monte la page avec un
  client factice, vérifie rendu des cartes, badge de reconnexion sans
  perte du board, états chargement/erreur — pas de test e2e Gherkin
  contre un vrai WebSocket (aucun mécanisme pour ça dans ce repo,
  fragile ; le test Mocha ci-dessus est la preuve bout-en-bout serveur).
- apps/web/cypress/e2e/admin-monitoring.cy.ts (REST) supprimé, remplacé
  par le test composant ci-dessus.

Vérification : pnpm lint clean, build complet du monorepo clean, suite
Cypress e2e complète 27/27 fichiers (114/114 tests), suite composant
complète 5/5 fichiers (54/54 tests, dont les 4 nouveaux). Suite Mocha
apps/api non exécutable cette session (Postgres/Docker injoignable,
comme pour #33) — le nouveau test socket.io n'a donc pas pu être
run-vérifié, seulement relu attentivement et vérifié par le lint.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
kyuno merged commit fbcf8cfde0 into fix/admin-monitoring-spa-route-collision 2026-09-05 15:36:55 +02:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: kyuno/batchCooking#35
No description provided.