MeeJayandClaude Opus 5 93f0689c1e Initial import: LifeTrack v1 (santé, vape, finances)
Tracker de vie auto-hébergé : suivi poids/calories/sport avec planning de
pesées, sevrage tabac (vape) avec modèle de coût DIY et économies, et
finances personnelles avec import de relevés bancaires.

Architecture : FastAPI + SQLAlchemy 2.0 + PostgreSQL 16, React 18 + TS +
Vite + Tailwind + ECharts, déploiement Docker Compose. Modules
auto-découverts des deux côtés (pkgutil / import.meta.glob) et framework
de connecteurs à deux voies (importeurs de fichiers + ingestion JSON)
pour brancher de nouvelles sources sans toucher au noyau.

Validé : 292 tests pytest, tsc + vite build, contrat API/web vérifié
contre le schéma OpenAPI, et déploiement Docker réel sur PostgreSQL 16
(28 tables, SPA servie par nginx, wizard de premier démarrage).

Documentation : README.md, docs/GUIDE.md, CONVENTIONS.md, et les
documents de conception et de recherche dans docs/.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 10:48:57 +02:00

LifeTrack

Tracker de vie auto-hébergé : poids et calories, sevrage tabagique (vape) et finances personnelles — le tout dans une interface web sombre et riche en graphiques.

Vos données restent chez vous, dans votre PostgreSQL. Aucun compte tiers, aucune télémétrie, aucune ressource chargée depuis Internet au moment de l'affichage.

Une seule dépendance réseau existe côté serveur, et elle est facultative : la recherche d'aliments interroge Open Food Facts (base publique, sans clé d'API) et met chaque produit consulté en cache local. Tout le reste fonctionne hors ligne.


Sommaire


À quoi sert LifeTrack

Trois besoins réunis dans une seule application, parce qu'ils s'alimentent mutuellement :

  1. Perdre du poids sans se tromper de chiffres. LifeTrack calcule votre métabolisme de base, votre dépense réelle et votre budget calorique quotidien, lisse vos pesées en une tendance, et projette la date d'atteinte de votre objectif.
  2. Tenir l'arrêt de la cigarette. Le module vape chiffre au centime près ce que vous dépensez en e-liquide et en résistances, et le compare à ce que le tabac vous coûterait aujourd'hui.
  3. Savoir où part l'argent. Les relevés bancaires s'importent en quelques clics, se catégorisent tout seuls et alimentent budgets, récurrents et graphiques.

L'application est mono-utilisateur en pratique : le premier compte créé est le vôtre, il n'y a pas d'inscription publique.


Fonctionnalités par module

L'interface est découpée en modules auto-découverts. Voici ce qui existe réellement aujourd'hui.

Tableau de bord (/)

  • Carte « Aujourd'hui » : ce qui est planifié le jour même (pesée, séance, journal alimentaire), fait ou non, avec les séries en cours.
  • Ajout rapide d'une pesée sans quitter la page.
  • Widgets KPI et graphiques de synthèse tirés des modules installés.

Santé (/sante/...)

Page Contenu
Poids & Objectif (/sante/poids) Historique des pesées, tendance lissée (EMA), projection de la date d'atteinte, mensurations, calendrier d'assiduité
Nutrition (/sante/nutrition) Journal alimentaire par repas, recherche d'aliments, favoris, aliments récents, suivi de l'hydratation
Activité & Sport (/sante/activite) Activité quotidienne (pas, distance, calories, minutes actives, étages), séances de sport
Balance énergétique (/sante/balance) Calories ingérées contre calories dépensées, déficit cumulé, calibration adaptative du TDEE

Détails utiles :

  • Profil corporel (taille, sexe, date de naissance, niveau d'activité) → BMR par la formule Mifflin-St Jeor, puis TDEE.
  • Objectif de poids en trois modes : rythme hebdomadaire, date cible, ou maintien. Budget calorique = TDEE lissé sur 7 jours (rythme × 7 700 kcal 7), avec un plancher de sécurité.
  • Planning de suivi : vous choisissez les jours de la semaine où vous prévoyez de vous peser (weigh_in), de faire une séance (workout) et de tenir votre journal alimentaire (food_log). LifeTrack en déduit assiduité, jours manqués et séries — sans case à cocher : l'habitude est considérée faite dès qu'une donnée existe pour ce jour.
  • Recherche d'aliments : cache local d'abord, puis Open Food Facts (recherche plein texte et code-barres) via un proxy côté serveur. Chaque produit consulté est mis en cache et n'est plus jamais redemandé.
  • Composition corporelle : IMC, et estimation de masse grasse par la méthode US Navy quand les mensurations nécessaires sont saisies.

Vape / sevrage (/vape)

Onglet Contenu
Consommation Recharges quotidiennes en ml, nicotine consommée, tendance, équivalent cigarettes
Coûts & modèle Catalogue de produits (base, boosters, arômes, résistances), recettes DIY, coût de revient au ml, achats réels
Résistances Changement de résistance en un clic, durée de vie moyenne, volume passé par résistance
Économies Économies cumulées face à la référence tabac, cigarettes évitées, temps de vie récupéré, jalons santé
  • Référence tabac gelée : date d'arrêt, cigarettes par jour avant l'arrêt, cigarettes par paquet, prix du paquet. C'est l'ancre de tout le calcul d'économies.
  • Modèle de coût DIY : chaque recette additionne quantité × (prix du flacon contenance) pour donner un coût au ml, auquel s'ajoute l'amortissement de la résistance.
  • Assistant de recette : à partir d'un volume total, d'un taux de nicotine visé et d'un booster, LifeTrack calcule les millilitres de booster, d'arôme et de base.
  • Jalons santé : 12 étapes chronométrées depuis la date d'arrêt (20 minutes, 8 heures, 24 heures, 48 heures, 72 heures, 2 semaines, 3 mois, 9 mois, 1 an, 5 ans, 10 ans, 15 ans).

Finances (/finances)

Page Contenu
Aperçu (/finances) Flux de trésorerie, dépenses par catégorie, diagramme Sankey, principaux marchands
Transactions (/finances/transactions) Liste filtrable, catégorisation unitaire ou en masse, saisie manuelle
Budgets (/finances/budgets) Budget mensuel par catégorie, avancement, projection de fin de mois
Récurrents (/finances/recurrents) Abonnements et prélèvements détectés automatiquement, prochaine échéance estimée
Comptes (/finances/comptes) Comptes courants, épargne, PayPal, espèces
Catégories (/finances/categories) Arborescence de catégories (un jeu français complet est créé automatiquement)
  • Import de relevés avec choix du compte, choix du profil bancaire, aperçu avant écriture et annulation d'un lot en un clic.
  • Presets de banques intégrés : CSV générique, BoursoBank / Boursorama, Crédit Agricole, BNP Paribas, Société Générale, La Banque Postale, Caisse d'Épargne, Fortuneo, Revolut, N26, OFX (toutes banques) et PayPal.
  • Déduplication systématique : ré-importer le même fichier n'ajoute rien deux fois.
  • Moteur de règles de catégorisation (libellé contenant, expression régulière, sens, fourchette de montant, compte), avec priorité, aperçu et application en masse. Une catégorisation manuelle n'est jamais écrasée.
  • Virements internes : détection automatique des paires de transactions qui s'annulent entre deux de vos comptes, pour ne pas les compter comme dépense.

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.
  • É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, Foodvisor CSV, Health Sync / Health Connect CSV, historique de poids CSV (date + poids).
  • Historique des imports avec statistiques par lot et annulation (rollback) : supprimer un lot supprime les lignes qu'il avait créées.
  • Taille maximale d'un fichier : 20 Mio.
  • Carte « Connecteurs & API » : URL d'ingestion à coller dans une passerelle Android, lien vers les clés d'appareil, et procédure d'export Foodvisor.

Réglages (/reglages)

Cinq onglets : Profil, Objectif, Vape, Appareils & API (création et révocation de clés d'API par appareil), Application (préférences locales au navigateur).


Captures d'écran

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.


Démarrage rapide (Docker Compose)

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

⚠️ À 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.

1. Créer le fichier .env

Depuis la racine du dépôt :

cp .env.example .env

Sous Windows (PowerShell) :

Copy-Item .env.example .env

2. Modifier les deux variables obligatoires

Ouvrez .env et changez impérativement ces deux valeurs :

Variable Valeur d'exemple à remplacer Pourquoi
LIFETRACK_JWT_SECRET generate-a-long-random-string Signe vos jetons de session. Laissée telle quelle, n'importe qui pourrait forger une session valide.
POSTGRES_PASSWORD change-me Mot de passe de la base PostgreSQL.

Pour générer un secret solide :

openssl rand -hex 32

Sans openssl (PowerShell) :

-join ((1..64) | ForEach-Object { '0123456789abcdef'[(Get-Random -Maximum 16)] })

Les autres variables ont des valeurs par défaut utilisables telles quelles :

Variable Défaut Rôle
POSTGRES_USER lifetrack Utilisateur PostgreSQL
POSTGRES_DB lifetrack Nom de la base
LIFETRACK_TIMEZONE Europe/Paris Fuseau des agrégations journalières
WEB_PORT 80 Port d'écoute de l'interface web

Deux variables restent commentées et ne servent qu'au développement : LIFETRACK_CORS_ORIGINS (origines autorisées quand Vite tourne sans proxy) et LIFETRACK_MODULES (liste blanche de modules backend à charger ; vide = tous).

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

docker compose up -d --build

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.

Suivre le démarrage :

docker compose logs -f

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

Deux adresses utiles :

Arrêter, mettre à jour, repartir de zéro

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 down -v              # ⚠️ supprime AUSSI le volume : toutes vos données

Premier démarrage

Au premier accès, LifeTrack détecte qu'aucun compte n'existe et vous redirige vers l'écran de création.

Étape 1 — Compte administrateur. Adresse e-mail, prénom ou pseudonyme (facultatif) et mot de passe de 8 caractères minimum. C'est le seul compte de l'instance : il n'y a pas de page d'inscription publique. Vous êtes connecté immédiatement après validation et arrivez sur le tableau de bord.

Étape 2 — Profil corporel. Rendez-vous dans Réglages → Profil et renseignez taille, sexe, date de naissance et niveau d'activité. Sans ces informations, LifeTrack ne peut pas calculer votre métabolisme de base, donc ni votre dépense estimée ni votre budget calorique.

Étape 3 — Objectif. Dans Réglages → Objectif, indiquez poids de départ, poids cible et mode de calcul (rythme hebdomadaire, date cible ou maintien). Le budget calorique quotidien et la date d'atteinte estimée s'affichent aussitôt. C'est aussi ici que vous accédez à votre planning de pesées et de séances.

Étape 4 (facultative) — Vape. Dans Réglages → Vape, saisissez votre date d'arrêt du tabac et votre consommation d'avant : sans cette référence, le module vape reste en mode « non configuré » et n'affiche aucune économie.

Ensuite seulement : importez vos premières données depuis Imports, ou saisissez-les à la main. Le guide d'utilisation détaille chaque parcours.


Mode développement

Commandes réellement exécutées et validées sur ce dépôt (Windows, PowerShell ou Git Bash).

Base de données seule

Le démon Docker doit tourner. Depuis la racine :

docker compose -f docker-compose.yml -f docker-compose.dev.yml up postgres

L'overlay de développement publie le port 5432 sur l'hôte, ce qui permet de faire tourner l'API directement sur la machine.

Backend (FastAPI)

L'environnement virtuel Python est déjà présent dans apps/api/.venv.

cd apps/api

# Suite de tests — 292 tests, exécutés sur SQLite en mémoire
.venv/Scripts/python.exe -m pytest

# Lint et formatage
.venv/Scripts/ruff.exe check .
.venv/Scripts/ruff.exe format .

# Serveur de développement avec rechargement à chaud
.venv/Scripts/python.exe -m uvicorn app.main:app --reload --port 8000

L'API écoute alors sur http://localhost:8000, documentation sur http://localhost:8000/api/docs.

Si l'environnement virtuel doit être recréé :

cd apps/api
python -m venv .venv
.venv/Scripts/python.exe -m pip install -r requirements.txt -r requirements-dev.txt

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

Frontend (React + Vite)

cd apps/web

npm install          # une seule fois
npm run dev          # serveur de développement sur http://localhost:5173
npm run build        # tsc --noEmit puis build de production
npm run preview      # prévisualiser le build

Proxy Vite : vite.config.ts redirige tout /api vers http://localhost:8000. Vous n'avez donc rien à configurer côté CORS tant que l'API tourne sur le port 8000 — le frontend appelle simplement /api/... en chemin relatif, exactement comme en production derrière nginx.

Si vous préférez ne pas utiliser le proxy, décommentez LIFETRACK_CORS_ORIGINS dans .env :

LIFETRACK_CORS_ORIGINS=["http://localhost:5173"]

Tout en conteneurs, avec rechargement à chaud

docker compose -f docker-compose.yml -f docker-compose.dev.yml up

L'API tourne alors en --reload sur le port 8000 et Vite sur le port 5173, les sources étant montées depuis l'hôte.


Architecture

Organisation du dépôt

LifeTrack/
├─ apps/
│  ├─ api/                    # Backend FastAPI
│  │  ├─ app/
│  │  │  ├─ 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
│  │  │  └─ main.py           # création de l'app — ne jamais modifier pour un module
│  │  ├─ requirements.txt
│  │  └─ openapi.json         # schéma OpenAPI exporté
│  └─ web/                    # Frontend React + TypeScript
│     └─ src/
│        ├─ app/              # routeur, layout, authentification — ne pas modifier
│        ├─ components/       # composants partagés (ui/, charts/)
│        ├─ lib/              # wrapper API, formatage fr-FR
│        └─ modules/          # home, health, vape, finance, imports, settings
├─ docker/                    # Dockerfiles et configuration nginx
├─ docs/                      # documents de conception et de recherche
├─ docker-compose.yml
├─ docker-compose.dev.yml
├─ .env.example
├─ CONVENTIONS.md             # règles obligatoires pour toute contribution
└─ DESIGN.md                  # synthèse des décisions de conception

Modules auto-découverts

C'est la décision structurante du projet : ajouter un module ne demande de modifier aucun fichier existant.

  • Côté backend, app/core/module_loader.py parcourt app/modules/ avec pkgutil. Tout dossier qui expose un router.py contenant router = APIRouter(prefix="/<nom>") est monté automatiquement sous /api. Les fichiers models.py, importers.py et ingest.py sont importés au démarrage pour peupler les métadonnées SQLAlchemy et les registres de connecteurs.
  • Côté frontend, src/app/modules.ts utilise import.meta.glob sur src/modules/*/index.ts. Chaque module exporte par défaut un ModuleManifest déclarant son identifiant, son titre français, son ordre, ses routes et ses entrées de navigation.

Pour créer un module, suivez CONVENTIONS.md §C2 (backend) et §C5 (frontend). Les règles y sont normatives : nommage, unités canoniques, pagination, gestion des erreurs, portabilité SQLite.

Où vivent les données

  • PostgreSQL 16, dans le volume Docker nommé pgdata. Il survit à docker compose down mais pas à docker compose down -v.
  • Le schéma est créé au démarrage par Base.metadata.create_all — il n'y a pas encore de migrations Alembic en v1.
  • Conventions de stockage : horodatages en UTC, jours locaux en Date, poids en kg, distances en m, volumes en ml, énergie en kcal, durées en secondes, argent en centimes d'euro. L'affichage en fr-FR (virgule décimale, après le montant) est fait côté interface.
  • Sauvegarde recommandée :
    docker compose exec postgres pg_dump -U lifetrack lifetrack > sauvegarde.sql
    

API

  • Toutes les routes sont préfixées par /api, suivi du nom du module : /api/auth, /api/health, /api/vape, /api/finance, /api/imports, /api/ingest.
  • En production, nginx relaie /api/ vers le conteneur api sur le port 8000 ; tout le reste retombe sur index.html (application monopage).
  • Format d'erreur unique : {"error": {"code", "message", "details"}}, code stable en snake_case, message en français.
  • Les dates-heures sont en UTC ISO 8601, les jours locaux en YYYY-MM-DD, et les agrégations journalières acceptent un paramètre ?tz= (défaut Europe/Paris).

Authentification

Deux mécanismes, pour deux usages :

  1. JWT — pour vous, dans le navigateur. Obtenu via POST /api/auth/login, envoyé dans l'en-tête Authorization: Bearer <jeton>. Mots de passe hachés en argon2. Durée de validité : 7 jours (choix assumé pour un déploiement domestique).
  2. Clés d'appareil — pour vos machines. Créées dans Réglages → Appareils & API, préfixées ltk_, envoyées dans l'en-tête X-API-Key, et portant des portées explicites (ingest:health, ingest:*, …). La clé en clair n'est affichée qu'une seule fois à la création ; seul son empreinte est stockée. Chaque clé est révocable individuellement.

Tout endpoint hors POST /api/auth/login, POST /api/auth/setup, GET /api/auth/status et /api/healthz exige une authentification et filtre systématiquement les données par utilisateur.


Stack technique

Couche Technologies
Backend Python 3.12 · FastAPI · SQLAlchemy 2.0 typé · Pydantic v2 · PyJWT · argon2 (pwdlib)
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

Aucune ressource externe n'est chargée à l'exécution : polices, icônes et bibliothèques sont embarquées dans le build.


Feuille de route et écarts connus

Ce qui suit est décrit dans les documents de conception mais absent du code d'aujourd'hui. Rien de tout cela n'est nécessaire pour utiliser LifeTrack, mais autant le savoir avant de le chercher dans l'interface.

Écarts entre la conception et le code actuel

  • Table CIQUAL absente. La recherche d'aliments prévoyait une base d'aliments génériques français (ANSES). Le code ne contient ni script d'import ni données : seule la recherche Open Food Facts fonctionne. Les plats maison se saisissent donc à la main.
  • Adaptateur webhook sans en-tête d'authentification non implémenté. La recherche prévoyait une route acceptant un jeton dans l'URL, pour les passerelles Android incapables de poser un en-tête HTTP. Elle n'existe pas : POST /api/ingest/{domain} exige X-API-Key ou un jeton JWT. Conséquence pratique détaillée dans le guide.
  • Ingestion limitée au domaine health. L'écran de création de clé propose les portées ingest:vape et ingest:finance, mais aucun gestionnaire ne les traite : POST /api/ingest/vape et POST /api/ingest/finance répondent « Domaine d'ingestion inconnu ». Ces portées sont réservées pour plus tard.
  • Pas de table de charges utiles brutes. Chaque ligne conserve sa charge utile d'origine dans une colonne raw, mais il n'existe pas de table dédiée permettant de rejouer une normalisation après coup.
  • Importeurs de séances absents : pas de lecteur TCX, GPX ou FIT. Les séances de sport se saisissent à la main ou arrivent par l'ingestion JSON.
  • Importeurs MyFitnessPal et Cronometer absents : seul Foodvisor dispose d'un profil nutrition dédié.
  • Pas de migrations Alembic : le schéma est créé au démarrage. Une modification de schéma sur une base existante devra être gérée manuellement.

v2 — envisagé

  • Application compagnon Android maison (Kotlin, SDK Health Connect), avec vraie authentification par jeton et rattrapage d'historique au-delà de 48 heures.
  • Connecteur bancaire Enable Banking (PSD2, gratuit pour ses propres comptes) en remplacement des imports de fichiers.
  • Calculateur DIY avancé, migrations Alembic, notifications.

v3 — exploratoire

  • Enregistrement direct des séances de tapis de course via Web Bluetooth + FTMS (Chrome/Edge uniquement, HTTPS requis).
  • Analyse photo des repas, multi-utilisateurs complet, thème clair.

Documentation

Document Contenu
docs/GUIDE.md Guide d'utilisation au quotidien : connecter Health Connect, suivre poids et calories, nutrition, vape, finances, dépannage
CONVENTIONS.md Règles obligatoires pour contribuer au code
DESIGN.md Synthèse des décisions de conception
docs/design/architecture.md Architecture technique complète
docs/design/datamodel-health-vape.md Modèle de données et formules santé, nutrition, vape
docs/design/datamodel-finance.md Modèle de données et logique finances
docs/design/ux-pages.md Spécification UX page par page
docs/design/addendum-planning.md Planning d'habitudes, assiduité, séries
docs/research/health-connect.md Comment sortir les données de Health Connect
docs/research/nutrition-sources.md Foodvisor, Open Food Facts, CIQUAL
docs/research/finance-sources.md Formats d'export réels des banques françaises

Licence

Aucune licence n'est déclarée : ce dépôt est un projet personnel auto-hébergé, il n'est pas publié sous licence libre. En l'absence de fichier LICENSE, tous droits sont réservés par défaut.

Notez les obligations attachées aux données tierces que vous consommerez :

  • Open Food Facts — base sous ODbL, contenus sous DbCL. Un usage personnel ne pose aucune difficulté ; une attribution « Données produits : Open Food Facts (ODbL) » reste la bonne pratique.
  • Les applications passerelles Android citées dans la documentation ont leurs propres licences (AGPL-3.0 pour health-connect-webhook, GPL-3.0 pour HCGateway) — elles ne sont pas distribuées avec LifeTrack.
S
Description
No description provided
Readme
892 KiB
Languages
TypeScript 53.4%
Python 46.4%
Dockerfile 0.1%