Files
home_hub/docs/plan.md
T
gilles be0c8bceb6 feat: export Markdown notes (ARQ/Redis) + backup/restore BDD — v0.5.2
- Volume data/ (bind mount ./data) remplace le volume uploads nommé
  data/notes/ → .md auto-générés, data/uploads/ → médias, data/backup/ → dumps
- Service Redis (redis:7-alpine) + worker ARQ (backend-worker)
- notes_markdown.py : frontmatter YAML + contenu + pièces jointes (liens relatifs)
  Nom : YYYY-MM-DD_slug-titre_shortid.md, rotation si titre modifié
- api/notes.py : publie export_note_markdown / remove_note_markdown sur Redis
  après chaque create / update / delete / add_attachment / delete_attachment
- api/admin.py : POST /backup, GET /backups, POST /restore/{filename} (pg_dump/pg_restore)
- Backend Dockerfile : postgresql-client ; requirements : arq==0.26.1
- ConfigPage : section "Base de données" avec sauvegarde + liste + restauration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:33:29 +02:00

296 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HomeHub — Plan de développement
> Approche itérative : chaque phase livre quelque chose d'utilisable.
> Stack : FastAPI · React/Vite/TS · PostgreSQL 16 · Docker Compose
---
## Phase 1 — Socle technique ✅
**Objectif** : environnement opérationnel, rien de métier, mais tout tourne.
### Backend
- [x] Structure projet FastAPI (`app/api/`, `app/core/`, `app/models/`, `app/schemas/`, `app/services/`)
- [x] Configuration SQLAlchemy 2.0 async + pool de connexions
- [x] Création des schémas PostgreSQL (`todos`, `shopping`, `notes`) via Alembic
- [x] Migration initiale : toutes les tables (voir spec section 3)
- [x] Endpoint santé : `GET /api/health`
- [x] Module media (`app/services/media.py`) :
- [x] Validation des formats acceptés (JPG, PNG, SVG, WebP, WebM, M4A)
- [x] Génération miniature Pillow : 150×150 (shopping), 300×300 (notes), 400×300 (inline)
- [x] Sauvegarde `originals/` + `thumbnails/` sur le volume
- [x] Endpoint `POST /api/media/upload` → retourne `{ file_path, thumbnail_path }`
- [x] Endpoint `DELETE /api/media/{uuid}` → supprime original + miniature
- [x] Middleware CORS pour réseau local `10.0.0.0/22`
- [x] Configuration via variables d'environnement (`.env`)
- [x] Dockerfile backend (Python 3.12 slim)
### Frontend
- [x] Scaffold Vite + React 18 + TypeScript
- [x] Tailwind CSS configuré avec les tokens Gruvbox (via `tokens.json`)
- [x] Intégration `design_system/components/ui-kit.jsx`
- [x] `@vite-pwa/plugin` configuré (Service Worker + manifest)
- [x] `manifest.json` avec icônes iOS + Android
- [x] Routage React Router v6
- [x] Layout de base : navigation mobile (bottom bar) + navigation laptop (sidebar)
- [x] Dockerfile frontend (Nginx + proxy `/api/` → backend)
- [x] Favicon maison (SVG Gruvbox orange)
### Infra
- [x] `docker-compose.yml` avec 3 services (frontend, backend, db)
- [x] Volume PostgreSQL persistant
- [x] Volume uploads persistant (`/uploads/`)
- [x] Fichier `.env.example`
- [x] Script de seed (`backend/app/data/seed.py`) : 113 produits + 9 magasins
---
## Phase 2 — Module Todos ✅
**Objectif** : créer, lister, modifier, terminer et reporter des tâches depuis mobile et laptop.
### Backend
- [x] `GET /api/todos` — liste avec filtres (domaine, statut, priorité, tags, période)
- [x] `POST /api/todos` — création
- [x] `PATCH /api/todos/{id}` — mise à jour partielle
- [x] `DELETE /api/todos/{id}` — suppression
- [x] `POST /api/todos/{id}/postpone` — incrémente postponed_count + décale due_date
- [x] Schémas Pydantic : TodoCreate, TodoUpdate, TodoResponse
- [x] Tests d'intégration (9 tests)
### Frontend Mobile
- [x] Page Todos : liste des tâches en cours, groupées par domaine
- [x] Bouton "+ Tâche" flottant → Modal création (titre + domaine + date + priorité + tags)
- [x] Double-tap → Modal édition pré-rempli
- [x] Swipe droite → marquer done
- [x] Swipe gauche → actions (reporter 1j / reporter 1 semaine / supprimer)
- [x] Badge compteur par domaine
### Frontend Laptop
- [x] Vue tableau avec filtres : domaine, statut, priorité, période
- [x] Formulaire complet (tous les champs) via Modal
- [x] Double-clic sur titre → Modal édition
- [x] Actions inline : ✓ done / +1j / +1S / ✕ supprimer
---
## Phase 3 — Module Shopping (liste de courses) ✅
**Objectif** : liste de courses fonctionnelle en magasin depuis smartphone, avec génération automatique.
### Backend
- [x] `GET /api/shopping/stores` — liste magasins
- [x] `GET /api/shopping/products` — catalogue avec recherche (param `q`)
- [x] `GET /api/shopping/lists` — listes de courses avec compteurs
- [x] `POST /api/shopping/lists` — création liste
- [x] `GET /api/shopping/lists/{id}` — détail liste avec articles
- [x] `PATCH /api/shopping/lists/{id}` — mise à jour liste
- [x] `DELETE /api/shopping/lists/{id}` — suppression
- [x] `POST /api/shopping/lists/{id}/items` — ajout article (produit catalogue ou custom)
- [x] `PATCH /api/shopping/lists/{id}/items/{item_id}` — cocher / modifier
- [x] `DELETE /api/shopping/lists/{id}/items/{item_id}` — suppression article
- [x] `POST /api/shopping/lists/{id}/finish` — terminer les courses (report articles non cochés → nouvelle liste draft avec `carried_over=True`)
- [x] `POST /api/shopping/lists/generate` — liste magique V1 (score = retard / intervalle moyen, articles cochés comme historique)
- [x] Schémas Pydantic complets (listes, articles, produits, magasins)
- [x] Tests d'intégration (10 tests)
- [x] Champ `tags TEXT[]` sur les produits du catalogue (migration 006)
### Frontend
- [x] Page Shopping avec 3 vues : liste des listes / détail / mode magasin
- [x] Vue "listes" : cartes par liste (statut, compteur articles), FAB + bouton "Liste magique"
- [x] Bouton "Liste magique" : désactivé si une liste `draft`/`active` existe
- [x] Vue "détail" : articles avec swipe-to-delete, FAB ajout article, bouton ✏️ modal gestion
- [x] Modal ✏️ : ajout rapide d'article + bouton rouge "Supprimer la liste en cours"
- [x] Vue "Mode magasin" : plein écran, grands boutons (48px+), section cochés/non cochés
- [x] Wake Lock API activée automatiquement en mode magasin
- [x] Composant `Modal` générique réutilisable (overlay + Escape + stopPropagation)
- [x] Composant `ItemRow` : checkbox circulaire + swipe-to-delete + mode magasin
- [x] Hook `useWakeLock` avec fallback gracieux (mode économie d'énergie)
- [x] Client API TypeScript typé (`frontend/src/api/shopping.ts`)
- [x] Migration TodoForm vers Modal (plus de panneau inline)
- [x] Bottom sheet multi-select pour l'ajout d'articles depuis le catalogue
- [x] Stats d'achat dans le catalogue : dernier achat + intervalle moyen par produit
- [x] Swipe gauche → modal édition (TodoPage + ShoppingPage)
- [x] Tags sur les produits : chip-input dans CatalogueModal, recherche étendue aux tags
- [x] Recherche article : `type="search"` supprime la suggestion URL iOS, `autoFocus` automatique
- [x] Upload photo produit : support HEIC/HEIF, redimensionnement 500×500 max (ratio conservé), miniature auto
- [x] Collage Ctrl+V photo dans CatalogueModal et TodoForm
---
## Phase 4 — Module Notes ✅
**Objectif** : saisie rapide de notes avec photo, audio et GPS depuis smartphone.
### Backend
- [x] `GET /api/notes` — liste avec search full-text + filtres tags/catégorie
- [x] `POST /api/notes` — création
- [x] `PATCH /api/notes/{id}` — mise à jour
- [x] `DELETE /api/notes/{id}` — suppression
- [x] `POST /api/notes/{id}/attachments` — upload fichier (image/audio)
- [x] `DELETE /api/notes/{id}/attachments/{att_id}` — suppression pièce jointe
- [x] Compression image Pillow → WebP (service media partagé)
- [x] Recherche FTS PostgreSQL français
### Frontend Mobile
- [x] Page Notes : liste chronologique avec aperçu
- [x] Bouton "+ Note" → formulaire rapide (contenu + tags) via Modal
- [x] Bouton 📷 → Camera API (capture directe ou import galerie)
- [x] Bouton 🎤 → MediaRecorder (enregistrement audio inline)
- [x] Bouton 📍 → Geolocation API → affichage coordonnées
- [x] Visionneuse photo inline + lecteur audio inline
- [ ] Compression WebP côté client (Canvas API) — différé Phase 5+
- [ ] Waveform visuel enregistrement — différé Phase 5+
### Frontend Laptop
- [x] Grille notes avec vignettes photo
- [x] Recherche full-text
- [x] Filtres rapides : avec photo, avec audio, avec GPS
- [ ] Vue carte pour les notes avec GPS (Leaflet.js) — différé Phase 5+
---
## Phase 4b — UX transversale ✅
**Objectif** : améliorations d'expérience applicables à tous les modules.
### Thème et apparence
- [x] `ThemeContext` : gestion dark / light / system avec persistance `localStorage`
- [x] Script anti-flash dans `index.html` : thème et zoom appliqués avant le premier rendu
- [x] Page `/config` : boutons thème + slider police (0.81.4, pas de 0.05)
- [x] Aperçu temps réel du texte sur le slider
- [x] Zoom CSS sur `<html>` : scale global compatible avec les tailles en pixels fixes
- [x] `TopBar` : en-tête fixe 44px avec bouton de thème cyclique (lune / soleil / demi-cercle)
- [x] Tuile "Paramètres" sur la page d'accueil (`/config`, icône `fa-sliders`)
### Mobile — clavier iOS
- [x] `BottomSheet` : `visualViewport` API pour remonter le panneau au-dessus du clavier virtuel
- [x] Transition fluide (0.15s) sur `bottom`, `max-height`, `border-radius` à l'ouverture du clavier
### Todos
- [x] Case "Pas de date" pré-cochée par défaut dans `TodoForm` (champ date masqué)
- [x] Collage Ctrl+V pour coller une image directement dans le formulaire Todo
### Infra / Media
- [x] nginx : `location ^~ /media/` pour éviter que la règle regex WebP prenne la priorité
- [x] nginx : `client_max_body_size 15m` pour les photos de smartphone (38 Mo)
---
## Phase 5 — Export Markdown notes + Backup BDD ✅
**Objectif** : persistance double des notes (BDD source de vérité + fichiers Markdown partagés), infrastructure Redis, backup/restore depuis l'interface.
### Architecture
- Volume Docker `./data/` (bind mount) remplace le volume nommé `uploads`
- `data/notes/` — fichiers `.md` auto-générés
- `data/uploads/` — médias (photos, audio)
- `data/backup/` — dumps PostgreSQL
### Backend
- [x] Service Redis : `redis:7-alpine` dans docker-compose
- [x] Worker ARQ (`backend-worker`) : consomme la queue `notes:markdown`, même image que backend
- [x] `app/core/redis.py` — pool ARQ, `enqueue()` best-effort (silence si Redis down)
- [x] `app/workers/notes_worker.py` — tâches `export_note_markdown` + `remove_note_markdown`
- [x] `app/services/notes_markdown.py` — génère le frontmatter YAML + contenu + pièces jointes (liens relatifs `../uploads/…`)
- Nom de fichier : `YYYY-MM-DD_slug-du-titre_shortid.md`
- Rotation automatique si titre modifié (recherche par `*_{shortid}.md`)
- [x] `app/api/notes.py` — publie sur Redis après create / update / delete / add_attachment / delete_attachment
- [x] `app/api/admin.py``POST /api/admin/backup`, `GET /api/admin/backups`, `POST /api/admin/restore/{filename}`
- [x] `backend/Dockerfile` — ajout `postgresql-client` pour pg_dump / pg_restore
- [x] `requirements.txt` — ajout `arq==0.26.1`
### Frontend
- [x] `frontend/src/api/admin.ts` — client TypeScript backup/restore
- [x] Page Config — section "Base de données" : bouton sauvegarde + liste des dumps + bouton Restaurer par fichier
---
## Phase 6 — Scan produits + enrichissement catalogue
**Objectif** : scan code-barres depuis mobile, auto-remplissage depuis OpenFoodFacts.
### Services Docker
- [ ] Dockerfile `product-search/` : Python slim + `requests` + client OpenFoodFacts
- [ ] Ajout service `product-search` dans `docker-compose.yml` (port 8002)
- [ ] Ajout service `searxng` dans `docker-compose.yml` (images uniquement)
### Backend (endpoints proxy)
- [ ] `GET /api/products/lookup?barcode=` → proxifie vers product-search
- [ ] `GET /api/products/search?q=` → proxifie vers product-search
- [ ] `POST /api/products/import` → crée produit depuis données OpenFoodFacts
### Frontend Mobile — scan
- [ ] Intégration `zxing-js` (BarcodeReader via flux caméra)
- [ ] Bouton "Scanner" dans le formulaire d'ajout de produit
- [ ] Sur scan réussi → auto-remplissage formulaire
### Frontend Laptop — enrichissement catalogue
- [ ] CRUD complet produits dans catalogue
- [ ] Recherche texte → OpenFoodFacts → import 1 clic
- [ ] Gestion magasins
---
## Phase 7 — Service OCR (conteneur dédié)
**Objectif** : OCR partagé, prérequis pour shopping avancé et notes avancées.
- [ ] Dockerfile `ocr/` : Python slim + Tesseract 5 + langues (fra, eng) + Pillow
- [ ] `POST /extract` : reçoit image (multipart), retourne `{ text, confidence }`
- [ ] Endpoint backend `POST /api/ocr/extract` : proxifie vers `ocr:8001`
- [ ] Ajout du service `ocr` dans `docker-compose.yml`
---
## Phase 8 — Shopping avancé (OCR + suivi prix)
### Backend
- [ ] `POST /api/shopping/ocr/price-tag` — photo étiquette → extraction prix
- [ ] `POST /api/shopping/ocr/receipt` — photo ticket → reconciliation liste
- [ ] `GET /api/shopping/products/{id}/price-history` — historique prix par produit + magasin
### Frontend
- [ ] Bouton 📷 sur chaque article → OCR étiquette → pré-remplissage prix
- [ ] Graphique d'évolution du prix par produit (Recharts)
- [ ] Comparaison prix par magasin
---
## Phase 9 — MCP Server
**Objectif** : exposer les outils HomeHub aux agents IA (Hermes, Claude, etc.).
- [ ] Intégration SDK MCP Python (`mcp` package)
- [ ] Endpoint SSE `/mcp`
- [ ] Implémentation des outils : `get_todos()`, `add_todo()`, `add_shopping_item()`, `search_notes()`
- [ ] Documentation OpenAPI des outils MCP
---
## Phases futures (hors scope initial)
### Phase 9 — Authentification multi-utilisateurs
- Auth JWT (login / refresh token)
- Activation de `owner_id` dans toutes les tables
- Page connexion React
### Phase 10 — Calendrier + intégrations
- Sync Google Calendar (OAuth2 + worker)
- Endpoint CalDAV pour iOS
- Redis pour la queue de synchronisation
- Webhooks Gitea → Kanban
- Home Assistant : capteur "tâches en retard"
### Phase 11 — Vision IA
- Endpoint analyse frigo (photo → suggestions liste de courses)
- Amélioration OCR via modèle Vision local (Ollama)
---
## Ordre de développement
```
Phase 1 ✅ → Phase 2 ✅ → Phase 3 ✅ → Phase 4 ✅ → Phase 4b ✅ → Phase 5 ✅ → Phase 6 (Scan) → Phase 7 (OCR) → Phase 8 (Shopping avancé) → Phase 9 (MCP)
```