V0.3.0 — UI connexion, Administration et Live vidéo
Publiée le 01/10/2026.
Documentation fonctionnelle
Ce que change la version pour les collaborateurs
Pourquoi
L'API du Site existait depuis la 0.2.0, mais l'Administrateur n'avait que curl ou /api/docs pour s'en servir, et ne pouvait regarder aucune Caméra : un navigateur ne lit pas le RTSP, et MediaMTX ne relayait que trois chemins simulés figés.
Ce qu'on peut faire maintenant
- Ouvrir l'UI sur
http://\<hôte>:8080(conteneurweblancé par le mêmedocker compose), depuis la machine ou le réseau local. - Se connecter, être renvoyé sur la connexion quand la session n'est plus valide puis ramené à la page demandée, se déconnecter.
- Administration : voir toutes les Caméras et leur état (rafraîchi toutes les 10 s), en créer, les modifier sans ressaisir le mot de passe RTSP masqué, les désactiver / réactiver, supprimer une Caméra désactivée après confirmation ; les refus de l'API (doublon, URL invalide) s'affichent dans le formulaire.
- Live : regarder en WebRTC (moins d'une seconde de retard) le Flux d'une Caméra active à la fois, passer à la suivante (bouton Suivant ou flèche droite), avec « Flux indisponible » et nouvelle tentative automatique si la vidéo ne vient pas.
- MediaMTX en pont : toute Caméra active, simulée ou réelle, devient lisible dans le navigateur sans configurer MediaMTX à la main ; l'API la relaie sur
camera-\<id>, sans réencodage, tirée seulement quand quelqu'un regarde. - L'API n'accepte les appels avec cookie que depuis les origines de
ARGOS_ORIGINES_AUTORISEES(CORS avec credentials).
Documentation technique
Spec versionnée : docs/specs/v0.3.0-ui-connexion-administration-live.md
Issue parente : #12
V0.3.0 — UI connexion, Administration et Live vidéo
Problem Statement
L'API du Site existe (0.2.0), mais l'Administrateur n'a qu'un curl ou /api/docs pour s'en servir : pas d'écran pour se connecter, gérer les Caméras ou voir leur état. Surtout, il ne peut regarder aucune Caméra : un navigateur ne lit pas le RTSP, et MediaMTX ne relaie que trois chemins simulés figés, sans lien avec les Caméras déclarées au Site.
Solution
Un frontend React servi par un nouveau conteneur web, lancé par le même docker compose. L'Administrateur se connecte, gère ses Caméras dans l'onglet Administration, et regarde dans l'onglet Live le Flux d'une Caméra active à la fois, en WebRTC, en passant à la suivante d'un clic. Pour cela, l'API fait de MediaMTX un pont : elle réconcilie en continu les Caméras actives avec des chemins MediaMTX camera-\<id> qui relaient leur URL RTSP, sans réencodage.
User Stories
Stack et accès
- En tant que développeur, je veux que
docker compose up -d --waitlance aussi le frontend, pour tout démarrer d'une commande. - En tant qu'Administrateur, je veux ouvrir l'UI sur
http://\<hôte>:8080depuis la machine ou le réseau local, pour gérer le Site depuis un navigateur. - En tant que développeur, je veux lancer le frontend en mode dev (
npm run dev, port 5173) contre la stack, pour itérer sans reconstruire l'image. - En tant qu'Administrateur, je veux que l'API n'accepte les appels avec cookie que depuis les origines listées dans
.env(ARGOS_ORIGINES_AUTORISEES), pour qu'un autre site ne puisse pas piloter le Site avec ma session. - En tant qu'Administrateur, je veux que le même build fonctionne via
localhostou l'IP du réseau local, pour ne pas reconstruire selon l'adresse.
Connexion
- En tant qu'Administrateur, je veux une page de connexion (identifiant, mot de passe), pour accéder au Site.
- En tant qu'Administrateur, je veux un message clair si mes identifiants sont faux, sans dire lequel, pour corriger ma saisie.
- En tant qu'Administrateur, je veux un message « Trop de tentatives, réessayez dans 15 min » sur un
429, pour comprendre le blocage. - En tant qu'Administrateur, je veux être envoyé sur la connexion dès que ma session n'est plus valide (absente, expirée, mot de passe changé), puis ramené à la page demandée, pour ne pas rester sur un écran cassé.
- En tant qu'Administrateur, je veux un bouton Déconnexion dans l'en-tête, pour fermer ma session.
Administration
- En tant qu'Administrateur, je veux voir toutes les Caméras (nom, emplacement, hôte:port, URL masquée, active ou désactivée, état, dernière vérification), pour avoir la vue d'ensemble du Site.
- En tant qu'Administrateur, je veux que la liste se rafraîchisse toute seule toutes les 10 s, pour suivre les états sans recharger.
- En tant qu'Administrateur, je veux créer une Caméra (nom, URL RTSP, emplacement facultatif), pour que le Site la connaisse.
- En tant qu'Administrateur, je veux modifier le nom, l'URL ou l'emplacement d'une Caméra sans ressaisir le mot de passe RTSP masqué, pour corriger une erreur.
- En tant qu'Administrateur, je veux voir dans le formulaire pourquoi une saisie est refusée (
409doublon,422URL invalide), pour la corriger. - En tant qu'Administrateur, je veux désactiver et réactiver une Caméra, pour arrêter ou reprendre sa surveillance.
- En tant qu'Administrateur, je veux supprimer une Caméra désactivée après confirmation, et ne pas pouvoir supprimer une Caméra active, pour éviter les accidents.
Live
- En tant qu'Administrateur, je veux voir en grand le Flux vidéo d'une Caméra active, avec moins d'une seconde de retard, pour regarder ce qui se passe.
- En tant qu'Administrateur, je veux voir au-dessus de la vidéo le nom, l'emplacement et l'état de la Caméra (rafraîchi toutes les 10 s), pour savoir ce que je regarde.
- En tant qu'Administrateur, je veux passer à la Caméra active suivante (bouton Suivant ou flèche droite), dans l'ordre des noms, en revenant à la première après la dernière, pour faire le tour du Site.
- En tant qu'Administrateur, je veux un message avec un lien vers Administration quand aucune Caméra n'est active, pour savoir quoi faire.
- En tant qu'Administrateur, je veux voir « Flux indisponible » quand la vidéo ne vient pas ou s'interrompt, avec une nouvelle tentative toutes les 5 s, pour que le Live reprenne seul.
- En tant qu'Administrateur, je veux que changer de Caméra ferme la connexion vidéo précédente, pour ne pas tirer plusieurs Flux à la fois.
MediaMTX en pont
- En tant qu'Administrateur, je veux que toute Caméra active, simulée ou réelle, devienne lisible dans le navigateur sans configuration manuelle de MediaMTX, pour ajouter une Caméra depuis l'UI seulement.
- En tant qu'Administrateur, je veux qu'une Caméra désactivée ou supprimée ne soit plus relayée, pour ne pas exposer un Flux que je ne surveille plus.
- En tant qu'Administrateur, je veux que le Flux d'une Caméra ne soit tiré que quand quelqu'un la regarde, pour ne pas charger la caméra et le réseau inutilement.
- En tant qu'Administrateur, je veux que créer ou modifier une Caméra fonctionne même si MediaMTX est momentanément indisponible, le relais se rattrapant ensuite, pour que l'Administration ne dépende pas du relais.
- En tant que développeur, je veux que la représentation d'une Caméra expose
chemin_flux(camera-\<id>,nullsi désactivée), pour que le front sache quel Flux lire. - En tant que développeur, je veux qu'une Caméra déclarée sur
rtsp://mediamtx:8554/cam1soit relue surrtsp://localhost:8554/camera-\<id>, pour vérifier le pont sans matériel.
Documentation
- En tant que développeur, je veux que le glossaire définisse Live, pour parler le même langage.
- En tant que développeur, je veux une ADR « MediaMTX en pont », pour savoir pourquoi l'API pilote MediaMTX plutôt que de servir seulement les Caméras simulées.
- En tant que développeur, je veux que
docs/securite.mdcouvre le CORS avec credentials (et la limite « same-site »), l'API de contrôle MediaMTX non publiée et les cheminscamera-\<id>lisibles sans auth sur le réseau local, pour ne pas oublier ces limites. - En tant que développeur, je veux que la feuille de route décrive la 0.3.0 livrée (vidéo WebRTC incluse) et marque la 1.0.0 « à redéfinir », pour garder le plan juste.
- En tant que nouveau venu, je veux que le README explique comment ouvrir l'UI, lancer le front en dev, installer Playwright pour les tests, déclarer les Caméras simulées depuis l'UI, et la nouvelle structure.
Implementation Decisions
- Frontend : React + Vite + TypeScript dans
web/. react-router :/connexion, et une zone connectée à deux onglets/administrationet/live. Pas de bibliothèque de composants ; CSS simple. UI en français uniquement. Node épinglé dans l'image. - Service
web: build multi-étapes (Node → nginx) qui sert seulement les fichiers statiques (pas de proxy/api), port 8080 publié. Dev : Vite sur 5173. - Deux origines : le front appelle l'API sur le même hôte que la page, port 8000 (configurable), avec
credentials: 'include'. Le cookieSameSite=Laxpasse (le port ne change pas le « site »). - CORS dans l'API : credentials autorisés, origines exactes lues dans
ARGOS_ORIGINES_AUTORISEES(liste séparée par des virgules, défauthttp://localhost:8080,http://localhost:5173), jamais*. Documenté dans.env.example. - Session côté UI :
GET /api/moiau chargement ; tout401→/connexionpuis retour à la page demandée ; Déconnexion =DELETE /api/session;429→ message dédié. - Administration : couvre tout le contrat 0.2.0 (liste, création,
PATCHavec URL masquée renvoyée telle quelle,activevrai/faux, suppression d'une désactivée avec confirmation). Rafraîchissement de la liste toutes les 10 s. - Live : Caméras actives triées par nom, Suivant + flèche droite, retour circulaire, message vide avec lien vers Administration. Lecture WebRTC uniquement, par un client WHEP écrit dans le front (
POST http://\<hôte>:8889/\<chemin_flux>/whep, puis\<video>), pas d'iframe, pas de HLS. Échec de négociation ou coupure → « Flux indisponible », nouvelle tentative toutes les 5 s. Changement de Caméra → fermeture de la connexion WebRTC précédente. Badge d'état rafraîchi toutes les 10 s. - MediaMTX en pont (ADR 0001) : l'API réconcilie les Caméras actives avec les chemins MediaMTX
camera-\<id>(source =url_rtsp,sourceOnDemand), au démarrage puis toutes les 10 s dans sa boucle de fond : ajoute les manquants, met à jour une URL changée, retire les cheminscamera-*sans Caméra active. Elle ne touche pas aux autres chemins. Via l'API de contrôle MediaMTX (port 9997, activée, non publiée, joignable seulement par le réseau Compose). Un échec de MediaMTX ne fait jamais échouer le CRUD. Module de réconciliation à interface étroite (Caméras actives voulues en entrée, chemins MediaMTX ajustés en sortie). - Représentation d'une Caméra : nouveau champ
chemin_flux(camera-\<id>,nullsi désactivée). - Config MediaMTX :
runOnInit(ffmpeg en boucle) déplacé depathDefaultsverscam1–cam3seulement, qui restent les sources simulées ; API de contrôle activée. - Sonde d'état inchangée : elle sonde
url_rtspdirectement, pas le relais.online+ Live en échec désigne donc le relais. - Pas de réencodage : MediaMTX remballe le H.264 en WebRTC. Autres codecs hors scope.
- Glossaire : Live : la vue qui montre le Flux d'une seule Caméra active à la fois, avec passage à la suivante. Avoid : mur d'images, mosaïque, visionneuse. « Administration » reste un nom d'onglet, pas un terme du glossaire.
- ADR 0001 — MediaMTX en pont : alternative écartée : servir seulement les chemins des Caméras simulées (à refaire à la première vraie caméra) ; synchro à chaque opération CRUD écartée au profit de la réconciliation.
- Document de sécurité : CORS avec credentials et limite « same-site » (une autre application sur le même hôte est same-site) ; API de contrôle MediaMTX non publiée ; chemins
camera-\<id>lisibles sans auth par tout poste du réseau local (extension de la dette « Flux sans auth »). - Feuille de route : section 0.3.0 réécrite (UI + vidéo WebRTC via MediaMTX en pont) ; 1.0.0 conservée avec la mention « à redéfinir : la vidéo arrive en 0.3.0 ».
- Rôle
test-roots: ajout detests/web/.
Testing Decisions
tests/api/(HTTP contre la stack), prior art : tests Caméras 0.2.0 etffprobedetests/flux/. Couvre :chemin_flux(valeur,nullsi désactivée) ; une Caméra surrtsp://mediamtx:8554/cam1devient lisible auffprobesurrtsp://localhost:8554/\<chemin_flux>après réconciliation (attente bornée) ; le chemin disparaît après désactivation ; CORS : origine autorisée → en-têtes CORS avec credentials, autre origine → pas d'autorisation.tests/web/(nouvelle racine, pytest-playwright, Chromium headless sur l'hôte, contrehttp://localhost:8080) : connexion réussie et ratée ; redirection vers/connexionsans session ; CRUD dans Administration (création, erreur409visible, modification, désactivation, suppression) ; Live où la\<video>joue vraiment (currentTimequi augmente) ; Suivant change de Caméra ; Déconnexion.- Mêmes règles qu'en 0.2.0 : jamais d'effacement de la base, noms uniques par test puis désactivation et suppression ; identifiants lus dans
.env, échec immédiat avec message s'ils manquent. - Un bon test vérifie un comportement observable (écran, vidéo qui avance, Flux lisible), pas la structure des composants ni les appels à l'API de contrôle MediaMTX.
- Pas de tests de composants (Vitest) sauf logique pure qui le justifie.
/tlance toutes les racines detest-roots.
Out of Scope
- HLS, iframe MediaMTX, codecs autres que H.264, réencodage.
- HTTPS, cookie
Secure, authentification des Flux MediaMTX, chiffrement des identifiants RTSP, accès distant / VPN. - Plusieurs comptes, rôles.
- Mosaïque / mur d'images, plein écran dédié, précédent, sélection directe d'une Caméra dans le Live.
- Sonde via le relais, historique des états, alertes, détection de Flux figé ou rejoué.
- Enregistrement, IA, ONVIF, PTZ.
Further Notes
- WebRTC demande l'UDP 8189 joignable ; sur le réseau local,
MTX_WEBRTCADDITIONALHOSTS(déjà dans.env.example) doit contenir l'IP de la machine. - Une Caméra créée peut mettre jusqu'à un tour de réconciliation (10 s) avant d'être lisible ; le Live réessaie seul.
- Contexte : feuille de route de dev (section 0.3.0), spec 0.2.0,
docs/securite.md.