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>
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 5d60a40 → 678b5a4).
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) où 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 bienregisterPlugin. 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)
- App iOS (sim ou device) sur
/live, page au premier plan. - App watch → bouton Test (FC simulée).
/livedoit 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 systemdcoach-web - Staging :
/home/ubuntu/coach_sportif_staging, servicecoach-web-staging - Auto-deploy : webhook Gitea sur push
main→deploy.sh(git reset --hard origin/main+pip install -r requirements.txt+ restartcoach-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-tab → main (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).