Commit Graph

144 Commits

Author SHA1 Message Date
gilles 5cfd8184e9 Ajouter data/uploads/test
Build & Push OCI / build (./backend, backend) (push) Failing after 19s
Build & Push OCI / build (./frontend, frontend) (push) Failing after 16s
2026-05-31 09:43:20 +02:00
gilles 59b5836fbd Actualiser .env.example
Build & Push OCI / build (./backend, backend) (push) Failing after 15s
Build & Push OCI / build (./frontend, frontend) (push) Failing after 15s
2026-05-30 11:05:48 +02:00
gilles 091eead5bb fix(pwa): manifest cassé + icônes PWA non appliquées
Build & Push OCI / build (./backend, backend) (push) Failing after 2m59s
Build & Push OCI / build (./frontend, frontend) (push) Failing after 16s
index.html référençait /manifest.json (inexistant) → nginx renvoyait index.html
en fallback SPA → "Manifest: Line 1, column 1, Syntax error" dans Chromium, et la
PWA n'avait aucune icône valide (d'où les favicons icon-192/512 non utilisées
pour le raccourci).

- Retire le <link rel="manifest" href="/manifest.json"> codé en dur :
  VitePWA injecte déjà /manifest.webmanifest au build
- Ajoute <link rel="apple-touch-icon" href="/icons/icon-192.png"> pour le
  raccourci écran d'accueil iOS (qui n'utilise pas le manifest pour l'icône)

v0.5.17

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 10:53:51 +02:00
gilles 36b5760566 chore(deploy): images OCI Gitea + compose production + CI
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>
2026-05-30 10:45:30 +02:00
gilles c36be15e18 OCI registry consigne 2026-05-30 10:16:16 +02:00
gilles 53018c16dd fix(notes): erreur 500 sur les notes antérieures (urls=NULL)
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>
2026-05-30 10:15:15 +02:00
gilles 6c889f1561 fix(mcp+alembic): désactive DNS rebinding (421) + rechaîne migrations 006
MCP :
- FastMCP recevait Host=localhost (sans port) mais le pattern par défaut
  allowed_hosts=["localhost:*", ...] EXIGE un port → 421 Invalid Host header
  pour tout accès non-localhost (ex: Hermes via http://10.0.0.50:3001/mcp)
- Désactive enable_dns_rebinding_protection : le Bearer MCP_API_KEY est la
  vraie barrière (protection rebinding = anti-attaque navigateur, inutile ici)
- nginx /mcp : retour à Host $host (le rewrite localhost était cassé)

Alembic :
- Collision : 006_notes_urls et 006_product_tags partageaient revision='006'
  → "Multiple head revisions" au démarrage
- Renumérote notes_urls en 0061, chaîné après product_tags
  Chaîne finale : 005 -> 006 (product_tags) -> 0061 (notes_urls) -> 007

v0.5.15

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 10:07:00 +02:00
gilles 7bf6caa3dd feat(shopping): listes projet + déduplication nommage hebdo
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>
2026-05-30 09:59:53 +02:00
gilles 031708ad8f feat(notes): ajout de liens nommés (label + url) sur les notes
Backend :
- Migration 006 : colonne urls JSONB nullable sur notes.items
- Modèle NoteItem : champ urls list[dict]
- Schémas : NoteUrl (label + url avec validation http/https),
  NoteCreate/NoteUpdate/NoteResponse exposent urls

Frontend :
- api/notes.ts : interface NoteUrl + champ urls sur Note/NoteCreate
- NoteForm : section "Liens" avec ajout (libellé + URL), suppression,
  validation http/https, confirmation par Enter
- NotesPage : badge compteur liens dans metaLine (semi/collapsed),
  section liens cliquables dans le mode expanded

v0.5.13

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 09:47:49 +02:00
gilles b084905226 fix(ui): icône logo TopBar → fa-circle-nodes (cohérence avec icon hub)
Remplace fa-house par fa-circle-nodes dans la TopBar pour aligner
le logo affiché dans l'app avec la nouvelle icône PWA/favicon hub.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 09:42:49 +02:00
gilles 208af72344 6 2026-05-30 09:33:12 +02:00
gilles 7a500e2435 chore(design): nouvelle icône hub (réseau de nœuds) favicon + PWA
Remplace la maison par un graphe central orange (accent) avec 3 nœuds
satellites crème (d5c4a1) sur fond sombre Gruvbox. Décliné en
favicon.svg, icon-192.png et icon-512.png (PWA shortcut smartphone).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 09:29:28 +02:00
gilles 4518ed8311 chore(design): ajout du package design system smartphone
Contient les tokens, composants et exemples adaptés au mobile,
à utiliser comme référence lors du développement des vues smartphone.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 08:53:36 +02:00
gilles 4c616fa5d3 fix(ui): bouton + laptop, overflow mobile, clôture semaine, backup complet
- TodosPage/ShoppingPage : bouton « + » visible en laptop (hidden lg:flex)
- ShoppingPage : renomme « Terminer » en « Clôturer la semaine », badge ⚠
  si semaine ISO dépassée, confirmation modale avec décompte non-cochés
- NotesPage : overflowWrap:anywhere sur titre/contenu/markdown (URLs longues
  qui débordaient hors de la tuile sur smartphone)
- index.css : overflow-x:hidden + max-width:100vw sur html/body (garde-fou global)
- admin.py : backup remplacé par archive .tar.gz (pg_dump + uploads/) streamée
  au navigateur ; restore via multipart upload avec extraction sécurisée
- admin.ts : downloadBackup() (blob trigger) + uploadAndRestore() avec progression XHR
- ConfigPage : refonte section backup avec boutons Télécharger/Restaurer
  et barre de progression upload

v0.5.11

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-30 08:52:37 +02:00
gilles 69c2042995 fix(mcp): nginx Host=localhost pour passer la protection DNS rebinding FastMCP 2026-05-28 10:42:57 +02:00
gilles 20483dc5f9 fix(mcp): désactiver DNS rebinding protection (auth Bearer suffisant) 2026-05-28 10:40:44 +02:00
gilles 727ebc6484 fix(mcp): démarrer session_manager dans le lifespan FastAPI 2026-05-28 10:31:34 +02:00
gilles 39939b9621 fix(mcp): streamable_http_path=/ pour mount FastAPI à /mcp 2026-05-28 10:05:52 +02:00
gilles d50d659daf fix: autoriser 10.0.1.45 dans CORS_ORIGINS 2026-05-28 06:58:00 +02:00
gilles 0a798d2791 chore: bump version — MCP server v0.5.10 2026-05-28 06:54:46 +02:00
gilles 828efb9dd8 fix(mcp): MCP_API_KEY via variable d'environnement (pas en clair dans docker-compose) 2026-05-28 06:53:36 +02:00
gilles 8ebdccb543 feat(mcp): câblage FastAPI + nginx proxy + docker-compose MCP_API_KEY
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 06:49:50 +02:00
gilles 5d7dbec67c fix(mcp): status active + search_products guard + item.product + cleanup auto-name 2026-05-28 06:41:04 +02:00
gilles 87efbcb03d feat(mcp): 6 outils shopping + tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 06:37:38 +02:00
gilles c72ffd0ad6 fix(mcp): FTS colonnes non qualifiées + test positif search_notes 2026-05-28 06:29:36 +02:00
gilles e902452781 feat(mcp): 5 outils notes + tests
Ajoute search_notes, get_note, create_note, update_note, delete_note au serveur MCP.
Tests: 6 nouveaux tests notes (13 tests MCP au total, tous passent).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 23:11:15 +02:00
gilles 6cd866c77a fix(mcp): scope fixture NullPool + suppression imports inutiles + validation enums + cleanup tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 23:03:43 +02:00
gilles 05db49f27a feat(mcp): 5 outils todos + tests
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>
2026-05-25 22:58:20 +02:00
gilles 24598c836b fix(mcp): comparaison constante hmac + rejet si clé vide + WWW-Authenticate
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>
2026-05-25 22:49:45 +02:00
gilles cc8fc5ba3f feat(mcp): middleware ASGI Bearer token pour /mcp* 2026-05-25 22:47:24 +02:00
gilles 6ff7c2f74e fix(mcp): contrainte version mcp<2.0 + MCP_API_KEY dans .env.example
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 22:46:36 +02:00
gilles 48e1b5343e feat(mcp): dépendance mcp>=1.9 + champ mcp_api_key dans Settings 2026-05-25 22:44:57 +02:00
gilles bbf264fb61 docs: plan d'implémentation MCP server (16 outils + tutoriel Hermes) 2026-05-25 22:40:41 +02:00
gilles b8d89acafa docs: ajout clé MCP_API_KEY générée dans la spec 2026-05-25 22:29:54 +02:00
gilles fefde4eb31 docs: spec serveur MCP HomeHub (16 outils, Streamable HTTP, Hermes) 2026-05-25 22:28:28 +02:00
gilles 273e032245 3 2026-05-25 21:27:13 +02:00
gilles f81be12a38 feat(notes): renderer markdown étendu v0.5.9
- Tableaux (pipe syntax) avec header distinct
- Task lists - [ ] / - [x] avec checkbox colorée
- Listes imbriquées avec indentation (• / ◦)
- Texte barré ~~strikethrough~~
- Liens [texte](url) cliquables (target _blank)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:13:14 +02:00
gilles ec87bc091d feat(sse): sync temps réel multi-appareils via Server-Sent Events v0.5.8
- Broadcaster asyncio.Queue avec keepalive 25s (prévient timeout proxy)
- Endpoint GET /api/events/stream (StreamingResponse text/event-stream)
- Broadcast notes_changed / todos_changed / shopping_changed sur toutes mutations
- Hook useServerEvents: EventSource avec reconnexion automatique (3s)
- Pages Notes, Todos, Shopping abonnées aux événements SSE
- nginx: location SSE dédiée (proxy_buffering off, timeout 24h)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 20:12:02 +02:00
gilles 2129da4f55 fix(notes): audio overflow + volume 50%, grille 3col, bouton laptop, sidebar BDD
- Audio : minWidth:0 + onLoadedMetadata volume=0.5 (plus de débordement)
- Grille : repeat(3,1fr) sur laptop, 1fr sur mobile (était auto-fill)
- Header laptop : bouton "Nouvelle note" (fa-plus + accent) visible lg:flex
- SideNav : DbStatusBar en bas — LED verte/rouge + taille BDD, polling 30s
- docs/plan.md : Phase 4c documentée

v0.5.7

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:51:44 +02:00
gilles c72ca04fd2 feat(config): stats vidéo + user 1000:1000 dans docker-compose
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>
2026-05-25 16:44:31 +02:00
gilles dd4ce6f52b feat(notes): 3 états de tuile + renderer pseudo-markdown
Tuile note : semi (défaut, 3 lignes tronquées) / expanded (markdown complet
+ médias) / collapsed (titre + date uniquement). Bouton toggle fa-chevron
en haut à droite qui cycle entre les états.

Renderer pseudo-markdown inline : # ## ###, - * listes, 1. numérotées,
> citations, --- séparateur, **gras** *italique* `code`, ``` blocs.

Méta de tuile : icônes fa-image / fa-microphone / fa-video / fa-location-dot
visibles en état semi et expanded.

v0.5.5

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:43:44 +02:00
gilles 6c9ebcaab7 feat(notes): support vidéo + transcodage audio AAC universel
Audio : ffmpeg transcode toute entrée (webm/ogg/m4a) vers AAC/m4a
au moment de l'upload → lecture Safari iOS garantie.

Vidéo : nouveau save_video(), webm transcodé en H.264/mp4, mp4/quicktime
stocké directement. Lecteur <video> inline dans NoteCard.

Frontend :
- Bouton vidéo (fa-video) dans les actions de chaque note
- Icônes fa-image / fa-microphone / fa-video / fa-location-dot dans la méta
- Filtres rapides : Photo / Audio / Vidéo / GPS (avec icônes fa)
- Boutons actions migrés vers icônes Font Awesome
- client_max_body_size nginx : 15m → 200m pour les vidéos

v0.5.4

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:31:05 +02:00
gilles 11b5c6c92e docs: ajouter Phase 12 éditeur Markdown notes (idée future)
Barre d'outils flottante (titre, liste, code, photo, audio, GPS)
compatible clavier virtuel iOS/Android. Backend inchangé.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:18:56 +02:00
gilles b3c365d773 fix(notes): GPS lat/lon sérialisé en float au lieu de Decimal
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>
2026-05-25 16:13:01 +02:00
gilles d017a0879e fix(media): corriger import ALLOWED_AUDIO_PREFIXES et strip codec MIME
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>
2026-05-25 16:07:04 +02:00
gilles 454dbadb2f feat(config): section statistiques — BDD, médias, entités
- GET /api/admin/stats : taille BDD (pg_database_size), nb+poids photos/audio
  (scan filesystem), nb notes/todos/listes (requêtes SQL directes)
- ConfigPage : grille 3 colonnes todos/notes/listes + 2 tuiles médias + ligne BDD

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:04:08 +02:00
gilles de9a1e3c73 fix(photo): supprimer capture=environment — accès bibliothèque + appareil photo
Sans cet attribut, iOS affiche le menu natif (Prendre une photo / Bibliothèque /
Fichiers) et Android propose un sélecteur de source.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:57:33 +02:00
gilles 9aaa5fb562 fix(audio+gps): lecture audio multi-navigateur + icône GPS dans tuile note
Audio :
- MediaRecorder détecte le format supporté : webm (Chrome/Firefox) ou mp4 (Safari/iOS)
- Extension sauvegardée correctement (.webm ou .m4a) selon le navigateur
- Backend : ALLOWED_AUDIO_PREFIXES remplace le set strict, strip des codec suffixes

GPS (note card) :
- Icône fa-location-dot (accent vert) avec tooltip lat/lon remplace l'emoji 📍

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:55:35 +02:00
gilles 9de8ad5f3e fix(gps): erreur explicite + saisie manuelle si GPS indisponible (HTTP/laptop)
navigator.geolocation est undefined sur HTTP hors localhost (contexte non sécurisé).
- Message d'erreur visible selon le cas (permission, HTTPS, timeout)
- Fallback : deux champs lat/lon s'affichent automatiquement
- TodoForm : bouton GPS toujours actif (plus disabled sur navigator.geolocation)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:52:41 +02:00
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