Fix 20+ defects found by real Docker/PostgreSQL deployment

Première exécution réelle de la stack (build des images, PostgreSQL 16,
parcours fonctionnels en HTTP) : 28 tables, 105 index, extension pg_trgm,
et 181 assertions rejouées après correction.

Santé : filtres enum invalides renvoyaient 500 au lieu d'une erreur
française ; plan_line s'arrêtait à la fin de la fenêtre du graphique au
lieu de la date d'atteinte de l'objectif ; deficit_target_kcal était
recalculé après le plancher calorique ; « dernière pesée » affichait deux
valeurs différentes selon l'endpoint ; objectif protéines absent.

Vape : durée de vie moyenne des résistances incluait la résistance en
cours ; archiver la recette active la laissait active ; coût théorique
inventé avant la date d'arrêt ; économies projetées dans le futur ;
€/ml arrondi à 2 décimales écrasait le modèle de coût DIY.

Finances : le sankey compensait crédits et débits non catégorisés ;
rows_total excluait les lignes filtrées, faussant l'arithmétique du
rapport d'import.

Socle : les erreurs HTTP du framework fuitaient en anglais dans
l'enveloppe française ; nginx renvoyait sa page 413 HTML au lieu du JSON
français ; fins de ligne normalisées en LF.

348 tests pytest (+7), ruff, tsc et vite build au vert.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-14 11:40:05 +02:00
co-authored by Claude Opus 5
parent d32436db4c
commit fa3db7ff22
20 changed files with 408 additions and 78 deletions
+68 -33
View File
@@ -127,8 +127,9 @@ Détails utiles :
### Imports et connecteurs (`/imports`)
- Assistant en trois étapes : **Fichier → Vérification → Import**, avec détection
automatique du format à partir de l'en-tête du fichier.
- Assistant en trois étapes : **Fichier → Vérification → Import**. Le format est détecté à
partir de l'en-tête du fichier ; pour un relevé bancaire, le profil de banque est
présélectionné à partir de ce format et reste modifiable avant l'aperçu.
- Éditeur de mappage de colonnes (séparateur, encodage, format de date, colonnes) enregistré
dans un profil personnel.
- **Formats reconnus** : relevé bancaire CSV, relevé bancaire OFX, PayPal CSV,
@@ -150,14 +151,12 @@ révocation de clés d'API par appareil), **Application** (préférences locales
**Il n'y en a pas, et ce n'est pas un oubli.**
L'application a été développée et testée intégralement hors ligne : la suite de tests
backend et la compilation du frontend passent, mais **la pile Docker Compose n'a jamais été
démarrée** (le démon Docker était indisponible pendant tout le développement). Aucune
capture n'a donc pu être prise sur une instance réelle.
Plutôt que de publier des images de synthèse trompeuses, cette section restera vide jusqu'à
votre premier démarrage. Une fois l'application lancée, les pages à capturer en priorité
sont : le tableau de bord, `/sante/poids`, `/vape/economies` et `/finances`.
La pile a bien été construite et exécutée (voir « Ce qui est vérifié » ci-dessous), mais la
validation s'est faite **en HTTP, pas dans un navigateur** : aucune session graphique n'a
été ouverte, donc aucune capture authentique n'a pu être prise. Plutôt que de publier des
images de synthèse trompeuses, cette section restera vide jusqu'à votre premier démarrage.
Les pages à capturer en priorité : le tableau de bord, `/sante/poids`, `/vape/economies` et
`/finances`.
---
@@ -165,18 +164,32 @@ sont : le tableau de bord, `/sante/poids`, `/vape/economies` et `/finances`.
### Prérequis
- **Docker Desktop installé ET démarré.** Vérifiez que la baleine est bien active dans la
barre des tâches ; `docker compose version` doit répondre sans erreur.
- **Docker installé et démarré** ; `docker compose version` doit répondre sans erreur
(Compose v2 ou plus récent). Sous Windows, vérifiez que Docker Desktop est bien actif.
- Environ 2 Go d'espace disque pour les images et la base.
- Aucun autre service n'écoute sur le port choisi (80 par défaut).
- Aucun autre service n'écoute sur les ports choisis (80 et 8000 par défaut).
> ⚠️ **À lire avant le premier lancement.** Le démon Docker était **hors service pendant
> toute la phase de développement** : la pile Compose n'a donc jamais été construite ni
> démarrée. Le code applicatif, lui, est validé (292 tests backend, compilation frontend).
> Attendez-vous éventuellement à un petit ajustement lors du tout premier `up` — le plus
> probable étant un temps de build long, ou une image de base à retélécharger. Si un
> conteneur refuse de démarrer, consultez ses journaux avec
> `docker compose logs api` avant toute autre chose.
### Ce qui est vérifié
La séquence ci-dessous a été **exécutée telle quelle** sur un hôte Ubuntu (Docker Compose
v5.1.1, 8 vCPU, 1,8 Go de RAM), à partir d'un volume de données vide :
- les deux images se construisent (`lifetrack-api` ≈ 330 Mo, `lifetrack-web` ≈ 76 Mo) ;
- les trois conteneurs passent `healthy` en une vingtaine de secondes ;
- le schéma est créé sur un **vrai PostgreSQL 16** (28 tables, 105 index, extension
`pg_trgm` activée) et les données survivent à un `down` / `up` ;
- l'assistant de premier démarrage, puis un parcours par module (pesée + checklist du jour,
réglages vape + recharge + économies cumulées, import d'un relevé bancaire CSV en cp1252
avec ré-import dédupliqué et annulation) ont été rejoués **en HTTP à travers nginx** :
SPA, liens profonds, proxy `/api/` et gros fichiers compris.
Ce qui n'a **pas** été vérifié : le rendu graphique dans un navigateur (voir « Captures
d'écran »). Si un conteneur refuse de démarrer, commencez par
`docker compose logs api`.
> **Hôte avec moins de 2 Go de RAM** : construisez les images **une par une** (étape 3
> ci-dessous) et, si `vite build` se fait tuer par l'OOM killer, décommentez
> `NODE_OPTIONS=--max-old-space-size=1024` dans `.env`.
### 1. Créer le fichier `.env`
@@ -229,39 +242,59 @@ Deux variables restent commentées et ne servent qu'au développement :
> `LIFETRACK_DATABASE_URL` n'est **pas** à renseigner dans `.env` : `docker-compose.yml` la
> compose automatiquement à partir de `POSTGRES_USER`, `POSTGRES_PASSWORD` et `POSTGRES_DB`.
### 3. Lancer la pile
### 3. Construire les images
C'est la séquence exacte qui a été validée. Les deux builds sont **séparés** : le build web
(`npm ci` + `vite build`) est le seul gros consommateur de mémoire, mieux vaut ne pas le
faire tourner en même temps qu'autre chose.
```bash
docker compose up -d --build
docker compose build api # ~30 s
docker compose build web # ~40 s
```
`docker compose build` (sans argument) fonctionne aussi et construit les deux à la suite.
### 4. Lancer la pile
```bash
docker compose up -d
```
Trois conteneurs démarrent : `postgres` (base de données), `api` (FastAPI) et `web`
(nginx servant le frontend compilé et relayant `/api/` vers l'API). Le premier build prend
plusieurs minutes.
(nginx servant le frontend compilé et relayant `/api/` vers l'API). Depuis un volume vide,
il faut une vingtaine de secondes pour que les trois soient `healthy`.
Suivre le démarrage :
Vérifier :
```bash
docker compose logs -f
docker compose ps # attendre (healthy) sur postgres, api et web
docker compose logs -f # au besoin, suivre le démarrage
```
### 4. Ouvrir l'application
### 5. Ouvrir l'application
<http://localhost>
Si vous avez changé `WEB_PORT`, adaptez l'adresse (par exemple `http://localhost:8080`).
Depuis un autre appareil du réseau local, utilisez l'adresse IP du serveur.
Depuis un autre appareil du réseau local, utilisez l'adresse IP du serveur. L'assistant de
premier démarrage s'ouvre automatiquement et crée le compte administrateur.
Deux adresses utiles :
- <http://localhost/api/healthz> — doit répondre `{"status":"ok"}`
- <http://localhost/api/docs> — documentation interactive de l'API
> `docker-compose.yml` porte `name: lifetrack` : toutes les commandes ci-dessus sont donc
> déjà rattachées au projet `lifetrack`, sans avoir à passer `-p`.
### Arrêter, mettre à jour, repartir de zéro
```bash
docker compose down # arrêt, les données sont conservées
docker compose up -d --build # reconstruction après mise à jour du code
docker compose build api # après mise à jour du code
docker compose build web
docker compose up -d # …puis redémarrage
docker compose down -v # ⚠️ supprime AUSSI le volume : toutes vos données
```
@@ -317,7 +350,7 @@ L'environnement virtuel Python est déjà présent dans `apps/api/.venv`.
```bash
cd apps/api
# Suite de tests — 292 tests, exécutés sur SQLite en mémoire
# Suite de tests — 348 tests, exécutés sur SQLite en mémoire
.venv/Scripts/python.exe -m pytest
# Lint et formatage
@@ -341,7 +374,9 @@ python -m venv .venv
> Les tests tournent sur **SQLite en mémoire** alors que la production utilise
> **PostgreSQL 16** : tous les types de colonnes doivent rester portables (voir
> [CONVENTIONS.md](CONVENTIONS.md) §C8).
> [CONVENTIONS.md](CONVENTIONS.md) §C8). `app/tests/test_postgres_ddl.py` vérifie hors
> ligne le DDL réellement émis pour PostgreSQL (types natifs, index partiels, cascades
> d'import) ; ce DDL a par ailleurs été confronté à un vrai serveur PostgreSQL 16.
### Frontend (React + Vite)
@@ -388,7 +423,7 @@ LifeTrack/
│ │ │ ├─ core/ # config, base, sécurité, pagination, erreurs,
│ │ │ │ # framework d'import et d'ingestion, chargeur de modules
│ │ │ ├─ modules/ # auth, health, vape, finance, imports
│ │ │ ├─ tests/ # 292 tests pytest
│ │ │ ├─ tests/ # 348 tests pytest
│ │ │ └─ main.py # création de l'app — ne jamais modifier pour un module
│ │ ├─ requirements.txt
│ │ └─ openapi.json # schéma OpenAPI exporté
@@ -478,7 +513,7 @@ utilisateur.
| Base de données | PostgreSQL 16 (pilote psycopg 3) — SQLite en mémoire pour les tests |
| Frontend | React 18 · TypeScript strict · Vite 6 · TailwindCSS · Apache ECharts · TanStack Query · React Router |
| Déploiement | Docker Compose : `postgres` + `api` + `web` (nginx 1.27) |
| Qualité | pytest (292 tests) · ruff · `tsc --noEmit` |
| Qualité | pytest (348 tests) · ruff · `tsc --noEmit` |
Aucune ressource externe n'est chargée à l'exécution : polices, icônes et bibliothèques sont
embarquées dans le build.