Auth headless durable via storage_state + refresh navigateur (homelab-ready)

- capture_token s'appuie sur storage_state.json (cookies ~60j) en new_context :
  fonctionne headless, la SPA rafraîchit le token (contourne l'anti-bot OAuth)
- session 'roule' (storage_state ré-exporté à chaque refresh) ; access token 30min,
  refresh token 60j
- goto en domcontentloaded + attente (networkidle ne se déclenche jamais sur la SPA)
- Dockerfile/Playwright alignés en 1.60.0 (chromium préinstallé) ; doc déploiement maj :
  session créée via attach_capture (login direct Playwright bloqué par anti-bot)
This commit is contained in:
2026-06-15 23:08:09 +02:00
parent 051ecb50d8
commit 5d3899fdfb
4 changed files with 104 additions and 65 deletions

View File

@@ -9,24 +9,27 @@ Client MCP visé : **Hermes** (Nous Research), qui tourne sur le même homelab.
> ⚠️ HelloFresh n'a pas d'API publique. AntiCoco s'appuie sur l'API interne `gw/` du site
> (non documentée, susceptible de changer) — **usage strictement personnel**.
## État (testé en local, 2026-06)
## État (validé de bout en bout, 2026-06)
**Lecture validée sur le menu réel** : login Playwright + token, menu de la semaine
(`menus-service/menus`) → détails en 1 appel batch (`recipes/recipes?ids=…`), filtrage coco
correct (4 recettes coco détectées sur 85, faux positifs des tags internes neutralisés),
scoring par préférences, gestion de la liste d'exclusion. Serveur MCP fonctionnel (handshake OK).
**Boucle complète testée sur le vrai compte** : lecture du menu (`menus-service/menus`
détails batch `recipes/recipes?ids=…`), filtrage coco (4/85 détectées, faux positifs des tags
internes neutralisés), proposition classée, et **écriture réelle réussie** (`PUT /v1/carts/{week}`,
HTTP 200) — sélection par index de course, ids de compte dérivés dynamiquement.
**Écriture (`hf_confirm_selection`) à finaliser** : l'endpoint d'enregistrement de la
sélection (`set_selection`) n'a pas pu être capturé (compte de test sans abonnement actif).
À découvrir via `discover_api.py` sur un compte avec une box modifiable (changer une recette
pour observer l'appel `PUT`/`POST`), puis renseigner `config/endpoints.json`.
**Auth headless durable** : le token (30 min) est rafraîchi par un navigateur headless chargé
avec la session (`storage_state.json`, refresh ~60 j) — contourne la protection anti-bot des
endpoints OAuth. Aucune intervention pendant ~60 j ; la session « roule » à chaque refresh.
> ⚠️ La connexion **directe** automatisée (Playwright/Chromium qui remplit le formulaire) est
> bloquée par l'anti-bot HelloFresh. La session se crée donc via **attache CDP à ton vrai Chrome**
> (`tools/attach_capture.py`), où le login marche normalement.
## Architecture
```
Hermes ──HTTP──▶ server.py (FastMCP, :9200/mcp)
├─ hellofresh/auth.py login Playwright + capture du bearer token
├─ hellofresh/api.py appels httpx vers le gateway HelloFresh
├─ hellofresh/auth.py session storage_state + refresh token headless
├─ hellofresh/api.py httpx : menu, détails, deliveries, PUT cart
├─ hellofresh/filter.py exclusion (coco !) + scoring préférences
└─ config/ excludes.json · prefs.json · endpoints.json
```
@@ -37,36 +40,39 @@ Hermes ──HTTP──▶ server.py (FastMCP, :9200/mcp)
```bash
pip install -r requirements.txt
playwright install chromium
cp .env.example .env # remplir HF_EMAIL / HF_PASSWORD (optionnels, fallback re-login)
cp .env.example .env
```
### 2. Découvrir les endpoints (étape 0, en local, fenêtre visible)
### 2. Créer la session (login via TON Chrome, anti-bot contourné)
Lance ton Chrome avec un port de debug + profil dédié (ta fenêtre Chrome habituelle peut rester
ouverte), connecte-toi à HelloFresh (email + mot de passe), puis attache la capture :
```bash
ANTICOCO_HEADLESS=0 python tools/discover_api.py
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
--remote-debugging-port=9222 --user-data-dir="$HOME/.hf-chrome-debug" \
https://www.hellofresh.fr/my-account/deliveries/menu
python tools/attach_capture.py # capture trafic + exporte .session/storage_state.json
```
Connecte-toi, ouvre le menu de la semaine, change une recette pour capturer l'écriture, puis
Entrée. Vérifie/complète ensuite `config/endpoints.json` (généré depuis le trafic capturé,
voir aussi `.session/discovery_log.json`).
`storage_state.json` (cookies, ~60 j) est la session réutilisable. `config/endpoints.json` est
déjà rempli ; rejoue `attach_capture` si l'API change (cf. `config/endpoints_discovered.json`).
### 3. Tester en local
### 3. Tester en local (headless, comme le homelab)
```bash
ANTICOCO_HEADLESS=0 python server.py # 1er run : login dans la fenêtre si besoin
python server.py # auth via storage_state, refresh token automatique
```
Le login crée `.session/profile` (profil Playwright persistant) — réutilisé ensuite headless.
## Déploiement homelab (Docker)
`.session/` et `.env` ne sont **jamais** versionnés. Workflow :
```bash
# 1. Sur le Mac : générer une session connectée (fenêtre visible)
ANTICOCO_HEADLESS=0 python server.py # se connecter, puis Ctrl-C
# 1. Sur le Mac : générer la session (cf. « Mise en route » §2 → .session/storage_state.json)
# 2. Pousser le code
git add -A && git commit -m "..." && git push
# 3. Synchroniser la session vers le homelab (NON versionnée ; endpoints.json est dans git)
scp -r .session jerem@192.168.0.43:<path>/AntiCoco/
# storage_state.json suffit (le homelab tourne headless et rafraîchit le token tout seul).
scp .session/storage_state.json jerem@192.168.0.43:<path>/AntiCoco/.session/
# 4. Sur le homelab : déployer
ssh homelab