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

122 lines
6.0 KiB
Markdown

# 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)``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) :
```html
<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 `main``deploy.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-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).