Recrée les fichiers de déploiement perdus lors de la restauration du repo.
- docker-compose.deploy.yml : production basée sur les images publiées
(git.maison43gil.com/gilles/home_hub:{backend,frontend}-latest), sans build:,
avec service backend-migrate (alembic upgrade head) avant le démarrage
- .gitea/workflows/build.yml : CI Gitea Actions, build+push des 2 images
- backend/.dockerignore + frontend/.dockerignore : images propres, sans secrets
- .env.example : template complet sans secret réel (placeholder change-me)
- README : section déploiement OCI (build manuel, CI, serveur, note 413 proxy)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Les notes créées avant la migration 0061 ont urls=NULL en base. Le défaut
NoteResponse.urls=[] ne s'applique qu'à un attribut absent, pas à None, d'où
ResponseValidationError "Input should be a valid list" → GET /api/notes 500
→ "Erreur de chargement" côté UI.
Ajoute un field_validator(mode='before') qui coerce None → [].
Nettoie aussi l'import HttpUrl inutilisé.
v0.5.16
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend :
- Migration 007 : list_type VARCHAR(20) sur shopping.lists (weekly/project),
url/description/image_url sur shopping.list_items
- Modèle ShoppingList : champ list_type
- Modèle ListItem : champs url, description, image_url
- Schémas : list_type sur Create/Response, nouveaux champs sur ItemCreate/Update/Response
- _unique_week_label() : évite les doublons S22 2026 → S22 2026 (2)
- finish_shopping : carry-over uniquement pour list_type='weekly'
Frontend :
- api/shopping.ts : list_type, champs enrichis item, createProjectList()
- ProjectItemCard.tsx : carte avec image, description, URL, boutique, cochage
- ShoppingPage :
· Séparation weekly / project dans la sélection de liste active
· Section "Listes projet" sur l'écran vide avec navigation
· Badge PROJET dans l'en-tête
· Bouton "Clôturer la semaine" et badge "semaine dépassée" masqués sur projet
· Bouton "+ Ajouter" (mobile + laptop) sur les listes projet
· Vue grille ProjectItemCard pour les listes projet
· Modale création liste projet (nom + boutique)
· Modale ajout/édition item projet (nom, description, URL, image URL)
v0.5.14
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ajoute mcp_server.py avec get_todos, create_todo, update_todo, postpone_todo, delete_todo.
Ajoute test_mcp.py (7 tests). Corrige conftest pour injecter NullPool dans AsyncSessionLocal des outils MCP (évite les conflits d'event loop entre tests).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes deux vulnérabilités critiques en sécurité:
1. **Timing attack** — remplace la comparaison naïve `!=` par
`hmac.compare_digest()` pour éviter les attaques temporelles
(constant-time comparison).
2. **Clé vide acceptée** — ajoute le check `not settings.mcp_api_key`
pour rejeter (401) TOUS les requêtes `/mcp` si MCP_API_KEY n'est
pas configurée, empêchant l'accès unauthenticated silencieux.
Bonus: ajoute l'en-tête `www-authenticate: Bearer` (RFC 9110).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Admin stats : ajout video (count + size_bytes) dans /api/admin/stats.
ConfigPage : grille médias 3 colonnes (Photos / Audio / Vidéos).
docker-compose : backend et backend-worker tournent en user 1000:1000
pour que les fichiers écrits dans ./data/ appartiennent à l'utilisateur
hôte et non à root.
v0.5.6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Decimal Python → string JSON causait TypeError: z.toFixed is not a function
dans NoteCard (title attribute de l'icône GPS). Tous les champs gps_lat/gps_lon
passent maintenant en float | None dans les schémas Pydantic.
v0.5.3
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ImportError au démarrage du backend : ALLOWED_AUDIO_TYPES avait été renommé
en ALLOWED_AUDIO_PREFIXES dans services/media.py mais l'import dans api/media.py
n'avait pas été mis à jour.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Migration 006 : colonne tags TEXT[] sur shopping.products
- Modèle SQLAlchemy + schémas Pydantic mis à jour (ProductCreate/Update/Response)
- Interface TypeScript Product/ProductCreate/ProductUpdate avec tags?: string[]
- CatalogueModal : chip input (Entrée/virgule pour ajouter, clic pour supprimer, Backspace pour retirer le dernier)
- Recherche dans le catalogue et le bottom sheet étendue aux tags (insensible aux accents)
- Tags affichés en pills dans la liste du catalogue
v0.4.12
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Avec 119 articles en base et limit=50, les articles en fin de liste
alphabétique (Îles flottantes, Éponge…) n'apparaissaient pas dans
le bottom sheet. Limite portée à 500 côté backend ET frontend.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CatalogueModal : cleanForm() capitalise désormais le nom avant envoi API
(création et modification d'article)
- Migration 005 : met à jour shopping.products.name et
shopping.list_items.custom_name pour capitaliser les données existantes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend :
- Migration 004 : last_purchased_at (DATE) + avg_interval_days (NUMERIC)
sur shopping.products
- update_item : met à jour les stats au premier cochage d'un article
lié à un produit (moyenne mobile exp. 70/30)
- ProductResponse expose les deux nouveaux champs
Frontend :
- ItemRow : long press 500ms → onEdit() (mobile) ; crayon + croix (laptop)
- ShoppingPage : modal édition quantité/unité, état editingItem
- api/shopping.ts : Product inclut last_purchased_at + avg_interval_days
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Upload photo (context=product → thumbnail 150×150) dans CatalogueModal
- Miniature affichée dans la liste et dans le formulaire
- Schémas ProductCreate/Update/Response exposent image_path + thumbnail_path
- Backend sert /media/* via StaticFiles (FastAPI)
- Proxy /media → backend dans vite.config et nginx.conf
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- UX : vue par défaut = liste en cours, landing si pas de liste (+ vert +
baguette magique), suppression des vues "listes" et "mode magasin" séparés
- Articles cochés barrés et déplacés en bas, tri alphabétique par section
- Nom de liste auto avec numéro de semaine ISO (S21 2026)
- Wake lock activé dès qu'une liste est ouverte
- CRUD boutiques : POST/PATCH/DELETE /stores + modal Boutiques
- CRUD articles : POST/PATCH/DELETE /products + modal Catalogue
- Champs enrichis produits : description, prix, quantité/unité, boutique défaut
- Champs enrichis boutiques : url, store_type (alimentaire, bricolage…)
- Migration 003 : nouveaux champs en base
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ajoute POST /api/shopping/lists/generate qui calcule un score retard/intervalle
par article (achats_with_lag CTE pour contourner la limite PostgreSQL sur
AVG+LAG imbriqués) et génère une liste draft avec les articles >= 0.7.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ajoute backend/app/api/shopping.py avec les routes :
- GET/POST /api/shopping/lists
- GET/PATCH/DELETE /api/shopping/lists/{id}
- POST/PATCH/DELETE /api/shopping/lists/{id}/items
- GET /api/shopping/stores
- GET /api/shopping/products
- POST /api/shopping/lists/{id}/finish (report des articles non cochés)
Enregistre le router dans main.py avec le prefix /api/shopping.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Changer due_after/due_before de str | None vers datetime | None pour typage FastAPI
- FastAPI parse et valide automatiquement, retourne 422 si format invalide (pas 500)
- Supprimer le parsing manuel datetime.fromisoformat() qui levait ValueError brute
- Ajouter ORDER BY déterministe: due_date ASC NULLS LAST, created_at DESC
évite les réordonnances aléatoires entre requêtes PostgreSQL
Tests: 15/15 passent ✓
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ajoute les 5 endpoints REST (list, create, update, delete, postpone),
enregistre le routeur sur /api/todos, et corrige l'isolation des sessions
de test via NullPool + dependency_overrides dans conftest.py.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>