📝 Note

pro/av platform/architecture

reactfastifyqsystypescriptkubernetesmqttsocket-io

Architecture — av-platform

Liens : Decisions · Journal

Vue d’ensemble

Plateforme de contrôle AV pour salles — interface web temps réel, Q-SYS Core, intégration MQTT bâtiment.

Browser (React 19) ←→ Socket.IO ←→ Fastify 5 ←→ Q-SYS Core (QRWC/QRC)

                                   Redis (adapter K8s)

                                   MQTT Broker (HVAC, éclairage)

Composants principaux

apps/frontend

  • Auth : MSAL + useAuth hook + RoleGuard composant
  • State : Zustand — qsysStore, roomStore, buildingStore
  • Socket : socketClient.ts singleton, token MSAL dans handshake.auth.token
  • Thèmes : 8 thèmes CSS [data-theme] sur <html>, persistance localStorage
  • WYSIWYG : AdminRoomEditor — react-grid-layout + panneau de style
  • Contrôles : Fader, MuteButton, SourceSelect, SnapshotButton
  • VU-mètres : VUMeter — Canvas 2D, 30fps rAF, Float32Array (jamais React state)

apps/backend

  • Auth plugin : JWT Entra ID (jwks-rsa, cache JWKS 1h)
  • Redis adapter : @socket.io/redis-adapter — scale horizontal pods
  • QSysManager : QRWC + QRC fallback TCP, reconnexion backoff exponentiel
  • MQTTService : broker MQTT v5, topicMap bâtiment → BuildingUpdate
  • Routes : GET /health, GET /ready, GET /metrics (Prometheus, sans auth)

packages/types (0 deps runtime)

  • Room, ControlConfig, Control, QSysStatus, AppRole, SocketEvents, BuildingUpdate
  • Extensions WYSIWYG : ControlLayout, ControlStyle, Room.theme, Room.layoutMode

packages/qsys-client

  • QRWCClient (@q-sys/qrwc) + QRCClient (qrc-client-js, TCP)
  • Change Groups : ui 100ms · meters 50ms · status 1000ms
  • Keep-alive QRC : NoOp toutes les 30s

Flux de données

  1. Contrôle : Browser → control:set → RBAC check → QSysManager.setControl() → Core
  2. Update : Core → QRWC event → io.to(room).emit('control:update') → clients
  3. Mètres : Core → Change Group meters 50ms → meters:updateuseMeterCanvas rAF
  4. Bâtiment : MQTT → topicMapbuilding:updatebuildingStore
  5. Thème : room.themeRoomControl useEffectdocument.documentElement.setAttribute('data-theme', ...) + localStorage

Dépendances externes

ServiceUsage
Q-SYS CoreContrôle audio (QRWC WebSocket + QRC TCP fallback)
Microsoft Entra IDAuth JWT, JWKS, 4 rôles RBAC
Redis 7Socket.IO adapter K8s
MQTT BrokerDonnées bâtiment (HVAC, éclairage)
Prometheus / GrafanaMonitoring (cluster Millennium)

Schémas

RBAC

av.admin       → tout (config, WYSIWYG, snapshots, monitoring)
av.technician  → toutes salles + snapshots + VU-mètres + statut
av.staff       → salles assignées, contrôles de base
av.kiosk       → une salle (claim av_room JWT), read-mostly

Monorepo

av-platform/
├── apps/frontend/      React 19 + Vite + TS strict
├── apps/backend/       Fastify 5 + Socket.IO + TS strict
├── packages/types/     Interfaces partagées (0 deps runtime)
├── packages/qsys-client/
├── k8s/                Manifests (namespace: av-dashboard)
└── docs/               PLAN.md, QSYS_SETUP.md

Infra K8s — cluster Millennium (Vaud), namespace av-dashboard

  • Backend : 2 replicas min, HPA CPU 70%, max 5 pods
  • Frontend : Nginx alpine, 1-2 replicas
  • Ingress : annotations WebSocket obligatoires