# Temps réel (WebSocket / broadcasting)

Référence de configuration du temps réel byplus (chat, appels, présence, notifications) : pile actuelle, variables
d'environnement, autorisation des canaux, et migration vers **Laravel Reverb** (transition sans coupure).

## 1. Vue d'ensemble

```
Event (ShouldBroadcast)
  → file Redis (worker Horizon)
    → connexion(s) de diffusion            ← config/broadcasting.php
      → serveur WebSocket                  ← laravel-echo-server (actuel) et/ou Reverb (cible)
        → clients (Laravel Echo)           ← web (core-app) + mobile (byplus)
```

- Tous les events chat/appel implémentent `ShouldBroadcast` → **mis en file** (Redis) puis diffusés par un **worker
  Horizon** (`php artisan horizon`). **Sans worker, aucun temps réel.**
- Tous les canaux chat/appel sont **privés** (`PrivateChannel`) → autorisés via `/broadcasting/auth`.

## 2. Pile ACTUELLE

- **Serveur WS** : `laravel-echo-server` (socket.io **v2**, EOL), port **6001**, alimenté par le **pub/sub Redis**.
  Config : `laravel-echo-server.json` (racine). Lancé hors dépôt (npm global + gestionnaire de process).
- **Driver de diffusion** : `BROADCAST_CONNECTION=redis` ; `BROADCAST_CONNECTIONS=redis` (défaut → echo-server seul).
- **File** : `QUEUE_CONNECTION=redis` + Horizon.
- **Serveur Reverb** : **pas encore déployé** (à installer + `reverb:start`, cf. §7). En prod, les apps installées
  parlent encore socket.io.
- **Clients — CODE déjà migré (déploiement Reverb en attente)** :
  - **Web** : `core-app/src/echo.js` — connecteur **piloté par `VITE_APP_BROADCASTER`** (défaut `socket.io`) ;
    `pusher-js` installé. Bascule vers `reverb` par variable au déploiement.
  - **Mobile** : `byplus/src/modules/messaging/realtime.ts` — **Reverb PUR** (`pusher-js`) ; `socket.io-client`
    **retiré**. ⚠️ aucun repli socket.io → nécessite le serveur Reverb up.

## 3. Variables d'environnement (backend `core-api/.env`)

| Clé | Rôle | Valeur |
|---|---|---|
| `BROADCAST_CONNECTION` | connexion de diffusion par défaut | `redis` (actuel) |
| `QUEUE_CONNECTION` | file des broadcasts | `redis` |
| `BROADCAST_CONNECTIONS` | **connexions de TOUS les events temps réel** (double-diffusion) | `redis` (défaut) · `redis,reverb` (transition) · `reverb` (final) |
| `REVERB_APP_ID` / `REVERB_APP_KEY` / `REVERB_APP_SECRET` | app Reverb (protocole Pusher) | `<id>` / `<key>` / `<secret — jamais côté client>` |
| `REVERB_HOST` / `REVERB_PORT` / `REVERB_SCHEME` | serveur Reverb | ex. `<host public>` / `443` / `https` (prod) |

> `BROADCAST_DRIVER` (héritage Laravel ≤10) n'est **plus lu** en Laravel 11 — c'est `BROADCAST_CONNECTION` qui pilote.
> Après toute modification du `.env` : `php artisan config:cache`.

## 4. Connexions de diffusion — `config/broadcasting.php`

- `default` ← `BROADCAST_CONNECTION` (`redis`).
- `connections.redis` (→ laravel-echo-server) et `connections.reverb` (→ Reverb, déjà déclarée).
- **`event_connections`** (ajoutée) ← `BROADCAST_CONNECTIONS` (liste). Consommée par le trait
  `app/Traits/BroadcastsToConfiguredConnections.php`, **appliqué à TOUS les events `ShouldBroadcast`** de l'app (chat,
  appels, helpdesk, notifications, sondages…). Laravel itère `broadcastConnections()` et diffuse sur **chaque** connexion listée.
  Défaut `['redis']` = comportement actuel inchangé.

## 5. Autorisation des canaux privés

- Route **`/broadcasting/auth`** (`app/Providers/BroadcastServiceProvider.php`) : middleware
  `InitializeTenancyByRequestData` (**header `X-Tenant`**) + guards **`auth:device,api`**.
- Callbacks : `routes/channels.php` — `App.Conversation.{id}` (`hasUser`), `App.Call.{userId}`, `App.New.Group.{id}`,
  `App.New.Personal.Conversation.{id}`, `App.Presence.{id}`, etc.
- **En-têtes clients requis** sur l'auth : `Authorization: Bearer <token>` + `X-Tenant: <tenant>`.

## 6. Clients (état du CODE)

Auth conservée quel que soit le connecteur : `Bearer` + `X-Tenant` sur `/broadcasting/auth`. Les canaux et hooks sont
**agnostiques du connecteur** (laravel-echo) → seul le bloc `new Echo({...})` diffère.

- **Web** (`core-app/src/echo.js`) : **double connecteur piloté par env** (le web se redéploie en continu → le toggle
  est sa commande de bascule).
  - `VITE_APP_BROADCASTER` = `socket.io` (défaut, echo-server) | `reverb` (pusher-js).
  - Mode reverb : `VITE_APP_REVERB_APP_KEY`, `VITE_APP_REVERB_HOST`, `VITE_APP_REVERB_PORT`, `VITE_APP_REVERB_SCHEME`.
    ⚠️ **`VITE_APP_REVERB_APP_SECRET` retiré du front** (jamais côté client).
- **Mobile** (`byplus/src/modules/messaging/realtime.ts`) : **Reverb PUR** (`pusher-js`), plus de socket.io.
  - `EXPO_PUBLIC_REVERB_KEY` (publique), `EXPO_PUBLIC_REVERB_WS_HOST` (**vide → dérivé du hostname de l'API au runtime**),
    `EXPO_PUBLIC_REVERB_WS_PORT=443`, `EXPO_PUBLIC_REVERB_SCHEME=https`.
  - `authEndpoint` **absolu** (`${host}/broadcasting/auth`), `disableStats:true`, `cluster:'mt1'` factice.
  - ⚠️ Ces `EXPO_PUBLIC_*` sont **bakés au build** → un changement = nouvelle release. Recommandé : Reverb **derrière le
    même domaine que l'API** (proxy `/app`) pour laisser `WS_HOST` **vide** et éviter tout figeage (seule la `KEY` compte).

---

## 7. Migration vers Laravel Reverb (transition SANS coupure)

Reverb parle le **protocole Pusher** (pas socket.io). Le socle est déjà compatible (events, canaux privés, auth tenant,
file). Stratégie retenue : **double-diffusion** — faire tourner **echo-server ET Reverb en parallèle** et diffuser
chaque event sur les deux, jusqu'à ce que toutes les apps mobiles soient passées à la version Reverb.

### Principe — double-diffusion pilotée par env
`BROADCAST_CONNECTIONS=redis,reverb` → chaque event chat/appel part **sur Redis** (→ echo-server, anciennes apps
socket.io) **ET sur Reverb** (→ nouvelles apps). Aucun redéploiement des events pour (dés)activer.

### Runbook
1. **Fondation backend (FAITE)** : clé `event_connections` + trait `App\Traits\BroadcastsToConfiguredConnections` +
   application à **TOUS les events `ShouldBroadcast`**. Défaut `redis` → **inerte**, aucun changement de comportement.
2. **Installer Reverb** : `composer require laravel/reverb` puis `php artisan reverb:install` (**garder
   `BROADCAST_CONNECTION=redis`**). Mettre les `REVERB_*` aux valeurs **prod** (host public, port, `REVERB_SCHEME=https`).
3. **Lancer Reverb + proxy** : `php artisan reverb:start` **sous supervisor/systemd, à côté d'echo-server**. Reverse-proxy
   nginx/Apache : router **WSS → Reverb** (port 8080) sur le chemin Pusher `/app`, **garder** `/socket.io`→6001.
4. **Activer la double-diffusion** : `BROADCAST_CONNECTIONS=redis,reverb` puis `php artisan config:cache`.
5. **Basculer les clients — ✅ CODE FAIT (cf. §6)** :
   - **Web** : connecteur piloté par `VITE_APP_BROADCASTER` → passer à `reverb` au (re)déploiement, une fois Reverb up.
   - **Mobile** : **Reverb PUR** (socket.io retiré) → **publier une nouvelle version d'app**. ⚠️ **Sans repli** :
     l'app ne fonctionne QUE si le serveur Reverb écoute + double-diffusion active. **ORDRE IMPÉRATIF** : faire les
     étapes 2-4 (Reverb up + `BROADCAST_CONNECTIONS=redis,reverb`) **AVANT** de publier cette version mobile. Les
     **anciennes apps** (socket.io) restent servies par echo-server tant que `redis` est dans la liste.
6. **Purger echo-server** (voir §8) : `BROADCAST_CONNECTIONS=reverb`, arrêter echo-server, supprimer
   `laravel-echo-server.json`.

### Points d'attention
- Ne lister **`reverb`** dans `BROADCAST_CONNECTIONS` **que** lorsque `laravel/reverb` est installé **et**
  `reverb:start` tourne (sinon la diffusion sur cette connexion échoue).
- **Whispers « typing »** : Reverb ne diffuse les client events que si **`enable_client_messages`** est activé côté
  serveur — sinon l'indicateur « écrit… » tombe silencieusement.
- **Clé / secret** : `REVERB_APP_SECRET` ne part **jamais** côté client (seule `APP_KEY` est publique). La `APP_KEY`
  étant publique, **fixe-la stable** — pas de rotation ; le SECRET, lui, se change **sans impact client** (l'auth des
  canaux est re-signée côté serveur). Si tu dois un jour changer la KEY, garde l'ancienne **valide côté Reverb**
  (config `apps` multi-clés) pendant la transition, sinon les apps bakées avec l'ancienne KEY perdent le temps réel.
- **Anciennes apps mobiles** : sans double-diffusion, elles perdent le temps réel au 1ᵉʳ plan (le REST + les push
  FCM/APNs continuent). La double-diffusion les préserve pendant toute la transition.

## 8. Critère de purge d'echo-server (savoir que tout le monde est sur Reverb)
1. **Signal principal** : nombre de **connexions actives sur echo-server**. Purge sûre quand il reste **≈ 0** sur une
   fenêtre glissante (ex. 2–4 semaines).
2. **Complément** : adoption par version (consoles App Store / Play Console) + header `X-App-Version` envoyé par la
   nouvelle app.
3. **Filet** : fixer une **date limite** puis un **gate de version minimale** (forced-update) pour les traînards avant de
   couper.

## 9. Vérification / dépannage
- Connexions actives du chat : `php artisan tinker` → `config('broadcasting.event_connections')`.
- **Worker Horizon actif** (`php artisan horizon:status`) — sinon aucun broadcast n'est livré.
- **Reverb côté client (piège fréquent)** : la connexion est un **WebSocket** (`wss://<domaine>/app/{key}`), **PAS** un
  appel API REST → **invisible** dans les logs XHR/« appels API ». Le seul appel HTTP est `POST /broadcasting/auth`, et
  seulement **après** que le WS soit connecté + un canal privé souscrit. Pour voir l'état : `Pusher.logToConsole = true`
  (web/mobile, en dev) → « Connecting / Connected / Error » ; ou les **logs de `reverb:start`** (connexions entrantes).
  Si l'app mobile (Reverb pur) ne voit rien : vérifier que `reverb:start` tourne et que le **proxy route `/app` → Reverb**.
- Test staging (2 clients web + mobile) : un `.private` reçoit, un **non-membre** obtient **403** sur
  `/broadcasting/auth`, présence + **typing** OK, les **appels sonnent** (`ChatCallEvent`).
- Après tout changement `.env` : `php artisan config:cache`.
