Files
coach-ios/HANDOFF-LIVE-BACKEND.md
Sylvain 5bb979d9bc docs(handoff): état fix /live (branche feat/live-nav-tab) + infra déploiement VPS
Branche poussée, infra (VPS ubuntu@83.228.246.229, prod/staging, make deploy*,
auto-deploy webhook), et étapes pour merger sur main depuis l'env Linux/VPS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 20:47:49 +02:00

6.0 KiB

Handoff — Backend /live (Mac → session Linux coach_sportif)

De : Claude Code Mac mini, 2026-05-25 (chantier watchOS Live Workout). Pour : session Claude Code sur le repo coach_sportif (backend FastAPI + templates). Objet : finir le point 1 — la page /live doit s'afficher dans l'app et recevoir les samples temps réel poussés par la Watch.

Contexte : le natif est TERMINÉ et PROUVÉ

Phases 1-3 (target watchOS CoachWatch + 4 fichiers Swift + plugin Capacitor CoachLiveBridge) livrées sur coach-ios (commits 5d60a40678b5a4).

Pont watch → iPhone validé end-to-end sur simulateur (paire iPhone+Watch) : les logs montrent chaque sample envoyé par la Watch reçu à l'identique par le plugin iPhone (HR 130.69 → 148.32, latence ~300 ms). Donc dès que /live s'affiche dans l'app et écoute l'event, les chiffres défileront.

Un mode Test (FC simulée) existe dans l'app watch (bouton sous les activités) : il envoie des samples synthétiques sans HealthKit. → tu peux valider /live sans dépendre de la vraie FC (la Santé de la Watch physique de Sylvain a un bug système séparé, hors scope ici).

Le problème observé

En ouvrant /live dans l'app, la page renvoie « token invalide ». → La route /live ne reconnaît pas la session authentifiée de l'app.

Rappel : l'app (wrapper Capacitor) s'authentifie via injection de cookie de session dans AppDelegate (coach-ios). Toutes les pages authentifiées marchent via ce cookie. La route /live (Phase 4) avait été écrite avec check_auth(request, token)token venait d'un ?token= — ce qui ne matche pas le flux cookie de l'app.

À faire côté coach_sportif

1. Auth de /live = même mécanisme que les autres pages authentifiées

Faire en sorte que /live accepte la session/cookie de l'app (comme les autres routes protégées), ?token= restant un fallback optionnel. Concrètement : utiliser le même Depends/helper d'auth que les autres pages, au lieu d'exiger le token en query. Objectif : ouvrir /live dans l'app authentifiée → 200, pas « token invalide ».

2. Un accès à /live depuis l'UI web

Ajouter un lien/bouton vers /live dans la nav de l'app (là où c'est pertinent — ex. page séance / accueil), pour que l'utilisateur y arrive sans taper d'URL.

3. JS du template live_workout.html — contrat EXACT du plugin natif

Le plugin iPhone émet l'event liveSample (jsName du plugin : CoachLiveBridge). Champs d'un sample :

champ type sens
heartRate number FC en bpm
activeEnergyKcal number calories actives cumulées
distanceMeters number distance cumulée en mètres
elapsedSec number durée écoulée en secondes
ts number timestamp epoch (secondes, fractionnaire)
sim bool présent (=true) uniquement en mode Test

JS à mettre dans le template (page chargée DANS l'app Capacitor) :

<script type="module">
import { registerPlugin } from '/static/vendor/capacitor-core.js';
const CoachLiveBridge = registerPlugin('CoachLiveBridge');
let lastTs = 0;
CoachLiveBridge.addListener('liveSample', (s) => {
  document.getElementById('live-hr').textContent      = Math.round(s.heartRate || 0);
  document.getElementById('live-kcal').textContent    = Math.round(s.activeEnergyKcal || 0);
  document.getElementById('live-dist').textContent    = ((s.distanceMeters || 0) / 1000).toFixed(2);
  document.getElementById('live-elapsed').textContent = fmtMS(s.elapsedSec || 0);
  document.getElementById('live-status').textContent  = 'En direct';
  lastTs = Date.now();
});
function fmtMS(sec){const h=Math.floor(sec/3600),m=Math.floor((sec%3600)/60),x=Math.floor(sec%60);
  return h?`${h}h${String(m).padStart(2,'0')}`:`${m}:${String(x).padStart(2,'0')}`;}
setInterval(()=>{ if (Date.now()-lastTs>8000)
  document.getElementById('live-status').textContent='Connexion perdue avec la Watch'; },1000);
</script>

Vérifier que /static/vendor/capacitor-core.js (ou équivalent) expose bien registerPlugin. En navigateur pur (hors app) il n'y a aucun WCSession donc 0 sample — c'est normal, la page n'est utile que dans le wrapper iOS.

Test d'acceptation (sans device, sur simulateur)

  1. App iOS (sim ou device) sur /live, page au premier plan.
  2. App watch → bouton Test (FC simulée).
  3. /live doit afficher FC/calories/distance/durée qui défilent (~ toutes les 2 s) et le statut « En direct ».

Côté natif c'est déjà prouvé (plugin CoachLiveBridge logue liveSample reçu de la Watch hr=…). Si /live ne bouge pas alors que ce log apparaît, c'est le JS/registerPlugin du template à corriger.

État du fix + déploiement (VPS)

Fix point 1 déjà écrit : branche feat/live-nav-tab sur coach_sportif (ajout d'un onglet nav « Live workout » → /live dans NAV_ITEMS, car la route existait mais n'était liée nulle part → page inatteignable → « token invalide » sur redirect /login). Commit ecf334a. PAS mergé sur main (pas de deploy prod à l'aveugle).

Infra (depuis Makefile / deploy.sh) :

  • VPS : ubuntu@83.228.246.229
  • Prod : /home/ubuntu/coach_sportif, service systemd coach-web
  • Staging : /home/ubuntu/coach_sportif_staging, service coach-web-staging
  • Auto-deploy : webhook Gitea sur push maindeploy.sh (git reset --hard origin/main + pip install -r requirements.txt + restart coach-web + healthcheck)
  • Targets : make test, make deploy-staging (sans risque), make deploy (prod)

Pour finir : sur l'env Linux/VPS (Python ≥ 3.11, le venv ne se monte pas sur le Mac : anyio==4.13.0), lancer make test, valider via make deploy-staging, puis merger feat/live-nav-tabmain (auto-deploy prod).

Hors scope de ce handoff

  • Vraie FC sur la Watch physique de Sylvain (bug système Santé de SA montre — NanoSettings crashe, à régler par désappairage/réappairage).
  • TestFlight (signing/App Store Connect côté Sylvain).