Files
coach-ios/HANDOFF-LIVE-BACKEND.md
Sylvain b92e4d5ce6 docs: point 1 (/live nav) DÉPLOYÉ en prod (coach_sportif ecf334a)
Onglet "Live workout" -> /live ajouté dans NAV_ITEMS, mergé sur main (ff) et
déployé via webhook (service coach-web actif). /live était déjà OK côté
route/auth/template (Phase 4) ; manquait juste le lien nav. "token invalide"
= accès /live non authentifié (pas de lien -> redirect login).

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

6.2 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ÉPLOYÉ EN PROD (2026-05-25) : onglet nav « Live workout » → /live ajouté dans NAV_ITEMS (la route/auth/template existaient déjà — Phase 4 — mais /live n'était lié nulle part → page inatteignable → « token invalide » sur le redirect /login). Commit ecf334a mergé sur main (fast-forward) + déployé via webhook Gitea (service coach-web actif). Validé sur le VPS : import OK, onglet présent. L'onglet apparaît dans le menu « Plus » de la tab bar (NAV_DEFAULT_TABS ne montre que home/calendar/workouts/meals par défaut).

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).