# PROMPT DE RÉGÉNÉRATION — « MA MAISON » ## 1. INTENTION Construire une application web française d’inventaire domestique partagé, destinée aux familles et aux personnes qui veulent savoir précisément où sont rangés leurs objets. L’inventaire suit la hiérarchie naturelle **Maison → Pièce → Meuble → Zone facultative → Objet**, avec photos, recherche, QR codes, prêts et historique. Plusieurs comptes peuvent partager une maison, tandis qu’un même compte peut appartenir à plusieurs maisons strictement isolées. Une identification facultative par photographie accélère la création d’un objet grâce au fournisseur d’intelligence artificielle choisi par l’utilisateur. L’application remplace fonctionnellement un service payant de gestion d’inventaire domestique et d’étiquetage QR ; la marque précise visée n’est pas déductible et doit être confirmée par l’auteur. Le produit doit être nommé **Ma Maison**, fonctionner en français, être utilisable sur téléphone comme sur ordinateur et privilégier une navigation fluide sans rechargement complet. --- ## 2. PÉRIMÈTRE FONCTIONNEL ### 2.1 Présentation publique - Un visiteur non connecté voit une page de présentation expliquant : - la vue d’ensemble du foyer ; - le classement des objets ; - l’ajout par photographie ; - les QR codes ; - l’assistant IA ; - le partage familial et l’isolation des données. - Il peut ouvrir une fenêtre de connexion ou d’inscription. - Il peut consulter le guide d’utilisation et la politique de confidentialité sans compte. - Si une invitation est présente dans l’URL, l’inscription est ouverte directement et affiche le nom du foyer concerné. ### 2.2 Inscription classique L’utilisateur renseigne : - son nom ou prénom, de 2 à 100 caractères ; - une adresse e-mail valide et unique, sur 254 caractères maximum ; - un mot de passe fort d’au moins 8 caractères, limité à 72 octets ; - la confirmation exacte du mot de passe ; - le nom du foyer, obligatoire hors invitation et limité à 120 caractères. Le système : - crée et active immédiatement le compte ; - crée le profil nominatif ; - crée une maison dont l’utilisateur devient propriétaire ; - connecte automatiquement l’utilisateur ; - sélectionne cette maison comme maison active ; - ajoute un inventaire de démonstration à une maison réellement nouvelle. Lorsqu’une installation contient une maison historique sans propriétaire, la première inscription classique sans invitation récupère cette maison au lieu d’en créer une nouvelle. En présence d’une invitation valide : - l’adresse e-mail est imposée par l’invitation ; - aucun nouveau foyer n’est créé ; - l’utilisateur rejoint le foyer invité comme membre ; - l’invitation est consommée ; - aucun inventaire de démonstration n’est ajouté. ### 2.3 Connexion et session - Connexion par adresse e-mail et mot de passe. - Option « Rester connecté ». - Déconnexion depuis le menu du profil. - Une session normale dure deux heures. - L’option de mémorisation peut maintenir la connexion pendant trente jours. - Si le compte n’appartient à aucune maison, la connexion est annulée et une erreur explicite est affichée. - Les tentatives de connexion et d’inscription sont limitées afin de freiner les abus. - Aucun parcours de mot de passe oublié n’est disponible. ### 2.4 Connexion Google facultative Lorsque l’intégration Google est configurée : - un visiteur peut créer un compte ou se connecter avec une adresse Google vérifiée ; - une nouvelle inscription Google crée automatiquement « Maison de {nom} », sauf invitation ; - une invitation peut être acceptée pendant le parcours Google ; - un utilisateur déjà inscrit par mot de passe doit d’abord se connecter normalement puis associer Google depuis la rubrique Foyer ; - l’adresse Google vérifiée doit être identique à celle du compte existant ; - une identité Google déjà liée à un autre compte est refusée ; - une adresse déjà inscrite par mot de passe ne provoque jamais de fusion automatique. En cas d’annulation, d’expiration, d’adresse non vérifiée ou d’échec fournisseur, l’utilisateur revient à l’application avec un message adapté. ### 2.5 Maison active et partage - Un compte peut appartenir à plusieurs maisons. - La rubrique **Foyer** affiche : - la maison active ; - toutes les maisons accessibles ; - les membres de la maison active ; - le rôle de chacun ; - l’état de l’association Google. - L’utilisateur peut changer de maison active. - Le changement recharge toutes les données métier dans le nouveau périmètre. - Il n’existe pas d’écran pour créer manuellement une deuxième maison, renommer une maison, retirer un membre ou transférer la propriété. ### 2.6 Invitations Seul le propriétaire d’une maison peut : - saisir l’adresse e-mail d’une personne ; - créer un lien d’invitation ; - copier ce lien ; - consulter les invitations en attente ; - révoquer une invitation. Comportement : - le lien expire après sept jours ; - il est destiné à une adresse e-mail précise ; - le rôle accordé est toujours « membre » ; - recréer une invitation pour la même adresse invalide l’invitation précédente non acceptée ; - une personne déjà membre ne peut pas être réinvitée ; - une invitation expirée, révoquée ou déjà acceptée est inutilisable ; - les invitations ne sont pas envoyées automatiquement par e-mail : le propriétaire partage le lien lui-même. Un utilisateur déjà connecté peut accepter l’invitation si l’adresse de son compte correspond. La maison rejointe devient alors active. ### 2.7 Inventaire de démonstration Une nouvelle maison reçoit : - les pièces Salon et Bureau ; - plusieurs meubles et zones ; - quatre objets d’exemple avec quantités, tags, descriptions et photos. L’accueil signale ces données par un badge « Démo » et propose de les supprimer. La suppression de la démonstration : - supprime les objets marqués comme démonstration ; - supprime les pièces, meubles et zones de démonstration restés vides ; - conserve tout parent de démonstration devenu nécessaire à un contenu réel créé par l’utilisateur ; - transforme alors ce parent en donnée normale ; - ne supprime jamais les données personnelles ajoutées par l’utilisateur. ### 2.8 Tableau de bord L’accueil connecté affiche : - un message de bienvenue utilisant le premier mot du nom de l’utilisateur ; - le nom de la maison active ; - le nombre total d’objets ; - le nombre de pièces ; - le nombre d’objets récents ; - jusqu’à six pièces ; - jusqu’à six objets récemment créés ou modifiés ; - un champ de recherche ; - un bouton d’ajout contextuel. Le bouton d’ajout propose automatiquement : 1. une première pièce si aucune pièce n’existe ; 2. un premier meuble si une pièce existe mais aucun meuble ; 3. un objet dès qu’un meuble existe. ### 2.9 Recherche Sur l’accueil : - la recherche est instantanée dans les données déjà chargées ; - elle examine le nom, la description, les tags et les noms de pièce, meuble et zone ; - elle affiche au maximum huit résultats ; - chaque résultat indique son emplacement ; - sélectionner un résultat ouvre sa fiche ; - un état vide reprend le texte recherché. L’API propose également une recherche limitée à vingt objets, portant sur le nom, les tags et la description, triée du plus récemment modifié au plus ancien. ### 2.10 Pièces L’utilisateur peut : - créer une pièce ; - lui donner un nom ; - choisir une icône dans l’interface historique ; - ajouter, remplacer ou supprimer une photo de couverture ; - ouvrir sa fiche ; - modifier ou supprimer la pièce ; - réordonner les pièces par glisser-déposer ou au clavier. La fiche affiche : - le nom et le badge éventuel « Démo » ; - la photo ou un appel à en ajouter une ; - le nombre d’objets ; - les meubles de la pièce ; - tous les objets rattachés à ses meubles ; - des actions pour modifier la pièce et ajouter un meuble ; - un état vide qui guide vers la création du premier meuble. ### 2.11 Meubles L’utilisateur peut : - créer un meuble dans une pièce ; - modifier son nom ou sa pièce ; - ajouter, remplacer ou supprimer sa photo ; - ouvrir sa fiche ; - supprimer le meuble ; - réordonner les meubles d’une pièce. La fiche affiche : - la pièce parente ; - les zones du meuble ; - les objets sans zone et ceux des zones ; - le nombre de zones et d’objets ; - une action pour ajouter une zone ; - une action QR ; - un état vide permettant d’ajouter directement un objet. Déplacer un meuble vers une autre pièce remet sa position ordonnée à zéro ; il apparaît après les éléments explicitement ordonnés. ### 2.12 Zones Une zone représente un tiroir, une étagère ou un compartiment facultatif. L’utilisateur peut : - créer une zone dans un meuble ; - modifier son nom ou son meuble ; - ajouter, remplacer ou supprimer une photo ; - ouvrir sa fiche ; - supprimer la zone ; - réordonner les zones d’un meuble ; - ajouter directement un objet dans cette zone. La fiche affiche la pièce, le meuble, la photo et la liste ordonnée des objets. Déplacer une zone vers un autre meuble remet son ordre à zéro. ### 2.13 Objets Un objet peut contenir : - un nom ; - une description ; - une quantité ; - des tags libres ; - un meuble ; - éventuellement une zone de ce meuble ; - plusieurs photos ; - le nom de son créateur ; - un destinataire de prêt éventuel. L’utilisateur peut : - créer un objet ; - modifier ses informations ; - le déplacer vers un autre meuble ou une autre zone ; - le supprimer ; - ouvrir une fiche partageable par URL ; - réordonner les objets d’un meuble ou les objets d’une zone ; - ajouter plusieurs photos ; - supprimer individuellement les photos depuis l’interface historique. Dans l’interface historique, séparer plusieurs noms par `|` crée plusieurs objets ayant les mêmes informations. Les photos sélectionnées sont alors associées uniquement au premier objet créé. Le même mécanisme permet la création multiple de meubles et de zones. La fiche objet affiche : - le fil d’Ariane complet ; - toutes les photos ; - le nom, la description, la quantité et les tags ; - l’emplacement ; - le créateur ; - l’état du prêt ; - les actions Modifier, Prêter ou Enregistrer le retour. ### 2.14 Photos - Formats utilisateur acceptés : JPEG, PNG et WebP. - Taille maximale : 8 Mo par fichier. - Résolution source maximale : 25 millions de pixels. - Le navigateur réduit la photo avant certains envois. - Le serveur ramène la plus grande dimension à 1 024 pixels maximum. - La photo stockée est convertie en WebP. - Les photos sont privées et ne peuvent être lues qu’après authentification et contrôle de la maison active. - Une pièce, un meuble ou une zone possède au maximum une photo de couverture. - Un objet peut posséder un nombre non borné de photos. - Ajouter une nouvelle couverture remplace le fichier précédent. - Il n’existe pas de fonction de réorganisation des photos d’un objet. ### 2.15 Ordre manuel Peuvent être réordonnés : - toutes les pièces d’une maison ; - tous les meubles d’une pièce ; - toutes les zones d’un meuble ; - tous les objets d’un meuble ; - les seuls objets d’une zone. Le système exige la liste complète et exacte du périmètre réordonné. Si la liste a changé entre-temps, l’opération est refusée et l’utilisateur doit recharger. Le tri d’une zone modifie seulement l’ordre relatif des objets de cette zone, sans déplacer les objets des autres zones dans l’ordre général du meuble. Le glisser-déposer fonctionne à la souris et au toucher. Au clavier, les flèches déplacent l’élément. Échap annule un déplacement en cours. ### 2.16 Prêts - Un objet disponible peut être prêté à une personne désignée par un texte de 1 à 100 caractères. - L’objet affiche ensuite « Prêté à {personne} ». - Enregistrer le retour vide cette information. - Les prêts en cours restent visibles dans la rubrique Activité, même si l’événement initial a plus de trente jours. - Un prêt et un retour produisent chacun une entrée d’activité. - Aucun fichier de contacts, date d’échéance, rappel ou historique spécialisé des emprunteurs n’est prévu. ### 2.17 QR codes Chaque meuble peut recevoir un jeton QR aléatoire unique. Depuis la fiche du meuble, l’utilisateur peut : - générer le QR s’il n’existe pas ; - voir son image ; - télécharger le QR en PNG ; - l’imprimer avec le nom du meuble et son URL ; - copier son lien. Depuis la rubrique Scanner, il peut : - autoriser la caméra arrière ; - scanner le QR ; - arrêter la caméra ; - coller l’URL complète ou le jeton dans un champ manuel. Résultat : - un QR valide ouvre la fiche du meuble ; - un QR d’une autre maison est traité comme inconnu ; - un visiteur non connecté est envoyé vers la connexion puis doit pouvoir reprendre le lien ; - le QR ne donne jamais un accès public à l’inventaire. ### 2.18 Activité et objets récents Les événements enregistrés sont : - ajout d’objet ; - modification ; - déplacement ; - prêt ; - retour ; - suppression. La rubrique Activité affiche : - les prêts actuellement ouverts ; - au maximum cinquante événements récents ; - uniquement les trente derniers jours ; - l’objet concerné s’il existe encore ; - l’auteur textuel de l’action ; - les détails utiles, notamment ancien et nouvel emplacement lors d’un déplacement. Chaque utilisateur peut vider : - sa propre liste d’activité dans la maison active ; - sa propre liste d’objets récents dans la maison active. Ce vidage : - ne supprime ni objets ni événements pour les autres membres ; - enregistre seulement un instant de masquage propre à l’utilisateur ; - n’empêche pas les actions ultérieures de réapparaître. Les objets récents sont ceux créés ou modifiés dans les trente derniers jours et postérieurs au dernier vidage personnel. ### 2.19 Identification par intelligence artificielle L’utilisateur configure une ou plusieurs clés personnelles parmi : 1. Google Gemini ; 2. OpenAI ; 3. Anthropic Claude ; 4. DeepSeek ; 5. Mistral AI. Pour chaque fournisseur, il peut : - saisir une clé ; - afficher ou masquer temporairement la saisie ; - tester la connexion ; - enregistrer la clé seulement si le test réussit ; - remplacer la clé ; - supprimer la clé ; - modifier la priorité du fournisseur. Le navigateur ne reçoit jamais la clé enregistrée. Il reçoit seulement : - l’état configuré ou non ; - un indice masqué constitué des quatre derniers caractères ; - la priorité. Pour identifier un objet : 1. l’utilisateur choisit une photo JPEG, PNG ou WebP de 8 Mo maximum ; 2. la photo est réduite avant l’envoi ; 3. les fournisseurs configurés sont essayés dans l’ordre personnel ; 4. si l’un échoue, le suivant est essayé automatiquement ; 5. le résultat propose un nom, une catégorie, jusqu’à huit tags, une description de 100 caractères maximum et un niveau de confiance ; 6. l’interface indique le fournisseur ayant répondu et si un secours a été utilisé ; 7. l’utilisateur vérifie et modifie la proposition, choisit l’emplacement puis enregistre l’objet. Sans clé personnelle, l’analyse est désactivée et la configuration des fournisseurs est proposée. Si tous les fournisseurs échouent, l’inventaire manuel reste disponible. ### 2.20 Thèmes et navigation - Thèmes clair et sombre. - Au premier accès, le thème suit la préférence du système. - Le choix manuel est conservé localement sur l’appareil. - Sur ordinateur : navigation latérale. - Sur mobile : menu repliable. - Les routes principales sont : - `/` ; - `/catalogue` ; - `/scanner` ; - `/assistant-ia` ; - `/foyer` ; - `/activite` ; - `/aide` ; - `/confidentialite`. - Les fiches utilisent des URL du type : - `/piece/{id}/{nom-normalise}` ; - `/meuble/{id}/{nom-normalise}` ; - `/zone/{id}/{nom-normalise}` ; - `/objet/{id}/{nom-normalise}`. - Le suffixe lisible est facultatif ; l’identifiant reste la référence. - Retour, avance, rechargement et accès direct doivent conserver la vue. - Le nom normalisé est corrigé dans l’URL après le chargement de la fiche. ### 2.21 Guide d’utilisation Le guide est public et disponible aux utilisateurs connectés. Il comporte : - une recherche ; - des filtres par catégorie ; - des guides pas à pas illustrés ; - un agrandissement des captures ; - une FAQ ; - une progression mémorisée localement. Les sujets couvrent le démarrage, les photos, l’organisation, les objets, la recherche, les QR, l’IA, le foyer, l’activité, les prêts et l’usage mobile. ### 2.22 Politique de confidentialité et contact La page publique explique : - les données de compte et d’inventaire ; - le partage entre membres ; - l’usage facultatif de Google et des fournisseurs IA ; - la conservation ; - les cookies ; - la sécurité ; - les droits relatifs aux données personnelles. Le formulaire demande : - nom ; - e-mail ; - objet ; - message. Le système enregistre le message et affiche une confirmation. Un champ leurre rempli par un robot produit une fausse réussite sans enregistrer le message. Une même adresse IP ne peut soumettre que cinq messages par heure. ### 2.23 Interface historique L’URL `/ancienne-interface` expose encore une interface antérieure utilisant les mêmes données et API. Elle ajoute notamment : - la création multiple par séparateur `|` ; - un sélecteur détaillé d’icônes de pièces ; - la suppression individuelle des photos d’objet ; - des gestes latéraux mobiles pour certaines actions. L’URL `/nouvelle-interface` affiche l’interface principale actuelle. Une régénération strictement équivalente doit conserver ces deux points d’entrée ou fournir toutes les fonctions historiques dans l’interface principale puis rediriger proprement l’ancienne URL. --- ## 3. MODÈLE DE DONNÉES Tous les identifiants sont des entiers positifs auto-incrémentés, sauf indication contraire. Toutes les dates sont stockées avec date et heure. ### 3.1 Compte utilisateur - `id` : entier non signé, clé primaire. - `username` : chaîne de 30 caractères maximum, unique, nullable ; généré techniquement. - `status` : chaîne nullable. - `status_message` : chaîne nullable. - `active` : booléen obligatoire, faux par défaut ; vrai immédiatement après inscription. - `last_active` : date nullable. - `created_at`, `updated_at`, `deleted_at` : dates nullables. Relations : - un compte possède un profil ; - plusieurs identités d’authentification ; - plusieurs adhésions à des maisons ; - plusieurs clés IA ; - éventuellement plusieurs invitations créées ou acceptées. ### 3.2 Identité d’authentification - `id` : clé primaire. - `user_id` : compte obligatoire, indexé, suppression en cascade. - `type` : chaîne obligatoire, notamment `email_password` ou `google`. - `name` : chaîne nullable. - `secret` : chaîne obligatoire : - adresse e-mail pour `email_password` ; - identifiant opaque Google pour `google`. - `secret2` : chaîne nullable, utilisée notamment pour le secret dérivé du mot de passe. - `expires` : date nullable. - `extra` : texte nullable. - `force_reset` : booléen faux par défaut. - `last_used_at`, `created_at`, `updated_at` : dates nullables. - Contrainte unique : `(type, secret)`. Le mot de passe n’est jamais stocké en clair. Il est haché avec l’algorithme de mot de passe recommandé par l’environnement, à coût configurable. ### 3.3 Profil utilisateur - `user_id` : clé primaire et étrangère vers le compte, suppression en cascade. - `nom` : chaîne obligatoire, 100 caractères maximum. - `created_at`, `updated_at` : dates nullables. ### 3.4 Maison - `id` : clé primaire. - `nom` : chaîne obligatoire, 120 caractères maximum. - `owner_user_id` : compte propriétaire initial, nullable, indexé ; devient nul si ce compte est supprimé. - `created_at`, `updated_at` : dates nullables. L’autorisation effective ne doit pas dépendre uniquement de `owner_user_id`, mais de l’adhésion et de son rôle. ### 3.5 Adhésion à une maison - `id` : clé primaire. - `maison_id` : maison obligatoire, indexée, suppression en cascade. - `user_id` : compte obligatoire, indexé, suppression en cascade. - `role` : chaîne obligatoire, `proprietaire` ou `membre`, `membre` par défaut. - `accepted_at` : date nullable ; l’accès exige une valeur non nulle. - `created_at` : date nullable. - `activity_cleared_at` : date nullable. - `recents_cleared_at` : date nullable. - Contrainte unique : `(maison_id, user_id)`. ### 3.6 Invitation - `id` : clé primaire. - `maison_id` : maison obligatoire ; suppression en cascade. - `invited_by_user_id` : compte invitant obligatoire ; suppression de l’invitant supprime l’invitation. - `email` : chaîne obligatoire, normalisée en minuscules, 254 caractères maximum. - `role` : chaîne obligatoire, `membre` par défaut. - `token_hash` : empreinte hexadécimale de 64 caractères, obligatoire et unique. - `expires_at` : date obligatoire. - `accepted_at` : date nullable. - `accepted_by_user_id` : compte nullable ; devient nul si le compte est supprimé. - `created_at` : date obligatoire. - Index composé : `(maison_id, email)`. Le jeton brut n’est jamais stocké ; seul son condensat cryptographique l’est. ### 3.7 Pièce - `id` : clé primaire. - `maison_id` : maison obligatoire, indexée, suppression en cascade. - `is_demo` : booléen obligatoire, faux par défaut. - `nom` : chaîne obligatoire, 1 à 100 caractères. - `icone` : chaîne de 50 caractères maximum, valeur initiale `ti-home`. - `photo` : chemin privé de 255 caractères maximum, nullable. - `ordre` : entier obligatoire, `0` par défaut. - `created_at` : date nullable. - Index composé : `(maison_id, is_demo)`. ### 3.8 Meuble - `id` : clé primaire. - `maison_id` : maison obligatoire, indexée, suppression en cascade. - `is_demo` : booléen obligatoire, faux par défaut. - `piece_id` : pièce obligatoire ; suppression de la pièce supprime le meuble. - `nom` : chaîne obligatoire, 1 à 100 caractères. - `photo` : chemin privé nullable, 255 caractères maximum. - `qr_token` : chaîne hexadécimale nullable de 64 caractères, unique. - `ordre` : entier obligatoire, `0` par défaut. - `created_at` : date nullable. - Index composé : `(maison_id, is_demo)`. ### 3.9 Zone - `id` : clé primaire. - `maison_id` : maison obligatoire, indexée, suppression en cascade. - `is_demo` : booléen obligatoire, faux par défaut. - `meuble_id` : meuble obligatoire ; suppression du meuble supprime la zone. - `nom` : chaîne obligatoire, 1 à 100 caractères. - `photo` : chemin privé nullable, 255 caractères maximum. - `ordre` : entier obligatoire, `0` par défaut. - Index composé : `(maison_id, is_demo)`. ### 3.10 Objet - `id` : clé primaire. - `maison_id` : maison obligatoire, indexée, suppression en cascade. - `is_demo` : booléen obligatoire, faux par défaut. - `zone_id` : zone nullable ; suppression de la zone met ce champ à nul. - `meuble_id` : meuble nullable ; suppression du meuble met ce champ à nul. - `nom` : chaîne obligatoire, 1 à 200 caractères. - `description` : texte nullable. - `quantite` : entier strictement positif, `1` par défaut. - `tags` : chaîne nullable, 500 caractères maximum, tags séparés par des virgules. - `prete_a` : chaîne nullable, 100 caractères maximum. - `ajoute_par` : chaîne nullable, 100 caractères maximum. - `ordre` : entier obligatoire, `0` par défaut. - `created_at`, `updated_at` : dates nullables, mises à jour automatiquement lors des créations et modifications. - Index composé : `(maison_id, is_demo)`. ### 3.11 Photo d’objet - `id` : clé primaire. - `maison_id` : maison obligatoire, indexée, suppression en cascade. - `objet_id` : objet obligatoire ; suppression en cascade. - `chemin` : chaîne obligatoire de 255 caractères. - `ordre` : entier obligatoire, `0` par défaut. L’affichage trie par `ordre`, puis par identifiant. ### 3.12 Activité - `id` : clé primaire. - `maison_id` : maison obligatoire, indexée, suppression en cascade. - `objet_id` : objet nullable ; devient nul à la suppression de l’objet. - `action` : énumération obligatoire : - `ajout` ; - `modification` ; - `deplacement` ; - `pret` ; - `retour` ; - `suppression`. - `detail` : objet JSON nullable. - `par` : chaîne nullable, 100 caractères maximum. - `created_at` : date, indexée. ### 3.13 Identifiant IA personnel - `id` : clé primaire. - `user_id` : compte obligatoire ; suppression en cascade. - `provider` : chaîne obligatoire de 24 caractères maximum. - `encrypted_api_key` : texte chiffré nullable. - `key_hint` : chaîne nullable de 20 caractères maximum. - `priority` : entier positif obligatoire, `1` par défaut. - `created_at`, `updated_at` : dates nullables. - Contrainte unique : `(user_id, provider)`. Valeurs de fournisseur autorisées : - `gemini` ; - `openai` ; - `anthropic` ; - `deepseek` ; - `mistral`. ### 3.14 Message de contact - `id` : clé primaire. - `name` : chaîne obligatoire, 2 à 100 caractères. - `email` : chaîne obligatoire, e-mail valide, 254 caractères maximum. - `subject` : chaîne obligatoire, 3 à 120 caractères. - `message` : texte obligatoire, 10 à 5 000 caractères. - `created_at` : date obligatoire et indexée. ### 3.15 Données techniques d’authentification Prévoir également : - un journal des tentatives de connexion avec adresse IP, agent utilisateur, identifiant tenté, compte éventuel, date et succès ; - des jetons persistants de « rester connecté », avec sélecteur unique, validateur haché, compte et expiration ; - une association compte-groupe ; - une association compte-permission ; - un stockage de session côté serveur ou équivalent sécurisé. Ces tables peuvent différer selon la technologie choisie si les garanties de sécurité et les durées restent équivalentes. --- ## 4. RÈGLES MÉTIER ### 4.1 Isolation des maisons - Toute donnée d’inventaire appartient obligatoirement à une maison. - La maison active est déterminée côté serveur depuis la session. - Aucun identifiant de maison fourni par le client ne doit être accepté comme autorité. - Toute lecture, recherche, modification, suppression, photo, QR ou opération de tri doit vérifier la maison active. - Une ressource d’une autre maison répond comme inexistante avec `404`, jamais avec un message révélant son existence. - Un parent fourni lors d’une création ou d’un déplacement doit appartenir à la même maison. - Une zone sélectionnée impose son propre meuble. - Si un meuble et une zone sont fournis, la zone doit appartenir à ce meuble. - Un utilisateur ne peut sélectionner comme maison active qu’une maison dont l’adhésion a été acceptée. ### 4.2 Autorisations par rôle Tous les membres acceptés peuvent : - lire l’inventaire ; - créer, modifier, déplacer, réordonner et supprimer pièces, meubles, zones et objets ; - gérer les photos ; - générer et utiliser les QR ; - enregistrer prêts et retours ; - consulter l’activité ; - vider leur propre historique ; - supprimer les données de démonstration ; - utiliser leurs propres clés IA. Seul le propriétaire peut : - créer une invitation ; - voir les invitations en attente ; - révoquer une invitation. Il n’existe pas d’autorisation plus fine ni de rôle en lecture seule. ### 4.3 Maison active - À la première requête connectée, si aucune maison active valide n’est en session, choisir la première maison accessible triée alphabétiquement. - Conserver cet identifiant en session. - Si l’identifiant mémorisé n’est plus accessible, revenir à la première maison autorisée. - Refuser un changement vers une maison inaccessible par `404`. ### 4.4 Validation des noms et quantités - Maison : 1 à 120 caractères. - Pièce, meuble, zone : 1 à 100 caractères. - Objet : 1 à 200 caractères. - Icône de pièce : 50 caractères maximum. - Quantité : entier strictement supérieur à zéro. - Destinataire d’un prêt : 100 caractères maximum. - Les valeurs sont nettoyées des espaces de début et de fin lorsqu’elles sont saisies par les parcours principaux. ### 4.5 Champs contrôlés par le serveur Le client ne peut jamais imposer : - `maison_id` ; - `is_demo` ; - l’auteur de l’objet ; - l’auteur d’une activité. À la création d’un objet, le nom du profil connecté est copié dans `ajoute_par`. ### 4.6 Emplacements et suppressions - L’interface principale exige un meuble pour créer un objet. - L’API accepte néanmoins un objet sans meuble ni zone. - Supprimer une zone conserve ses objets et met seulement leur zone à nul. - Supprimer un meuble supprime ses zones mais conserve les objets, qui perdent leur meuble et leur zone. - Supprimer une pièce supprime ses meubles et zones, mais les objets descendants sont conservés sans emplacement. - Supprimer un objet supprime ses photos. - L’activité liée à un objet supprimé reste conservée jusqu’à expiration, avec un identifiant d’objet nul. - Ces conséquences doivent être indiquées honnêtement dans toute confirmation de suppression. ### 4.7 Ordre - Les valeurs d’ordre commencent à 1 après un tri explicite. - La valeur `0` signifie « non explicitement ordonné » et place l’élément après ceux ayant un ordre positif. - À ordre égal, trier alphabétiquement par nom. - Déplacer un meuble, une zone ou un objet vers un autre parent remet son ordre à `0`. - Une requête de tri doit contenir entre 1 et 1 000 identifiants entiers positifs, sans doublon. - La liste doit correspondre exactement à tous les éléments actuels du périmètre. - Le tri doit être transactionnel. - Le tri ne doit pas modifier la date fonctionnelle de dernière modification d’un objet. ### 4.8 Recherche et récence - La recherche ne traverse jamais les maisons. - Une recherche vide revient à une liste filtrée normale. - La recherche API renvoie au maximum vingt résultats. - La liste récente se base sur la plus récente des dates de création ou modification. - La fenêtre de récence est strictement de trente jours. - Le vidage utilise le maximum entre la limite des trente jours et le marqueur personnel de vidage. - La liste d’accueil renvoie six objets maximum, mais son compteur porte sur tous les objets visibles dans la fenêtre. ### 4.9 Activité - Purger physiquement les activités âgées de plus de trente jours lors d’un nouvel événement ou d’une consultation. - Limiter la consultation à cinquante événements. - Les événements sont triés par date décroissante puis identifiant décroissant. - Le vidage ne supprime pas les lignes : il met à jour le marqueur personnel de l’adhésion. - Les prêts ouverts sont dérivés de `prete_a`, pas du journal d’activité. ### 4.10 Photos - Vérifier le type MIME réel, pas seulement l’extension. - Refuser tout format autre que JPEG, PNG ou WebP pour le stockage. - Refuser un fichier supérieur à 8 Mo. - Refuser une image illisible ou supérieure à 25 millions de pixels. - Conserver le ratio d’aspect. - Ne jamais agrandir une petite image. - Convertir en WebP avec une qualité d’environ 82 %. - Utiliser un nom aléatoire non devinable. - Stocker les fichiers en dehors de l’arborescence publiquement accessible. - Une URL de média doit comporter le type, l’identifiant de l’entité et un nom de fichier sûr. - Avant de servir le fichier, vérifier que son chemin est exactement référencé par l’entité de la maison active. - Répondre `404` pour un fichier absent, non référencé ou hors maison. - Servir avec type MIME exact, longueur, cache privé et interdiction de détection de type. - Lors du remplacement d’une couverture, supprimer l’ancien fichier. - Lors de la suppression d’une photo, supprimer la ligne et le fichier. ### 4.11 QR - Le jeton est composé de 64 caractères hexadécimaux aléatoires. - Il est unique. - Générer un nouveau jeton lorsqu’une régénération est demandée explicitement par l’API. - L’interface réutilise le jeton existant. - Le jeton ne transporte ni identifiant de maison ni autorisation. - L’accès reste soumis à la session et à la maison active. - Une requête JSON anonyme reçoit `401`. - Une navigation HTML anonyme est redirigée vers la connexion avec l’URL de reprise. - Un token inconnu en JSON renvoie `404`; en navigation HTML, il redirige vers l’accueil. ### 4.12 Prêts - Le champ `prete_a` doit être présent et être une chaîne. - Une chaîne vide signifie retour. - Une chaîne non vide signifie prêt. - Enregistrer systématiquement l’événement correspondant. - Un retour est autorisé même si l’objet n’était pas marqué comme prêté. - Aucune unicité n’existe sur le nom de l’emprunteur. ### 4.13 Invitations - Le jeton brut est aléatoire sur 32 octets et représenté par 64 caractères hexadécimaux. - Stocker uniquement son empreinte. - Vérifier simultanément : - la syntaxe ; - l’empreinte ; - l’absence d’acceptation ; - la date d’expiration ; - l’égalité exacte avec l’e-mail normalisé. - L’acceptation et la création de l’adhésion doivent être transactionnelles. - Si l’adhésion existe déjà, ne pas en créer une seconde, mais consommer l’invitation. - Une invitation consommée ne peut pas être réutilisée. ### 4.14 Inscription et données historiques - La création du compte, du profil, de la maison ou de l’adhésion et de l’inventaire de démonstration doit être atomique. - En cas d’échec, ne laisser ni compte partiel ni maison orpheline. - Lors de la récupération d’une maison historique sans propriétaire, verrouiller la sélection afin que deux inscriptions simultanées ne puissent pas la réclamer. - Une inscription Google ne récupère pas une maison historique sans propriétaire : elle crée son propre foyer. ### 4.15 Mots de passe et authentification - Normaliser l’e-mail en minuscules. - Refuser les doublons. - Limiter le mot de passe à 72 octets. - Exiger au moins 8 caractères et rejeter les mots de passe trop faibles, trop courants ou trop proches des informations personnelles. - Hacher le mot de passe avec un algorithme adaptatif et un coût configurable. - Utiliser des cookies `Secure`, `HttpOnly` et `SameSite=Lax`. - Régénérer périodiquement l’identifiant de session. - Protéger toute mutation par un jeton anti-CSRF lié à la session. - Le client doit actualiser le jeton anti-CSRF lorsqu’une réponse en fournit un nouveau. ### 4.16 Google OAuth - Utiliser un paramètre d’état aléatoire, stocké uniquement sous forme hachée. - Utiliser une preuve PKCE. - Le parcours expire après dix minutes. - Demander seulement identité OpenID, e-mail et profil. - Exiger une adresse e-mail vérifiée. - Limiter la taille du code d’autorisation reçu. - Ne jamais associer automatiquement un compte Google à un compte mot de passe portant le même e-mail. - L’association volontaire exige une session existante et une adresse strictement identique. ### 4.17 Clés IA - Une clé doit contenir entre 20 et 512 caractères et aucun espace. - La tester auprès du fournisseur avant de l’enregistrer. - Chiffrer la clé au repos avec une clé maîtresse distincte de la base. - Ne jamais renvoyer la valeur en clair au navigateur. - Afficher seulement les quatre derniers caractères. - Isoler les clés par compte, non par maison. - Chaque couple compte-fournisseur est unique. - Supprimer une clé revient à effacer sa valeur chiffrée et son indice, tout en pouvant conserver sa priorité. - L’ordre fourni par le client est filtré sur les cinq fournisseurs reconnus, dédupliqué puis complété par les fournisseurs manquants. - Seules les clés personnelles sont utilisées ; il n’existe pas de clé partagée de secours. ### 4.18 Analyse IA - Accepter une image encodée en base64, avec ou sans préfixe d’URL de données. - L’API accepte JPEG, PNG, WebP et GIF valide ; l’interface utilisateur ne propose que JPEG, PNG et WebP. - Essayer les fournisseurs dans l’ordre de priorité. - Interrompre dès la première réponse valide. - Une réponse doit contenir au minimum un nom non vide. - Extraire un objet JSON même si le fournisseur ajoute accidentellement des délimiteurs ou du texte périphérique. - Limiter les tags à huit. - Limiter la description à 100 caractères. - Ramener une confiance inconnue à `moyenne`. - Ne jamais exposer les détails techniques ou les clés dans les erreurs publiques. - Si aucune clé n’est disponible, répondre avec le code fonctionnel `AI_CREDENTIAL_REQUIRED`. - Si toutes les clés échouent, répondre que tous les services sont temporairement indisponibles. ### 4.19 Contact - Le champ leurre `website` doit rester vide. - S’il est rempli, répondre comme si l’enregistrement avait réussi. - Limiter à cinq soumissions par adresse IP sur une heure. - Ne pas enregistrer les données invalides. - Renvoyer les erreurs près de chaque champ. - Une erreur de stockage produit une erreur générique et un journal technique. ### 4.20 Erreurs et concurrence - `401` : session absente sur une API privée. - `403` : action authentifiée mais rôle ou adresse incompatible. - `404` : ressource absente ou appartenant à une autre maison. - `409` : état incompatible, tel qu’un utilisateur déjà membre ou déjà connecté. - `422` : données invalides ou incohérence métier. - `429` : limite de contact dépassée. - `500` : erreur transactionnelle ou de stockage interne. - `502` : tous les fournisseurs IA ont échoué. - Les formulaires conservent les valeurs saisies après une erreur. - Une erreur de validation s’affiche sous le champ concerné ; une erreur globale utilise un message visible ou une notification. - Une mutation en cours désactive son bouton et affiche un indicateur de progression. --- ## 5. INTERFACES ### 5.1 Écrans #### Présentation et authentification - Page marketing publique avec en-tête, introduction, démonstrations illustrées et appels à l’action. - Fenêtre Connexion/Inscription accessible au clavier. - États : - chargement initial ; - serveur inaccessible avec bouton Réessayer ; - connexion incorrecte ; - inscription fermée ; - invitation valide, invalide ou expirée ; - intégration Google indisponible ; - retour d’erreur Google. #### Accueil - Salutation, maison active, recherche, compteurs, pièces et objets récents. - États vides distincts : - aucune pièce ; - pièce sans meuble ; - aucun objet ; - objets existants mais aucun objet récent ; - recherche sans résultat. - Erreur de chargement avec bouton Réessayer. - Suppression de la démonstration après confirmation. #### Catalogue - Liste réordonnable de toutes les pièces. - Bouton Ajouter une pièce. - État vide cliquable. #### Fiches d’inventaire - Fil d’Ariane. - En-tête avec retour et actions. - Photo ou emplacement vide cliquable. - Listes d’enfants réordonnables. - États vides guidant vers l’action pertinente. - État Introuvable si l’entité n’existe plus ou n’est pas accessible. #### Formulaire d’inventaire Selon le type : - nom ; - pièce ; - meuble ; - zone facultative ; - description ; - quantité ; - tags ; - une ou plusieurs photos. Actions : - Enregistrer ; - Annuler ; - Supprimer en modification. Le formulaire est une fenêtre centrée sur ordinateur et doit rester utilisable sur petit écran, avec contenu défilant et actions accessibles. #### Assistant IA - Zone de capture ou sélection de photo. - Prévisualisation. - État « Analyse en cours ». - Résultat avec nom, confiance, description, tags et fournisseur. - Bouton ouvrant le formulaire d’objet prérempli. - Panneau des cinq fournisseurs avec état, clé masquée, test, sauvegarde, suppression et ordre. - État sans clé configurée. #### Foyer - Liste des membres et rôles. - Liste des maisons accessibles. - Association Google. - Invitation réservée au propriétaire. - Invitations en attente et révocation. - États de chargement et d’erreur. #### Scanner - Bouton d’ouverture et d’arrêt de caméra. - Cadre de scan. - Champ de saisie manuelle. - États : - caméra non démarrée ; - permission refusée ou caméra absente ; - QR invalide ; - QR inconnu ; - résolution en cours. #### Activité - Prêts en cours. - Historique récent. - Bouton Vider avec confirmation. - État sans prêt. - État sans activité. #### Aide - Accessible sans connexion. - Bibliothèque de sujets, recherche, filtres, progression et FAQ. - Détail pas à pas avec images, navigation précédent/suivant et zoom. #### Confidentialité - Accessible sans connexion. - Sections juridiques lisibles. - Formulaire de contact. - Confirmation, erreurs par champ et limitation de débit. ### 5.2 Convention générale de l’API Les réponses JSON métier utilisent : ```json { "success": true, "data": {} } ``` En cas d’erreur : ```json { "success": false, "error": "Message lisible", "errors": { "champ": "Erreur du champ" } } ``` Les réponses d’authentification et de contact peuvent également inclure : ```json { "csrf": { "header": "Nom de l’en-tête", "hash": "Jeton" } } ``` ### 5.3 API publique | Verbe et chemin | Requête | Réponse et statuts | |---|---|---| | `GET /api/auth/session` | Aucune | `200`, état d’authentification, disponibilité Google et jeton CSRF. Si connecté : utilisateur, maison active, maisons accessibles et état Google. | | `POST /api/auth/login` | JSON `{email, password, remember}` | `200` avec session ; `422` champs absents ; `401` identifiants incorrects ; `403` aucune maison accessible. | | `POST /api/auth/register` | JSON `{nom, email, password, password_confirm, maison_nom, invitation_token?}` | `201` avec session ; `409` déjà connecté ; `403` inscriptions fermées ; `422` validation ou invitation ; `500` échec atomique. | | `GET /api/auth/google?link=0|1&invitation=…` | Navigation | Redirection vers Google ou retour vers l’application avec un code d’erreur. | | `GET /api/auth/google/callback` | Paramètres OAuth | Redirection vers `/` avec `google=connected`, `google=linked` ou `google_error={code}`. | | `GET /api/auth/invitations/preview/{token}` | Token hexadécimal | `200` avec `{maison_nom,email,expires_at}` ; `404` invalide ou expiré. | | `POST /api/privacy/contact` | JSON `{name,email,subject,message,website}` | `200` confirmation ; `422` erreurs de champs ; `429` trop de messages ; `500` stockage impossible. | | `POST /confidentialite/contact` | Formulaire HTML équivalent | Redirection vers la section contact avec messages temporaires. | | `GET /qr/{token}` | En-tête `Accept` facultatif | JSON `200 {meuble_id}`, `401`, `404`, ou redirection HTML vers connexion, meuble ou accueil. | ### 5.4 API privée de session et foyer | Verbe et chemin | Requête | Réponse | |---|---|---| | `GET /api/me` | Aucune | `200`, même structure que la session connectée. | | `POST /api/auth/logout` | Aucune | `200`, session anonyme et nouveau CSRF. | | `POST /api/auth/switch-maison` | `{maison_id: entier}` | `200` session actualisée ; `404` maison inaccessible. | | `GET /api/members` | Aucune | `{members, pending, can_invite}` ; les invitations en attente ne sont renvoyées qu’au propriétaire. | | `POST /api/invitations` | `{email}` | `201 {email,expires_at,url}` ; `403` non propriétaire ; `409` déjà membre ; `422` e-mail invalide. | | `POST /api/invitations/accept/{token}` | Aucune | `200 {maison_id,maison_nom}` ; `403` mauvaise adresse ; `404` invalide ou expirée ; `500` transaction. | | `DELETE /api/invitations/{id}` | Aucune | `200 {revoked}` ; `403` non propriétaire. | ### 5.5 API des pièces | Verbe et chemin | Requête | Réponse | |---|---|---| | `GET /api/pieces` | Aucune | `200`, pièces de la maison avec `nb_objets`, triées par ordre puis nom. | | `POST /api/pieces` | `{nom, icone?}` | `201`, pièce créée ; `422` validation. | | `PUT /api/pieces/{id}` | Sous-ensemble `{nom, icone}` | `200`, pièce mise à jour ; `404` absente ; `422` validation. | | `DELETE /api/pieces/{id}` | Aucune | `200 {id}` ; `404`. | | `POST /api/pieces/{id}/photo` | Multipart `photo` | `200 {photo}` ; `404` ; `422` fichier. | | `DELETE /api/pieces/{id}/photo` | Aucune | `200 {id}` ; `404`. | ### 5.6 API des meubles | Verbe et chemin | Requête | Réponse | |---|---|---| | `GET /api/meubles?piece_id={id}` | Filtre facultatif | `200`, meubles ; `404` si la pièce est inaccessible. | | `GET /api/meubles/{id}` | Aucune | `200`, meuble avec pièce, zones, objets de chaque zone et objets sans zone ; `404`. | | `POST /api/meubles` | `{piece_id, nom}` | `201` ; `422` pièce ou données invalides. | | `PUT /api/meubles/{id}` | Sous-ensemble `{piece_id, nom}` | `200` ; `404` ; `422`. | | `DELETE /api/meubles/{id}` | Aucune | `200 {id}` ; `404`. | | `POST /api/meubles/{id}/qr` | Aucune | `200 {qr_token,url}` ; `404`. | | `POST /api/meubles/{id}/photo` | Multipart `photo` | `200 {photo}` ; `404` ; `422`. | | `DELETE /api/meubles/{id}/photo` | Aucune | `200 {id}` ; `404`. | ### 5.7 API des zones | Verbe et chemin | Requête | Réponse | |---|---|---| | `GET /api/zones?meuble_id={id}` | Filtre facultatif | `200`, zones ; `404` si le meuble est inaccessible. | | `GET /api/zones/{id}` | Aucune | `200`, zone avec pièce, meuble et objets ; `404`. | | `POST /api/zones` | `{meuble_id, nom}` | `201` ; `422`. | | `PUT /api/zones/{id}` | Sous-ensemble `{meuble_id, nom}` | `200` ; `404` ; `422`. | | `DELETE /api/zones/{id}` | Aucune | `200 {id}` ; `404`. | | `POST /api/zones/{id}/photo` | Multipart `photo` | `200 {photo}` ; `404` ; `422`. | | `DELETE /api/zones/{id}/photo` | Aucune | `200 {id}` ; `404`. | ### 5.8 API des objets et photos | Verbe et chemin | Requête | Réponse | |---|---|---| | `GET /api/objets` | `q`, `zone_id` ou `meuble_id` facultatifs | `200`, objets avec chemin et première photo ; `404` pour filtre hors maison. | | `GET /api/objets/recents` | Aucune | `200 {items,total}`. | | `DELETE /api/objets/recents` | Aucune | `200 {cleared:true}`. | | `GET /api/objets/{id}` | Aucune | `200`, objet avec emplacement et toutes ses photos ; `404`. | | `POST /api/objets` | `{nom,description?,quantite?,tags?,meuble_id?,zone_id?}` | `201`, objet enrichi ; `422`. | | `PUT /api/objets/{id}` | Sous-ensemble des champs précédents | `200`, objet enrichi ; `404` ; `422`. | | `DELETE /api/objets/{id}` | Aucune | `200 {id}` ; `404`. | | `PUT /api/objets/{id}/deplacer` | `{meuble_id:null|entier,zone_id:null|entier}` | `200`, objet déplacé ; `404` ; `422`. | | `PUT /api/objets/{id}/preter` | `{prete_a:"nom"}` ou `{prete_a:""}` | `200`, objet ; `404` ; `422`. | | `POST /api/photos` | Multipart `objet_id`, `photo` | `201 {id,chemin}` ; `404` objet ; `422` fichier. | | `DELETE /api/photos/{id}` | Aucune | `200 {id}` ; `404`. | ### 5.9 Tri et médias | Verbe et chemin | Requête | Réponse | |---|---|---| | `PUT /api/order/{pieces\|meubles\|zones\|objets}` | `{ids:[entiers],parent_id:null|entier,scope?:"meuble"|"zone"}` | `200 {ids}` ; `422` liste, type, parent ou concurrence ; `500` transaction. | | `GET /api/media/{pieces\|meubles\|zones\|objets}/{entityId}/{filename}` | Aucune | `200` binaire privé ; `404` absent ou non autorisé. | | `DELETE /api/demo` | Aucune | `200 {removed:{objets,zones,meubles,pieces}}` ; `500`. | ### 5.10 IA | Verbe et chemin | Requête | Réponse | |---|---|---| | `GET /api/ia/settings` | Aucune | `200 {providers,service_available}`. | | `PUT /api/ia/settings` | `{provider,api_key}` | `200`, statut complet ; `422` fournisseur, clé ou connexion. | | `DELETE /api/ia/settings/{provider}` | Aucune | `200`, statut complet ; `404` fournisseur inconnu. | | `POST /api/ia/settings/test` | `{provider,api_key?}` | `200 {valid:true,provider}` ; `422`. Une clé omise utilise la clé enregistrée. | | `PUT /api/ia/settings/order` | `{order:[provider,…]}` | `200`, statut complet ; `422` forme invalide. | | `POST /api/ia/identify` | `{image:"base64 ou data URL",media_type?}` | `200` proposition avec `_ia:{provider,source,fallback_used}` ; `422` image ou clé absente ; `502` fournisseurs indisponibles. | ### 5.11 Activité | Verbe et chemin | Requête | Réponse | |---|---|---| | `GET /api/activite?action={type}` | Filtre facultatif | `200`, au maximum 50 événements des 30 derniers jours. | | `DELETE /api/activite` | Aucune | `200 {cleared:true}`. | --- ## 6. CONTRAINTES TECHNIQUES ### 6.1 Contraintes réellement nécessaires #### Base relationnelle Une base relationnelle transactionnelle est nécessaire pour : - les relations hiérarchiques ; - les suppressions en cascade ou mises à nul ; - les contraintes uniques ; - les invitations atomiques ; - l’isolation multi-maison ; - le tri concurrent ; - les champs JSON de l’activité. MariaDB ou MySQL est le choix directement compatible. Une autre base relationnelle est acceptable si elle reproduit exactement les contraintes, transactions, tris et types JSON. #### Hébergement L’hébergement doit fournir : - HTTPS ; - réécriture des URL vers le point d’entrée de la SPA ; - sessions serveur ; - stockage persistant privé en écriture ; - accès sortant HTTPS vers Google et les fournisseurs IA ; - traitement d’images JPEG, PNG et WebP ; - génération de nombres aléatoires cryptographiquement sûrs ; - tâches de migration de schéma ; - sauvegarde coordonnée de la base et des médias. HTTPS est indispensable aux cookies sécurisés, à OAuth et à l’accès caméra des navigateurs. #### Variables et secrets Variables nécessaires : - URL publique canonique de l’application ; - environnement d’exécution ; - hôte, port, base, utilisateur et mot de passe de la base ; - clé maîtresse de chiffrement des clés IA ; - identifiant client Google OAuth, facultatif ; - secret client Google OAuth, facultatif. L’URL de rappel Google doit être : `{URL_PUBLIQUE}/api/auth/google/callback` Les clés personnelles Gemini, OpenAI, Anthropic, DeepSeek et Mistral viennent des utilisateurs et ne sont pas des variables serveur partagées. #### Stockage des médias - Les photos doivent être conservées hors du répertoire public. - Le serveur doit pouvoir convertir en WebP et redimensionner. - Le stockage doit survivre aux redéploiements. - L’accès doit passer par une route autorisée. - Une sauvegarde de la base sans les médias, ou inversement, est insuffisante. #### Sécurité - Authentification par session. - Protection CSRF sur toutes les mutations, y compris publiques. - Cookies sécurisés, non accessibles au JavaScript et `SameSite=Lax`. - Filtrage systématique par maison. - Jetons d’invitation et QR non prédictibles. - Chiffrement authentifié des clés IA au repos. - Limitation des tentatives d’authentification et du formulaire de contact. - Validation MIME et taille des fichiers. - Journalisation technique sans secret. ### 6.2 Intégrations externes - Google OpenID Connect/OAuth 2.0 pour la connexion facultative. - Google Gemini pour l’analyse d’image. - OpenAI pour l’analyse d’image. - Anthropic Claude pour l’analyse d’image. - DeepSeek pour l’analyse d’image. - Mistral AI pour l’analyse d’image. Le modèle précis appelé peut évoluer si le fournisseur retire celui utilisé, à condition de conserver : - la prise en charge des images ; - la réponse structurée ; - le test préalable de la clé ; - le résultat fonctionnel décrit ; - le repli entre fournisseurs. ### 6.3 Contraintes d’interface - Application monopage avec URLs navigables. - Aucune mutation métier ne doit recharger le document entier. - Navigation arrière et avant fonctionnelle. - Interface responsive sans débordement horizontal jusqu’à 320 pixels de large. - Contrôles tactiles d’environ 44 pixels pour les actions principales. - Focus visible et navigation clavier. - Respect de la préférence de réduction des animations. - Modales fermables par Échap lorsqu’aucune opération bloquante n’est en cours. - Pas d’empilement de fenêtres modales. - Thèmes clair et sombre persistants localement. - Les données existantes peuvent rester visibles pendant une actualisation, avec un état de progression discret. ### 6.4 Choix d’opportunité, remplaçables Les éléments suivants ne sont pas indispensables à l’équivalence fonctionnelle : - langage PHP ; - framework serveur CodeIgniter ; - bibliothèque d’authentification particulière ; - React ; - Mantine ; - Bootstrap ; - bibliothèque d’icônes ; - Vite ; - solution de notifications ; - bibliothèque exacte de génération ou lecture QR ; - noms de classes CSS ; - architecture interne des composants. Une autre stack est acceptable si elle respecte le modèle, les règles, les interfaces, la sécurité et les critères d’acceptation. ### 6.5 Exploitation Prévoir : - une commande de migration idempotente ; - une procédure de création du premier compte ; - une purge régulière ou opportuniste des activités de plus de trente jours ; - une sauvegarde de la base et du stockage privé ; - des journaux d’erreur ; - un environnement de test isolé ; - des tests automatisés d’isolation entre maisons. --- ## 7. CRITÈRES D’ACCEPTATION ### Authentification 1. Un visiteur anonyme obtient `401` sur toute API métier privée. 2. Un visiteur peut consulter l’aide et la confidentialité sans compte. 3. Une inscription valide crée le compte, le profil, la maison et l’adhésion propriétaire dans une seule transaction. 4. Une inscription invalide ne laisse aucune donnée partielle. 5. Deux comptes ne peuvent pas partager la même identité e-mail. 6. Un mot de passe faible ou différent de sa confirmation est refusé. 7. Une connexion incorrecte renvoie `401` sans préciser si l’e-mail existe. 8. La déconnexion invalide la session métier. 9. « Rester connecté » maintient la connexion au-delà de la session normale. 10. Toute mutation sans jeton CSRF valide est refusée. ### Google 11. Le bouton Google est désactivé lorsque l’intégration n’est pas configurée. 12. Une adresse Google non vérifiée est refusée. 13. Un compte mot de passe existant n’est jamais fusionné automatiquement avec Google. 14. L’association Google exige une session et la même adresse e-mail. 15. Un état OAuth expiré ou différent est refusé. 16. Un compte Google nouveau crée une maison, sauf invitation valide. ### Maisons et membres 17. Alice ne peut ni lister, lire, modifier, supprimer, trier ni télécharger une donnée appartenant uniquement à Bob. 18. Une ressource étrangère répond `404`. 19. Un identifiant de maison envoyé arbitrairement par le client ne change pas le périmètre. 20. Le changement de maison remplace toutes les données affichées. 21. Un membre peut modifier l’inventaire partagé. 22. Un membre non propriétaire ne peut ni créer ni révoquer une invitation. 23. Une invitation expire exactement après sept jours. 24. Une invitation ne peut être acceptée que par son adresse e-mail. 25. Une invitation acceptée ou révoquée ne peut pas être réutilisée. 26. Un utilisateur déjà membre ne reçoit pas une seconde adhésion. ### Inventaire 27. Une pièce valide peut être créée, modifiée, illustrée, réordonnée et supprimée. 28. Un meuble ne peut être créé que dans une pièce de la maison active. 29. Une zone ne peut être créée que dans un meuble de la maison active. 30. Une zone fournie pour un objet impose son meuble réel. 31. Une combinaison meuble-zone incohérente est refusée par `422`. 32. La quantité zéro, négative ou non entière est refusée. 33. Un déplacement vers un autre parent remet l’ordre de l’élément à zéro. 34. Supprimer une zone conserve ses objets sans zone. 35. Supprimer un meuble conserve ses objets sans emplacement. 36. Supprimer une pièce conserve ses objets sans emplacement. 37. Supprimer un objet supprime ses photos mais conserve son activité jusqu’à expiration. 38. Les champs `maison_id`, `is_demo` et `ajoute_par` envoyés par le client ne peuvent pas contourner les valeurs serveur. ### Tri 39. Le tri des pièces persiste après rechargement. 40. Le tri des meubles, zones et objets persiste après rechargement. 41. Le tri est partagé entre membres d’une même maison. 42. Une liste partielle, dupliquée, étrangère ou devenue obsolète est refusée. 43. Le tri des objets d’une zone conserve les positions relatives des objets extérieurs à cette zone. 44. Le glisser-déposer fonctionne à la souris et au toucher. 45. Les flèches du clavier permettent le même tri. 46. Échap annule un déplacement sans requête d’enregistrement. ### Recherche et écrans 47. La recherche d’accueil trouve un objet par nom, description, tag, pièce, meuble ou zone. 48. Elle n’affiche jamais d’objet d’une autre maison. 49. Une recherche sans résultat affiche un état vide explicite. 50. Un lien direct vers chaque fiche fonctionne après rechargement. 51. Retour et avance du navigateur restaurent la vue attendue. 52. Une fiche supprimée affiche un état Introuvable. 53. Aucun écran principal ne déborde horizontalement à 320, 390, 768 ou 1 365 pixels. 54. Les principaux parcours sont utilisables entièrement au clavier. 55. Les thèmes clair et sombre restent lisibles et le choix persiste après rechargement. ### Photos 56. Un JPEG, PNG ou WebP valide inférieur à 8 Mo est accepté. 57. Un autre type, un fichier trop grand, illisible ou excessivement défini est refusé. 58. Une image de 1 600 × 800 est stockée en WebP de 1 024 × 512. 59. Une image plus petite n’est pas agrandie. 60. Remplacer une couverture supprime l’ancien fichier. 61. Une photo privée est lisible par un membre de sa maison. 62. La même URL renvoie `404` dans une autre maison. 63. Une tentative de traversée de répertoire est refusée. 64. Plusieurs photos d’objet sont affichées dans leur ordre. ### QR 65. La génération produit un jeton hexadécimal unique de 64 caractères. 66. Le QR téléchargé et imprimé encode l’URL du meuble. 67. Scanner le QR en étant membre ouvre le bon meuble. 68. Le même QR est inconnu dans une autre maison. 69. Un utilisateur anonyme est renvoyé vers la connexion. 70. Un token manuel mal formé est rejeté avant la résolution. 71. Le scanner présente une solution manuelle si la caméra échoue. ### Prêts et activité 72. Un prêt sans nom est refusé. 73. Un nom de plus de 100 caractères est refusé. 74. Un prêt affiche immédiatement l’emprunteur sur la fiche et dans les prêts en cours. 75. Un retour retire immédiatement l’objet des prêts en cours. 76. Ajout, modification, déplacement, prêt, retour et suppression produisent le bon type d’activité. 77. Les activités de plus de trente jours ne sont plus visibles et sont purgées. 78. La consultation renvoie au maximum cinquante événements. 79. Vider l’activité d’Alice ne la masque pas pour Bob. 80. Vider les récents ne supprime aucun objet. 81. Un changement ultérieur réaffiche l’objet ou l’activité après le marqueur de vidage. ### Démonstration 82. Une nouvelle maison non invitée reçoit les données de démonstration. 83. Une maison rejointe sur invitation n’en reçoit pas. 84. La suppression de démonstration retire tous les objets de démonstration. 85. Un parent de démonstration contenant une donnée réelle est conservé et n’est plus marqué comme démonstration. 86. Aucune donnée personnelle n’est supprimée par cette action. ### Intelligence artificielle 87. Le statut IA affiche toujours les cinq fournisseurs dans un ordre déterministe. 88. Une clé invalide ou refusée n’est pas enregistrée. 89. Une clé valide est stockée chiffrée et n’apparaît jamais en clair dans la base ou les réponses. 90. Un autre utilisateur ne peut ni voir ni utiliser cette clé. 91. Supprimer une clé désactive ce fournisseur pour le compte. 92. Réordonner les fournisseurs change l’ordre des tentatives. 93. Si le premier fournisseur échoue, le suivant est essayé. 94. Le résultat indique le fournisseur réellement utilisé et le recours éventuel au secours. 95. Une réponse IA non JSON mais contenant un objet JSON exploitable est récupérée. 96. Une réponse sans nom est refusée. 97. La description est limitée à 100 caractères et les tags à huit. 98. Sans clé personnelle, l’analyse renvoie `AI_CREDENTIAL_REQUIRED`. 99. L’échec de tous les fournisseurs renvoie `502` sans exposer de secret. 100. La proposition IA ouvre un formulaire modifiable avant toute création. ### Contact et confidentialité 101. La politique de confidentialité est lisible connecté ou non. 102. Les quatre champs de contact sont validés selon leurs longueurs. 103. Une soumission valide crée exactement un message. 104. Une sixième soumission dans la même heure depuis la même IP est refusée par `429`. 105. Le champ leurre rempli produit une réussite sans enregistrement. 106. Une erreur de stockage n’expose aucune information technique. ### Robustesse et expérience 107. Les formulaires conservent la saisie après une erreur serveur. 108. Les erreurs de champ sont associées visuellement et sémantiquement au bon contrôle. 109. Une action en cours ne peut pas être soumise deux fois. 110. Une erreur de chargement global propose Réessayer. 111. Aucune mutation d’inventaire ne recharge le document HTML complet. 112. Une fenêtre modale restaure une navigation clavier cohérente à sa fermeture. 113. Les animations sont réduites lorsque le système le demande. 114. Les médias, la base et les clés chiffrées restent cohérents après redémarrage et redéploiement. 115. Des tests automatiques avec deux utilisateurs et deux maisons démontrent l’absence de fuite croisée. ### Points à confirmer par l’auteur - Le nom exact du logiciel payant que Ma Maison est censé remplacer n’est pas identifiable. La régénération doit viser la catégorie des inventaires domestiques payants avec QR, photos et collaboration. - Il faut confirmer si l’interface historique doit rester accessible à long terme ou si ses fonctions particulières doivent être intégrées à l’interface principale avant redirection. - Le comportement réel conserve les objets lorsque leur pièce ou meuble est supprimé, alors que certaines confirmations parlent de supprimer « le contenu associé ». Il faut confirmer si cette conservation sans emplacement est intentionnelle. - Aucune fonction de suppression de compte, export complet, récupération de mot de passe, vérification d’e-mail, retrait de membre, transfert de propriété ou renommage du foyer n’est exposée. Ne pas les inventer sans validation. - Des structures anciennes de gestion de pages et rubriques existent conceptuellement mais ne sont reliées à aucune route accessible et ne disposent pas du schéma actif nécessaire. Elles ne font pas partie du produit à régénérer sauf confirmation contraire.