# TODO-API.md ## 📋 Liste des tĂąches Ă  implĂ©menter ### 🔮 PRIORITÉ HAUTE #### 1. SystĂšme de backup pour les suppressions (DELETE) **DemandĂ© le :** 20/08/2025 **Objectif :** Sauvegarder toutes les donnĂ©es supprimĂ©es (soft delete) dans un fichier SQL pour pouvoir les restaurer en cas d'erreur humaine. **DĂ©tails techniques :** - CrĂ©er un systĂšme de backup automatique lors de chaque DELETE - Stocker les donnĂ©es dans un fichier SQL avec structure permettant la rĂ©intĂ©gration facile - Format suggĂ©rĂ© : `/backups/deleted/{annĂ©e}/{mois}/deleted_{table}_{YYYYMMDD}.sql` **Tables concernĂ©es :** - `ope_pass` (passages) - DELETE /passages/{id} - `users` (utilisateurs) - DELETE /users/{id} - `operations` (opĂ©rations) - DELETE /operations/{id} - `ope_sectors` (secteurs) - DELETE /sectors/{id} **Structure du backup suggĂ©rĂ©e :** ```sql -- Backup deletion: ope_pass -- Date: 2025-08-20 14:30:45 -- User: 9999985 (cv_mobile) -- Entity: 5 -- Original ID: 19500576 INSERT INTO ope_pass_backup ( original_id, deleted_at, deleted_by_user_id, deleted_by_entity_id, -- tous les champs originaux fk_operation, fk_sector, fk_user, montant, encrypted_name, encrypted_email, -- etc... ) VALUES ( 19500576, '2025-08-20 14:30:45', 9999985, 5, -- valeurs originales ... ); -- Pour restauration facile : -- UPDATE ope_pass SET chk_active = 1 WHERE id = 19500576; ``` **FonctionnalitĂ©s Ă  implĂ©menter :** 1. **Service de backup** : `BackupService.php` - MĂ©thode `backupDeletedRecord($table, $id, $data)` - GĂ©nĂ©ration automatique du SQL de restauration - Rotation des fichiers (garder 90 jours) 2. **IntĂ©gration dans les controllers** - Ajouter l'appel au BackupService avant chaque soft delete - Logger l'emplacement du backup 3. **Interface de restauration** (optionnel) - Endpoint GET /api/backups/deleted pour lister les backups - Endpoint POST /api/backups/restore/{backup_id} pour restaurer 4. **Commande de restauration manuelle** - Script PHP : `php scripts/restore_deleted.php --table=ope_pass --id=19500576` **Avantages :** - TraçabilitĂ© complĂšte des suppressions - Restauration rapide en cas d'erreur - Audit trail pour conformitĂ© - TranquillitĂ© d'esprit pour le client --- ### 🔮 PRIORITÉ HAUTE #### 2. Migration des bases de donnĂ©es vers container maria3 centralisĂ© **DemandĂ© le :** 07/10/2025 **Objectif :** Migrer les bases de donnĂ©es locales des containers dva-geo et rca-geo vers un container MariaDB centralisĂ© maria3 sur le mĂȘme host IN3. **Architecture actuelle :** - **dva-geo** : MariaDB local avec base `geo_app` (localhost) - **rca-geo** : MariaDB local avec base `geo_app` (localhost) - **maria3** : Container MariaDB 11.4 existant (IP: 13.23.33.4) - utilisĂ© uniquement pour la base `adresses` **Architecture cible :** - **maria3** : Container centralisĂ© avec : - Base `dva_geo` pour l'environnement DEV - Base `rca_geo` pour l'environnement RECETTE - Base `adresses` (dĂ©jĂ  existante) - **dva-geo** : Suppression du serveur MariaDB local - **rca-geo** : Suppression du serveur MariaDB local **Avantages :** - ✅ Centralisation des bases de donnĂ©es - ✅ Facilite les sauvegardes - ✅ RĂ©duction de la consommation mĂ©moire des containers API - ✅ SĂ©paration claire des responsabilitĂ©s (API vs DB) - ✅ PrĂ©paration pour architecture production (IN4/maria4/pra_geo) --- #### 📋 TODOLIST DÉTAILLÉE - ENVIRONNEMENT DVA-GEO (DEV) ##### Phase 1ïžâƒŁ : PrĂ©paration et sauvegarde (AVANT migration) - [x] **1.1** VĂ©rifier l'Ă©tat actuel de la base dans dva-geo ```bash incus exec dva-geo -- mysql -u root -pMyAlpineDb.90b -e "SHOW DATABASES;" incus exec dva-geo -- mysql -u root -pMyAlpineDb.90b geo_app -e "SHOW TABLES;" incus exec dva-geo -- mysql -u root -pMyAlpineDb.90b geo_app -e "SELECT COUNT(*) FROM users;" ``` - [x] **1.2** CrĂ©er une sauvegarde complĂšte de la base actuelle ```bash # Dump avec skip-lock-tables (vue problĂ©matique v_stripe_amicale_dashboard) incus exec dva-geo -- mariadb-dump -u root -pMyAlpineDb.90b --skip-lock-tables geo_app > /var/back/dva_geo_backup_20251007.sql ``` - [x] **1.3** VĂ©rifier l'intĂ©gritĂ© de la sauvegarde ```bash ls -lh /var/back/dva_geo_backup_20251007.sql # Taille : 1.2GB (1217046899 bytes) ``` - [x] **1.4** Supprimer la vue problĂ©matique et refaire un dump propre ```bash # Suppression de la vue cassĂ©e incus exec dva-geo -- mysql -u root -pMyAlpineDb.90b geo_app -e "DROP VIEW IF EXISTS v_stripe_amicale_dashboard;" # Nouveau dump complet et propre incus exec dva-geo -- mariadb-dump -u root -pMyAlpineDb.90b geo_app > /var/back/dva_geo_backup_final_20251007.sql ``` ##### Phase 2ïžâƒŁ : Configuration de maria3 - [x] **2.1** Se connecter Ă  maria3 et crĂ©er la base dva_geo ```bash incus exec maria3 -- mysql -u root -p'MyAlpLocal,90b' -e "CREATE DATABASE IF NOT EXISTS dva_geo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" ``` - [x] **2.2** CrĂ©er l'utilisateur dĂ©diĂ© dva_geo_user ```bash incus exec maria3 -- mysql -u root -p'MyAlpLocal,90b' -e " CREATE USER IF NOT EXISTS 'dva_geo_user'@'%' IDENTIFIED BY 'CBq9tKHj6PGPZuTmAHV7'; GRANT ALL PRIVILEGES ON dva_geo.* TO 'dva_geo_user'@'%'; FLUSH PRIVILEGES;" ``` - [x] **2.3** VĂ©rifier les permissions ```bash incus exec maria3 -- mysql -u root -p'MyAlpLocal,90b' -e "SHOW GRANTS FOR 'dva_geo_user'@'%';" # RĂ©sultat : ALL PRIVILEGES sur dva_geo.* ``` - [x] **2.4** Tester la connexion avec le nouvel utilisateur depuis dva-geo ```bash incus exec dva-geo -- mysql -h 13.23.33.4 -u dva_geo_user -p'CBq9tKHj6PGPZuTmAHV7' -e "SHOW DATABASES;" # ✅ Connexion rĂ©ussie ``` ##### Phase 3ïžâƒŁ : Migration des donnĂ©es - [x] **3.1** Copier le dump depuis le host vers maria3 ```bash incus file push /var/back/dva_geo_backup_final_20251007.sql maria3/tmp/ # ✅ Fichier transfĂ©rĂ© ``` - [x] **3.2** Importer le dump dans maria3 ```bash incus exec maria3 -- mysql -u root -p'MyAlpLocal,90b' dva_geo < /tmp/dva_geo_backup_final_20251007.sql # ✅ Import rĂ©ussi ``` - [x] **3.3** VĂ©rifier l'importation ```bash incus exec maria3 -- mysql -u root -p'MyAlpLocal,90b' dva_geo -e "SHOW TABLES;" incus exec maria3 -- mysql -u root -p'MyAlpLocal,90b' dva_geo -e "SELECT COUNT(*) FROM users;" incus exec maria3 -- mysql -u root -p'MyAlpLocal,90b' dva_geo -e "SELECT COUNT(*) FROM operations;" incus exec maria3 -- mysql -u root -p'MyAlpLocal,90b' dva_geo -e "SELECT COUNT(*) FROM ope_pass;" # ✅ Toutes les tables prĂ©sentes ``` - [x] **3.4** Comparer les comptages avec la base source ```bash # Base source (dva-geo localhost) incus exec dva-geo -- mysql -u root -pMyAlpineDb.90b geo_app -e " SELECT 'users' as table_name, COUNT(*) as count FROM users UNION ALL SELECT 'operations', COUNT(*) FROM operations UNION ALL SELECT 'ope_pass', COUNT(*) FROM ope_pass UNION ALL SELECT 'entites', COUNT(*) FROM entites;" # Base cible (maria3) incus exec maria3 -- mysql -u root -p'MyAlpLocal,90b' dva_geo -e " SELECT 'users' as table_name, COUNT(*) as count FROM users UNION ALL SELECT 'operations', COUNT(*) FROM operations UNION ALL SELECT 'ope_pass', COUNT(*) FROM ope_pass UNION ALL SELECT 'entites', COUNT(*) FROM entites;" # ✅ Comptages identiques confirmĂ©s ``` ##### Phase 4ïžâƒŁ : Configuration de l'API - [x] **4.1** Mettre Ă  jour AppConfig.php dans dva-geo ```php // Fichier: src/Config/AppConfig.php (local) // Lignes 153-165 // Configuration maria3 activĂ©e (migration effectuĂ©e le 07/10/2025) 'database' => [ 'host' => '13.23.33.4', // Container maria3 sur IN3 'name' => 'dva_geo', 'username' => 'dva_geo_user', 'password' => 'CBq9tKHj6PGPZuTmAHV7', ], ``` - [x] **4.2** DĂ©ployer la nouvelle configuration ```bash # Depuis /home/pierre/dev/geosector/api ./deploy-api.sh # ✅ DĂ©ploiement rĂ©ussi sur dva-geo ``` - [x] **4.3** RedĂ©marrer PHP-FPM ```bash incus exec dva-geo -- rc-service php-fpm83 restart # ✅ PHP-FPM redĂ©marrĂ© ``` ##### Phase 5ïžâƒŁ : Tests de l'API - [x] **5.1** Tester la connexion API Ă  la base ```bash curl -X GET https://dapp.geosector.fr/api/health # ✅ API opĂ©rationnelle ``` - [x] **5.2** Tester l'authentification ```bash curl -X POST https://dapp.geosector.fr/api/login \ -H "Content-Type: application/json" \ -d '{"username":"pv_admin","password":"mot_de_passe"}' # ✅ Login rĂ©ussi ``` - [x] **5.3** Tester la rĂ©cupĂ©ration de donnĂ©es ```bash curl -X GET https://dapp.geosector.fr/api/users \ -H "Authorization: Bearer {session_id}" # ✅ DonnĂ©es rĂ©cupĂ©rĂ©es depuis maria3 ``` - [x] **5.4** Tester l'application Flutter ```bash # Test depuis l'application mobile Flutter # ✅ Connexion, rĂ©cupĂ©ration donnĂ©es, crĂ©ation passages : OK ``` - [x] **5.5** VĂ©rifier les logs ```bash incus exec dva-geo -- tail -50 /var/www/geosector/api/logs/app.log incus exec dva-geo -- tail -50 /var/log/nginx/dva-api-error.log # ✅ Aucune erreur dĂ©tectĂ©e ``` ##### Phase 6ïžâƒŁ : Nettoyage et suppression de MariaDB local ⚠ **Migration validĂ©e et fonctionnelle** - Nettoyage effectuĂ© le 07/10/2025 - [x] **6.1** Faire une derniĂšre sauvegarde de sĂ©curitĂ© ```bash incus exec dva-geo -- mariadb-dump -u root -pMyAlpineDb.90b --skip-lock-tables geo_app > /var/back/dva_geo_FINAL_backup_$(date +%Y%m%d_%H%M%S).sql # ✅ Sauvegarde finale créée ``` - [x] **6.2** ArrĂȘter le serveur MariaDB local dans dva-geo ```bash incus exec dva-geo -- rc-service mariadb stop # ✅ MariaDB arrĂȘtĂ© ``` - [x] **6.3** DĂ©sactiver le dĂ©marrage automatique de MariaDB ```bash incus exec dva-geo -- rc-update del mariadb default # ✅ MariaDB retirĂ© du runlevel default ``` - [x] **6.4** DĂ©sinstaller MariaDB ```bash incus exec dva-geo -- apk del mariadb mariadb-client mariadb-common # ✅ MariaDB dĂ©sinstallĂ© du container ``` - [x] **6.5** Archiver les donnĂ©es MariaDB locales ```bash incus exec dva-geo -- tar -czf /var/back/mysql_data_archive_$(date +%Y%m%d).tar.gz /var/lib/mysql # ✅ Archive créée ``` - [x] **6.6** Supprimer les donnĂ©es MariaDB locales ```bash incus exec dva-geo -- rm -rf /var/lib/mysql /run/mysqld # ✅ DonnĂ©es supprimĂ©es, espace disque libĂ©rĂ© ``` ##### Phase 7ïžâƒŁ : Documentation - [x] **7.1** Mettre Ă  jour TODO-API.md avec toutes les commandes - Toutes les phases documentĂ©es avec commandes rĂ©elles - Statut mis Ă  jour : ✅ TERMINÉ - [x] **7.2** Documenter les problĂšmes rencontrĂ©s - **ProblĂšme 1** : Vue `v_stripe_amicale_dashboard` cassĂ©e - **Solution** : Suppression de la vue avant dump final - **Fichier** : `scripts/migrations/stripe_tables.sql` (ligne 183) - **ProblĂšme 2** : Erreur LOCK TABLES lors du dump initial - **Solution** : Ajout de `--skip-lock-tables` au mysqldump - [ ] **7.3** Mettre Ă  jour TECHBOOK.md avec la nouvelle architecture - Section "Base de donnĂ©es" (lignes 136-154) - Mettre Ă  jour le tableau pour reflĂ©ter la configuration DVA-GEO → maria3 --- #### 📋 PROCHAINES ÉTAPES (APRÈS DVA-GEO) Une fois la migration DVA-GEO validĂ©e et stable : ### ✅ Migration RCA-GEO (RECETTE) - TERMINÉE **Statut :** ✅ **MIGRATION COMPLÉTÉE AVEC SUCCÈS** (16/10/2025) **PrĂ©paration effectuĂ©e le 07/10/2025** : - ✅ Base `rca_geo` créée dans maria3 (vide) - ✅ Utilisateur `rca_geo_user` créé avec ALL PRIVILEGES - ✅ Vue problĂ©matique `v_stripe_amicale_dashboard` supprimĂ©e de la base source (rca-geo localhost) **Migration rĂ©alisĂ©e le 16/10/2025** : - ✅ Dump de la base `geo_app` depuis rca-geo (localhost) - ✅ Import dans maria3 `rca_geo` - ✅ Validation des comptages (toutes les tables migrĂ©es correctement) - ✅ Modification AppConfig.php lignes 113-128 - ✅ DĂ©ploiement manuel sur rca-geo - ✅ Tests complets sur https://rapp.geosector.fr/api/ - ✅ Suppression complĂšte de MariaDB local dans rca-geo - ✅ Suppression des dossiers `/var/lib/mysql` et `/run/mysqld` **Configuration finale** : - User : `rca_geo_user` - Password : `UPf3C0cQ805LypyM71iW` - Host : `13.23.33.4` (maria3 sur IN3) - Base : `rca_geo` - Privileges : ALL PRIVILEGES sur rca_geo.\* **RĂ©sultat** : - Application fonctionnelle en RECETTE avec base centralisĂ©e sur maria3 - Container rca-geo allĂ©gĂ© (plus de serveur MariaDB local) - Architecture cohĂ©rente DVA-GEO et RCA-GEO → maria3 --- ### đŸ”” CrĂ©ation environnement PRODUCTION (PLANIFIÉ JEUDI 10/10/2025 16h) **Architecture cible** : - Serveur IN4 (51.159.7.190) - Container `pra-geo` pour l'API (exportĂ© depuis dva-geo) - Container `maria4` pour la base de donnĂ©es - Base : `pra_geo` avec utilisateur dĂ©diĂ© - DonnĂ©es dupliquĂ©es depuis rca_geo (IN3/maria3) --- #### 📋 TODOLIST DÉTAILLÉE - ENVIRONNEMENT PRODUCTION (PRA-GEO) ##### Phase 0ïžâƒŁ : PrĂ©paration du serveur IN4 - [x] **0.1** VĂ©rifier l'accĂšs SSH au serveur IN4 ```bash ssh root@51.159.7.190 # ✅ Serveur accessible ``` - [x] **0.2** VĂ©rifier Incus sur IN4 ```bash ssh root@51.159.7.190 "incus list" # ✅ Incus opĂ©rationnel ``` - [x] **0.3** VĂ©rifier l'espace disque disponible ```bash ssh root@51.159.7.190 "df -h" # ✅ Espace suffisant ``` - [x] **0.4** PrĂ©parer le rĂ©pertoire de transfert ```bash ssh root@51.159.7.190 "mkdir -p /var/back/imports" # ✅ RĂ©pertoire créé ``` ##### Phase 1ïžâƒŁ : Export du container dva-geo depuis IN3 - [x] **1.1** Export dva-geo rĂ©alisĂ© ```bash # ✅ Container dva-geo exportĂ© depuis IN3 # Note: Export effectuĂ©, dĂ©tails exacts non documentĂ©s ``` - [x] **1.2** Snapshot et archive créés ```bash # ✅ Archive dva-geo créée et transfĂ©rĂ©e vers IN4 ``` ##### Phase 2ïžâƒŁ : Transfert vers IN4 - [x] **2.1** Archive transfĂ©rĂ©e vers IN4 ```bash # ✅ Archive transfĂ©rĂ©e sur IN4 ``` ##### Phase 3ïžâƒŁ : Import et configuration de pra-geo sur IN4 - [x] **3.1** Container pra-geo importĂ© et lancĂ© ```bash # ✅ Container pra-geo créé sur IN4 # IP: 13.23.33.22 (rĂ©seau Incus) ``` - [x] **3.2** Configuration rĂ©seau vĂ©rifiĂ©e ```bash # ✅ RĂ©seau configurĂ©, ping et connectivitĂ© OK ``` - [x] **3.3** Container pra-geo opĂ©rationnel ```bash # ✅ Container dĂ©marrĂ© et accessible ``` - [x] **3.4** Client MariaDB installĂ© ```bash # ✅ mariadb-client installĂ© sur pra-geo ``` ##### Phase 4ïžâƒŁ : CrĂ©ation du container maria4 sur IN4 - [x] **4.1** Container maria4 créé ```bash # ✅ Container maria4 créé sur IN4 # IP: 13.23.33.4 (mĂȘme IP que maria3 pour cohĂ©rence) ``` - [x] **4.2** MariaDB installĂ© et initialisĂ© ```bash # ✅ MariaDB 11.4 installĂ© et dĂ©marrĂ© # ✅ Mot de passe root: MyAlpLocal,90b ``` - [x] **4.3** Connexions distantes autorisĂ©es ```bash # ✅ bind-address = 0.0.0.0 # ✅ MariaDB redĂ©marrĂ© ``` - [x] **4.4** Base pra_geo créée ```bash # ✅ Base: pra_geo (utf8mb4_unicode_ci) # ✅ User: pra_geo_user / d2jAAGGWi8fxFrWgXjOA # ✅ ALL PRIVILEGES accordĂ©s ``` - [x] **4.5** Base adresses prĂ©sente ```bash # ✅ Base adresses avec tables par dĂ©partement (cp01, cp02, etc.) # ✅ User: adr_geo_user@13.23.33.2% / d66,AdrGeoPrd.User # ✅ SELECT privileges accordĂ©s ``` - [x] **4.6** Firewall UFW configurĂ© ```bash # ✅ UFW: allow from 13.23.33.0/24 to any port 3306 # ✅ Connexions depuis pra-geo (13.23.33.22) opĂ©rationnelles ``` - [x] **4.7** Tests de connexion rĂ©ussis ```bash # ✅ pra-geo → maria4 (pra_geo): OK - 24 090 users # ✅ pra-geo → maria4 (adresses): OK - Tables accessibles ``` ##### Phase 5ïžâƒŁ : Migration des donnĂ©es depuis dva_geo (IN3/maria3) - [x] **5.1** Dump dva_geo rĂ©alisĂ© ```bash # ✅ Dump de dva_geo (sans vue problĂ©matique v_stripe_amicale_dashboard) # Source: dva_geo (maria3 sur IN3) ``` - [x] **5.2** DonnĂ©es importĂ©es dans pra_geo ```bash # ✅ Import rĂ©ussi dans maria4/pra_geo ``` - [x] **5.3** VĂ©rification des donnĂ©es ```bash # ✅ 24 090 utilisateurs dans pra_geo # ✅ Tables operations, ope_pass, entites prĂ©sentes ``` **Note** : La base `pra_geo` a Ă©tĂ© initialisĂ©e depuis `dva_geo` (DEV) et non depuis `rca_geo` (REC). Pour la mise en production finale, il faudra probablement migrer depuis `rca_geo` (donnĂ©es de recette validĂ©es). ##### Phase 6ïžâƒŁ : Configuration de l'API pour PRODUCTION - [x] **6.1** AppConfig.php modifiĂ© localement ```php // Configuration PRODUCTION (lignes 84-111) 'database' => [ 'host' => '13.23.33.4', // ✅ maria4 sur IN4 'name' => 'pra_geo', 'username' => 'pra_geo_user', 'password' => 'd2jAAGGWi8fxFrWgXjOA', ], 'addresses_database' => [ 'host' => '13.23.33.4', // ✅ maria4 sur IN4 'name' => 'adresses', 'username' => 'adr_geo_user', 'password' => 'd66,AdrGeoPrd.User', ], ``` - [ ] **6.2** DĂ©ployer en PRODUCTION ```bash # ⏳ EN ATTENTE validation client ./deploy-api.sh pra ``` - [ ] **6.3** RedĂ©marrer PHP-FPM ```bash # ⏳ À faire aprĂšs dĂ©ploiement ssh root@51.159.7.190 "incus exec pra-geo -- rc-service php-fpm83 restart" ``` ##### Phase 7ïžâƒŁ : Configuration DNS et reverse proxy - [ ] **7.1** VĂ©rifier la configuration NGINX sur IN4 ```bash ssh root@51.159.7.190 "cat /etc/nginx/sites-available/app.geosector.fr" ``` - [ ] **7.2** Configurer le reverse proxy vers pra-geo ```nginx # /etc/nginx/sites-available/app.geosector.fr upstream pra_geo_backend { server 13.23.34.43:80; } server { listen 443 ssl http2; server_name app.geosector.fr; ssl_certificate /etc/letsencrypt/live/app.geosector.fr/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/app.geosector.fr/privkey.pem; location / { proxy_pass http://pra_geo_backend; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } ``` - [ ] **7.3** Activer le site et recharger NGINX ```bash ssh root@51.159.7.190 "ln -s /etc/nginx/sites-available/app.geosector.fr /etc/nginx/sites-enabled/" ssh root@51.159.7.190 "nginx -t" ssh root@51.159.7.190 "systemctl reload nginx" ``` - [ ] **7.4** VĂ©rifier le certificat SSL ```bash ssh root@51.159.7.190 "certbot certificates | grep app3.geosector.fr" # Si pas de certificat, le crĂ©er : # certbot certonly --nginx -d app3.geosector.fr ``` ##### Phase 8ïžâƒŁ : Tests de l'API PRODUCTION - [ ] **8.1** Tester le endpoint health ```bash curl -X GET https://app3.geosector.fr/api/health # ✅ Doit retourner {"status":"ok"} ``` - [ ] **8.2** Tester l'authentification ```bash curl -X POST https://app3.geosector.fr/api/login \ -H "Content-Type: application/json" \ -d '{"username":"test_user","password":"test_pass"}' # ✅ Login doit fonctionner ``` - [ ] **8.3** Tester la rĂ©cupĂ©ration de donnĂ©es ```bash curl -X GET https://app3.geosector.fr/api/users \ -H "Authorization: Bearer {session_id}" # ✅ DonnĂ©es rĂ©cupĂ©rĂ©es depuis maria4/pra_geo ``` - [ ] **8.4** Tester depuis l'application Flutter ```bash # Configurer l'app Flutter avec l'URL de production # Tester : Login, rĂ©cupĂ©ration opĂ©rations, crĂ©ation passage # ✅ Toutes les fonctionnalitĂ©s opĂ©rationnelles ``` - [ ] **8.5** VĂ©rifier les logs ```bash ssh root@51.159.7.190 "incus exec pra-geo -- tail -50 /var/www/geosector/api/logs/app.log" ssh root@51.159.7.190 "incus exec pra-geo -- tail -50 /var/log/nginx/pra-api-error.log" # ✅ Aucune erreur critique ``` ##### Phase 9ïžâƒŁ : SĂ©curisation et monitoring - [ ] **9.1** Configurer les sauvegardes automatiques de maria4 ```bash # CrĂ©er un script de backup quotidien ssh root@51.159.7.190 "cat > /root/backup-maria4.sh << 'EOF' #!/bin/bash DATE=$(date +%Y%m%d_%H%M%S) incus exec maria4 -- mysqldump -u root -p'MyAlpLocal,90b' --all-databases > /var/back/maria4_backup_${DATE}.sql # Garder les 30 derniers jours find /var/back/maria4_backup_*.sql -mtime +30 -delete EOF" ssh root@51.159.7.190 "chmod +x /root/backup-maria4.sh" ``` - [ ] **9.2** Configurer la tĂąche CRON pour les backups ```bash ssh root@51.159.7.190 "crontab -l | { cat; echo '0 2 * * * /root/backup-maria4.sh'; } | crontab -" ``` - [ ] **9.3** Configurer les logs de rotation ```bash # VĂ©rifier que logrotate est configurĂ© pour NGINX et PHP ssh root@51.159.7.190 "cat /etc/logrotate.d/nginx" ``` - [ ] **9.4** Configurer le monitoring (optionnel) ```bash # Installer un monitoring basique (htop, iotop, etc.) ssh root@51.159.7.190 "apt install htop iotop iftop -y" ``` - [ ] **9.5** Documenter les IPs et accĂšs ```bash # CrĂ©er un fichier README sur IN4 ssh root@51.159.7.190 "cat > /root/PRODUCTION_INFO.txt << 'EOF' ======================================== ENVIRONNEMENT PRODUCTION GEOSECTOR ======================================== Serveur : IN4 (51.159.7.190) Date de crĂ©ation : $(date +%Y-%m-%d) Containers : - pra-geo (API) : 13.23.34.43 - maria4 (DB) : 13.23.34.4 Base de donnĂ©es : - Base : pra_geo - User : pra_geo_user - Password : d2jAAGGWi8fxFrWgXjOA URLs : - API : https://app3.geosector.fr/api/ - Flutter : https://app3.geosector.fr/ Backups : - Quotidien Ă  2h : /var/back/maria4_backup_*.sql - RĂ©tention : 30 jours ======================================== EOF" ``` ##### Phase 🔟 : Documentation finale - [ ] **10.1** Mettre Ă  jour TECHBOOK.md - Ajouter la configuration PRODUCTION dans le tableau (ligne 142) - Documenter l'architecture complĂšte 3 environnements - [ ] **10.2** Mettre Ă  jour ce TODO-API.md - Marquer toutes les Ă©tapes comme complĂ©tĂ©es - Statut : ✅ TERMINÉ - [ ] **10.3** CrĂ©er un document de procĂ©dures d'urgence - Rollback en cas de problĂšme - Contacts et escalade - ProcĂ©dure de restauration depuis backup --- **Date de crĂ©ation TODO :** 07/10/2025 **Date planifiĂ©e d'exĂ©cution :** Jeudi 10/10/2025 Ă  16h **Date de dĂ©but :** 07/10/2025 **Statut :** 🟡 EN COURS - Phases 0 Ă  5 complĂ©tĂ©es, en attente validation client **DurĂ©e estimĂ©e :** 3-4 heures --- #### 🔐 Informations sensibles (Ă  ne pas commiter) **Container maria3 (IN3)** : - IP interne : 13.23.33.4 - Root password : `MyAlpLocal,90b` - Port : 3306 **Utilisateurs bases de donnĂ©es** : - DEV : `dva_geo_user` / `CBq9tKHj6PGPZuTmAHV7` - REC : `rca_geo_user` / `UPf3C0cQ805LypyM71iW` (Ă  crĂ©er) - PROD : `pra_geo_user` / mot de passe Ă  gĂ©nĂ©rer --- **Date de crĂ©ation :** 07/10/2025 **Statut :** ✅ TERMINÉ - Migration DVA-GEO complĂ©tĂ©e avec succĂšs **DerniĂšre mise Ă  jour :** 07/10/2025 --- ### 🟱 Migration de donnĂ©es PM7 → PRA-GEO (PRODUCTION) #### 3. Script de migration depuis backup PM7 restaurĂ© **DemandĂ© le :** 07/10/2025 **Objectif :** CrĂ©er un script PHP standalone pour migrer les donnĂ©es depuis un backup PM7 (restaurĂ© dans maria4) vers la base pra_geo. **Contexte :** - Backup quotidien de PM7 (11.1.2.17) : `geosector_YYYYMMDD.sql.tar.gz.enc` - Processus : DĂ©chiffrement → SCP vers IN4 → Import dans maria4 → Migration vers pra_geo - Script exĂ©cutĂ© depuis le container **pra-geo** (pas maria4, car PHP nĂ©cessaire) **Architecture de migration :** ``` PM7 (11.1.2.17) - Backup nocturne chiffrĂ© ↓ DĂ©chiffrement (decpm7.sh) ↓ SCP vers IN4:/var/back/ ↓ incus file push vers maria4:/var/back/ ↓ DĂ©compression et import maria4 (IN4) - Base geosector_YYYYMMDD ↓ Migration (script PHP depuis pra-geo) maria4 (IN4) - Base pra_geo ``` **Fichier créé :** `scripts/php/migrate_from_backup.php` **FonctionnalitĂ©s implĂ©mentĂ©es :** ✅ **1. Respect des contraintes FK** - Ordre de migration : x\_\* → entites → users → operations → ope_sectors → ope_pass → medias - Gestion des dĂ©pendances : x_devises → x_pays → x_regions → x_departements → x_villes ✅ **2. Chiffrement AES-256-CBC** - Utilisation de `ApiService::encryptData()` et `ApiService::encryptSearchableData()` - Tables concernĂ©es : entites (name, phone, email, iban, bic), users (name, email, phone), ope_pass (name, email, phone) ✅ **3. Mappings de champs** - `rowid` → `id` - `active` → `chk_active` - `date_creat` → `created_at` - `date_modif` → `updated_at` - `libelle` → `encrypted_name` (entites) - `nom` → `encrypted_name` (users, passages) ✅ **4. DĂ©tection mobile/fixe** - NumĂ©ros commençant par 06/07 → champ `encrypted_mobile` - Autres numĂ©ros → champ `encrypted_phone` ✅ **5. Support deux modes** - **Global** : Migration de toutes les amicales - **Entity** : Migration d'une amicale spĂ©cifique (avec tables de rĂ©fĂ©rence) ✅ **6. Optimisations** - Traitement par lots de 1000 pour `ope_pass` (table volumineuse) - `ON DUPLICATE KEY UPDATE` pour idempotence - Logging dĂ©taillĂ© avec progression **Utilisation :** ```bash # Sur IN4, dans le container pra-geo cd /var/www/geosector/api # Migration globale (toutes les amicales) php scripts/php/migrate_from_backup.php \ --source-db=geosector_20251007 \ --target-db=pra_geo \ --mode=global # Migration d'une amicale spĂ©cifique php scripts/php/migrate_from_backup.php \ --source-db=geosector_20251007 \ --target-db=pra_geo \ --mode=entity \ --entity-id=45 \ --log=/var/www/geosector/api/logs/migration_entity_45.log ``` **Tables migrĂ©es (dans l'ordre) :** 1. `x_devises`, `x_entites_types`, `x_types_passages`, `x_types_reglements`, `x_users_roles`, `x_users_titres` 2. `x_pays`, `x_regions`, `x_departements`, `x_villes` 3. `entites` (avec chiffrement) 4. `users` (avec chiffrement et dĂ©tection mobile) 5. `operations` 6. `ope_sectors` 7. `sectors_adresses` 8. `ope_users` 9. `ope_users_sectors` 10. `ope_pass` (avec chiffrement, traitement par lots) 11. `ope_pass_histo` 12. `medias` **Configuration connexion :** - Host : `13.23.33.4` (maria4 sur IN4) - Base source : `geosector_YYYYMMDD` (user root) - Base cible : `pra_geo` (user pra_geo_user) **Documentation :** `scripts/README-migration.md` **Statut :** ✅ TERMINÉ - Script standalone créé et prĂȘt Ă  ĂȘtre testĂ© **Date de rĂ©alisation :** 07/10/2025 --- #### 🔧 Correction critique du mapping des secteurs **Date :** 08/10/2025 **ProblĂšme identifiĂ© :** Bug majeur dans la migration des secteurs **Contexte du bug :** Le script initial lisait depuis la table `ope_sectors` dans la base SOURCE, alors que cette table n'existe QUE dans la base CIBLE. Dans la base source (geosector), les secteurs sont stockĂ©s dans la table `sectors`. **Architecture des secteurs :** ``` SOURCE (geosector_YYYYMMDD): - sectors (rowid, libelle, sector, color) - ope_users_sectors (fk_operation, fk_user, fk_sector → sectors.rowid) - ope_pass (fk_sector → sectors.rowid) - sectors_adresses (fk_sector → sectors.rowid) CIBLE (pra_geo): - ope_sectors (id AUTO_INCREMENT, fk_operation, fk_old_sector, libelle, sector, color) - ope_users_sectors (fk_sector → ope_sectors.id NOUVEAU) - ope_pass (fk_sector → ope_sectors.id NOUVEAU) - sectors_adresses (fk_sector → ope_sectors.id NOUVEAU) ``` **Corrections apportĂ©es :** ✅ **1. Migration ope_sectors (lignes 650-772)** - Lecture depuis `sectors` (source) via JOIN avec `ope_users_sectors` - CrĂ©ation dans `ope_sectors` (cible) avec ID auto-increment - GĂ©nĂ©ration d'un mapping : `fk_operation . '_' . old_sector_id → new_sector_id` - Stockage dans `$this->sectorMapping` pour utilisation par les tables suivantes ✅ **2. Migration sectors_adresses (lignes 774-881)** - Utilise le mapping pour remplacer `fk_sector` ancien par nouveau ID - Ignore les adresses dont le secteur n'a pas Ă©tĂ© migrĂ© (compteur `$skipped`) ✅ **3. Migration ope_users_sectors (lignes 952-1040)** - Utilise le mapping pour remplacer `fk_sector` ancien par nouveau ID - Ignore les associations dont le secteur n'a pas Ă©tĂ© migrĂ© ✅ **4. Migration ope_pass (lignes 1042-1200)** - Ajout du mapping au dĂ©but de la boucle (lignes 1120-1131) - Utilise le mapping pour remplacer `fk_sector` ancien par nouveau ID - Ignore les passages dont le secteur n'a pas Ă©tĂ© migrĂ© **Exemple de mapping créé :** ```php $this->sectorMapping = [ '12345_789' => 1001, // opĂ©ration 12345, ancien secteur 789 → nouveau secteur 1001 '12345_790' => 1002, '12346_789' => 1003, // MĂȘme ancien secteur, mais autre opĂ©ration = autre nouveau ID ]; ``` **Impact :** CRITIQUE - Sans cette correction, la migration aurait Ă©chouĂ© complĂštement **Fichiers modifiĂ©s :** - `scripts/php/migrate_from_backup.php` (corrections lignes 650-1200) **Statut :** ✅ CORRIGÉ - PrĂȘt pour tests **Date de correction :** 08/10/2025 --- #### 📋 PrĂ©paration migration en batch (406 entitĂ©s Ă©ligibles) **Date :** 08/10/2025 **Objectif :** Migrer progressivement les entitĂ©s actives depuis geosector_20251008 **CritĂšres de sĂ©lection des entitĂ©s :** - Plus de 4 utilisateurs actifs - Au moins 1 opĂ©ration créée aprĂšs le 2023-08-01 - Code postal renseignĂ© (nettoyage de 3 entitĂ©s de test) **Fichiers créés :** 1. **`scripts/migrations_entites.json`** (406 entitĂ©s) - Liste complĂšte des entitĂ©s Ă©ligibles avec statistiques - Format : entity_id, code_postal, nom, ville, nb_users, nb_operations, nb_passages 2. **`scripts/migrate_batch.sh`** (script bash orchestrateur) - Migration entitĂ© par entitĂ© avec progression - Options : `--start N`, `--limit N`, `--dry-run`, `--continue` - Logs dĂ©taillĂ©s par entitĂ© + log global - Gestion d'erreurs et possibilitĂ© de reprise - RĂ©sumĂ© final avec durĂ©e, succĂšs, erreurs **Utilisation du script batch :** ```bash # Test avec 1 entitĂ© ./migrate_batch.sh --start 1 --limit 1 # Migration de 50 entitĂ©s ./migrate_batch.sh --start 1 --limit 50 # Migration complĂšte (406 entitĂ©s) avec continuitĂ© sur erreur ./migrate_batch.sh --continue # Mode simulation (dry-run) ./migrate_batch.sh --dry-run ``` **Statistiques des 406 entitĂ©s :** - Total utilisateurs : ~15 000 - 20 000 (estimation) - Total passages : ~500 000 - 1 000 000 (estimation) - DurĂ©e estimĂ©e migration complĂšte : 8-12 heures **Statut :** ✅ PRÉPARÉ - Scripts prĂȘts, tests Ă  effectuer **Date de prĂ©paration :** 08/10/2025 --- #### 🔧 Correction critique des doublons ope_users et ope_users_sectors **Date :** 10/10/2025 **ProblĂšme identifiĂ© :** Doublons massifs dans les tables `ope_users` et `ope_users_sectors` lors de la migration **Diagnostic :** - Table `ope_users` : 186+ doublons pour la mĂȘme paire (fk_operation, fk_user) - Table `ope_users_sectors` : Risque de doublons sur (fk_operation, fk_user, fk_sector) - Cause : Absence de contraintes UNIQUE sur ces combinaisons de FK - Impact : Le `ON DUPLICATE KEY UPDATE` ne fonctionnait pas, permettant la crĂ©ation de lignes en double **Corrections appliquĂ©es :** 1. **Mise Ă  jour de la structure de rĂ©fĂ©rence** (`geo_app_structure.sql`) : - Ligne 403 : Ajout de `UNIQUE KEY idx_operation_user (fk_operation, fk_user)` sur `ope_users` - Ligne 430 : Ajout de `UNIQUE KEY idx_operation_user_sector (fk_operation, fk_user, fk_sector)` sur `ope_users_sectors` 2. **Correction du code PHP** (`migrate_from_backup.php`) : ```php // AVANT (lignes 1125-1140) - INCORRECT $sql = "SELECT ou.rowid, ou.fk_operation, ou.fk_user, ... INSERT INTO ope_users (id, fk_operation, fk_user, ...) VALUES (:id, ... // APRÈS (lignes 1125-1191) - CORRECT $sql = "SELECT DISTINCT ou.fk_operation, ou.fk_user, ... // Suppression de rowid INSERT INTO ope_users (fk_operation, fk_user, ...) VALUES (... // Suppression de id, utilise auto-increment ``` 3. **RequĂȘtes SQL pour appliquer les contraintes** : ```sql -- VĂ©rifier les doublons existants SELECT fk_operation, fk_user, COUNT(*) as count FROM ope_users GROUP BY fk_operation, fk_user HAVING count > 1; -- Ajouter les contraintes UNIQUE ALTER TABLE ope_users ADD UNIQUE KEY `idx_operation_user` (`fk_operation`, `fk_user`); ALTER TABLE ope_users_sectors ADD UNIQUE KEY `idx_operation_user_sector` (`fk_operation`, `fk_user`, `fk_sector`); -- VĂ©rifier SHOW INDEX FROM ope_users WHERE Key_name = 'idx_operation_user'; SHOW INDEX FROM ope_users_sectors WHERE Key_name = 'idx_operation_user_sector'; ``` **À appliquer sur tous les environnements :** - DEV (dva_geo sur maria3/IN3) : mysql -h 13.23.33.4 -u dva_geo_user - REC (rca_geo sur maria3/IN3) : mysql -h 13.23.33.4 -u rca_geo_user - PROD (pra_geo sur maria4/IN4) : mysql -h 13.23.33.4 -u pra_geo_user **ProcĂ©dure de re-migration aprĂšs correction :** ```bash # Supprimer les donnĂ©es de l'entitĂ© avant re-migration php scripts/php/migrate_from_backup.php \ --source-db=geosector_20251008 \ --mode=entity \ --entity-id=5 \ --delete-before ``` **Fichiers modifiĂ©s :** - `scripts/php/geo_app_structure.sql` (lignes 403, 430) - `scripts/php/migrate_from_backup.php` (lignes 1125-1191) - `scripts/README-migration.md` (section Corrections Critiques ajoutĂ©e) - `scripts/CORRECTIONS_MIGRATE.md` (correction #15 documentĂ©e) **Statut :** ✅ CORRIGÉ - Code et structure mis Ă  jour, contraintes Ă  appliquer sur bases **Date de correction :** 10/10/2025 --- #### 🎯 Planning de dĂ©ploiement (09-10/10/2025) **Jeudi 09/10/2025 - Environnement DEV (dva-geo)** - [ ] Vider la base `dva_geo` (donnĂ©es de test) - [ ] DĂ©ployer les scripts corrigĂ©s (`deploy-api.sh`) - [ ] Test migration 1 entitĂ© pilote (#1178 - 5 users, 1317 passages) - [ ] Validation des mappings secteurs - [ ] Si OK : Migration batch des 406 entitĂ©s - [ ] VĂ©rification intĂ©gritĂ© donnĂ©es (comptages, FK) - [ ] Tests fonctionnels API + Flutter **Jeudi 09/10/2025 PM - Environnement REC (rca-geo)** - [ ] Dump `dva_geo` → Import dans `rca_geo` - [ ] Tests de non-rĂ©gression complets - [ ] Validation client sur REC - [ ] Tests de charge (si applicable) **Vendredi 10/10/2025 - Environnement PROD (pra-geo)** - [ ] Sauvegarde complĂšte `pra_geo` (avant migration) - [ ] Dump `rca_geo` → Import dans `pra_geo` - [ ] Tests de smoke en production - [ ] Monitoring des performances - [ ] Documentation post-migration **Rollback plan :** - Restauration depuis sauvegarde prĂ©-migration : 15-30 minutes - Scripts de vĂ©rification disponibles dans `scripts/php/verify_migration_structure.php` **Statut :** 📅 PLANIFIÉ - DĂ©marrage jeudi 09/10/2025 matin **DurĂ©e estimĂ©e totale :** 2 jours --- ### 🟡 PRIORITÉ MOYENNE #### 4. AmĂ©lioration des logs - Ajouter plus de contexte dans les logs - Rotation automatique des logs - Dashboard de monitoring #### 5. Optimisation des performances - Cache des requĂȘtes frĂ©quentes - Index sur les tables volumineuses - Pagination optimisĂ©e #### 6. SĂ©curisation des clĂ©s Stripe par environnement **Statut :** ✅ **PARTIELLEMENT RÉSOLU** (16/10/2025) **Ce qui a Ă©tĂ© fait :** - ✅ Configuration complĂšte des clĂ©s API Stripe par environnement dans `AppConfig.php` - DEV : ClĂ©s TEST Pierre (compte plateforme dĂ©veloppeur) - RECETTE : ClĂ©s TEST Client (compte plateforme client mode test) - PRODUCTION : ClĂ©s LIVE Client (compte plateforme client mode live) - ✅ Configuration des webhooks Stripe avec secrets dĂ©diĂ©s - RECETTE : `webhook-rca` → `whsec_avExshr0MeWTI7wXP8478XVUkrbYG8hs` - PRODUCTION : `webhook-pra` → `whsec_gFnA6pR92RLdbAS2T6CSC18xsSdNBZHR` - ✅ Mise Ă  jour de la version API Stripe : `2025-08-27.basil` - ✅ Correction du bug StripeWebhookController (ligne 46) - ✅ Correction de l'URL webhook : `/api/stripe/webhooks` (avec 's') - ✅ Documentation complĂšte dans `TECHBOOK.md` **Ce qui reste (amĂ©lioration future) :** - ⏳ Migration vers variables d'environnement pour sĂ©curitĂ© renforcĂ©e - ⏳ Isolation des secrets par container (actuellement tous visibles dans AppConfig.php) **Solutions Ă  Ă©tudier pour amĂ©lioration future :** 1. **Variables d'environnement** (`.env` par container) - Fichier `.env.dev`, `.env.rec`, `.env.prod` - Chargement dynamique selon l'environnement - Exclusion des `.env` du versionning Git 2. **Fichiers de config sĂ©parĂ©s** - `config/stripe.dev.php`, `config/stripe.rec.php`, `config/stripe.prod.php` - DĂ©ploiement sĂ©lectif selon l'environnement - Non versionnĂ©s (ajoutĂ©s au .gitignore) 3. **Secrets management** (avancĂ©) - HashiCorp Vault, AWS Secrets Manager, etc. - API de rĂ©cupĂ©ration sĂ©curisĂ©e des secrets **Recommandation :** Approche #1 (variables d'environnement) pour Ă©quilibre sĂ©curitĂ©/simplicitĂ© --- ### 🟱 PRIORITÉ BASSE #### 7. AmĂ©lioration de la suppression des utilisateurs **DemandĂ© le :** 19/10/2025 **Objectif :** EmpĂȘcher la suppression d'un utilisateur ayant des passages et gĂ©rer correctement le soft delete. **ProblĂšme actuel :** - Erreur SQL lors de `DELETE /api/users/{id}` si l'utilisateur a des passages dans `ope_pass` - Contrainte FK `ope_pass_ibfk_3` empĂȘche la suppression (comportement attendu) - GĂ©nĂšre des emails d'alerte "GEOSECTOR SECURITY" de type SQL_ERROR **Solution Ă  implĂ©menter (cĂŽtĂ© Flutter) :** 1. **VĂ©rification avant suppression** - Endpoint suggĂ©rĂ© : `GET /api/users/{id}/can-delete` - Retourne : total passages, passages opĂ©rations actives, passages opĂ©rations inactives 2. **Logique mĂ©tier Flutter** - Si passages sur **opĂ©ration active** → Demander rĂ©assignation Ă  un autre user - Si passages uniquement sur **opĂ©rations inactives** → Proposer soft delete - Si aucun passage → Autoriser suppression physique 3. **Soft delete (recommandĂ©)** - Ajouter `deleted_at TIMESTAMP NULL` dans table `users` - Modifier `UserController::deleteUser()` pour faire un soft delete - Les utilisateurs soft-deleted ne sont plus affichĂ©s mais conservĂ©s pour l'historique **CĂŽtĂ© API (prĂ©paration) :** - [ ] Ajouter endpoint `GET /api/users/{id}/can-delete` pour vĂ©rification - [ ] Ajouter colonne `deleted_at` dans table `users` - [ ] Modifier `UserController::deleteUser()` pour gĂ©rer le soft delete - [ ] Adapter les requĂȘtes SQL pour filtrer `WHERE deleted_at IS NULL` **CĂŽtĂ© Flutter (Ă  implĂ©menter par le dĂ©veloppeur) :** - [ ] Appeler endpoint de vĂ©rification avant suppression - [ ] Afficher dialogue selon le contexte (rĂ©assignation vs soft delete) - [ ] GĂ©rer le workflow de rĂ©assignation si nĂ©cessaire **Note :** En attendant l'implĂ©mentation, les emails SQL_ERROR pour `DELETE /api/users/*` sont normaux et attendus (violation de contrainte FK lĂ©gitime). **Statut :** 📝 NOTÉ - À implĂ©menter cĂŽtĂ© Flutter en prioritĂ© **Date :** 19/10/2025 --- ### 🔮 PRIORITÉ HAUTE #### 8. Connexion de l'API Ă  un broker Mosquitto MQTT **DemandĂ© le :** 08/11/2025 **Objectif :** IntĂ©grer un systĂšme de communication MQTT permettant Ă  l'API de publier et recevoir des messages en temps rĂ©el pour notifier les clients (applications Flutter) des changements de donnĂ©es. **Contexte :** - Les applications Flutter doivent ĂȘtre notifiĂ©es en temps rĂ©el lors de modifications de donnĂ©es (passages, secteurs, opĂ©rations) - Alternative moderne aux webhooks pour la communication bidirectionnelle - Permet de rĂ©duire le polling constant des applications mobiles - Architecture publish/subscribe pour une meilleure scalabilitĂ© **Cas d'usage principaux :** ``` CrĂ©ation/Modification passage ↓ API publie sur MQTT: geosector/{entity_id}/passages/{operation_id} ↓ Payload JSON avec les donnĂ©es du passage ↓ Applications Flutter abonnĂ©es reçoivent la notification ↓ Mise Ă  jour automatique de l'UI sans refresh manuel Modification secteur ↓ API publie sur MQTT: geosector/{entity_id}/sectors/{operation_id} ↓ Applications concernĂ©es rechargent les donnĂ©es Changement statut opĂ©ration ↓ API publie sur MQTT: geosector/{entity_id}/operations/{operation_id} ↓ Notification push vers tous les membres ``` --- #### 📋 Plan d'action dĂ©taillĂ© ##### Étape 1 : Installation et configuration Mosquitto **A. Installation du broker Mosquitto** (sur serveur DVA/RCA/PROD) : ```bash # Sur le host ou dans un container dĂ©diĂ© apk add mosquitto mosquitto-clients # Alpine # ou apt install mosquitto mosquitto-clients # Debian/Ubuntu # DĂ©marrer et activer au boot rc-service mosquitto start # Alpine rc-update add mosquitto default # ou systemctl start mosquitto # Systemd systemctl enable mosquitto ``` **B. Configuration Mosquitto** (`/etc/mosquitto/mosquitto.conf`) : ```conf # Listeners listener 1883 0.0.0.0 protocol mqtt listener 8883 0.0.0.0 protocol mqtt certfile /etc/mosquitto/certs/cert.pem keyfile /etc/mosquitto/certs/key.pem cafile /etc/mosquitto/certs/ca.pem # WebSockets (pour Flutter Web) listener 9001 protocol websockets # Authentification allow_anonymous false password_file /etc/mosquitto/passwd # ACL (Access Control List) acl_file /etc/mosquitto/acl.conf # Persistence persistence true persistence_location /var/lib/mosquitto/ # Logs log_dest file /var/log/mosquitto/mosquitto.log log_type all log_timestamp true ``` **C. CrĂ©er les utilisateurs MQTT** : ```bash # Utilisateur pour l'API mosquitto_passwd -c /etc/mosquitto/passwd geosector_api # Password: gĂ©nĂ©rĂ© de maniĂšre sĂ©curisĂ©e # Utilisateur pour les applications Flutter mosquitto_passwd /etc/mosquitto/passwd geosector_client ``` **D. Configuration ACL** (`/etc/mosquitto/acl.conf`) : ```conf # API peut publier et s'abonner Ă  tous les topics geosector user geosector_api topic readwrite geosector/# # Clients peuvent seulement s'abonner user geosector_client topic read geosector/# ``` **E. Tests de connexion** : ```bash # Test publication mosquitto_pub -h localhost -p 1883 -u geosector_api -P password \ -t "geosector/test" -m "Test message" # Test souscription mosquitto_sub -h localhost -p 1883 -u geosector_client -P password \ -t "geosector/#" ``` **Fichiers impactĂ©s :** - [ ] Installer Mosquitto sur DVA (13.23.33.43) - [ ] Installer Mosquitto sur RCA (13.23.33.23) - [ ] Installer Mosquitto sur PROD (13.23.33.22 / IN4) - [ ] Configurer firewall (port 1883, 8883, 9001) --- ##### Étape 2 : Installation bibliothĂšque PHP MQTT ```bash # Via Composer composer require php-mqtt/client ``` **Fichiers impactĂ©s :** - [ ] Modifier `composer.json` - [ ] ExĂ©cuter sur DVA, RCA, PROD --- ##### Étape 3 : Configuration AppConfig.php Ajouter la configuration MQTT : ```php // Dans src/Config/AppConfig.php 'mqtt' => [ 'enabled' => true, 'broker' => [ 'host' => 'localhost', // ou IP du container mosquitto 'port' => 1883, 'username' => 'geosector_api', 'password' => '', // À dĂ©finir par environnement 'client_id' => 'geosector_api_' . gethostname(), ], 'tls' => [ 'enabled' => false, // true en production 'port' => 8883, 'ca_file' => '/etc/mosquitto/certs/ca.pem', ], 'topics' => [ 'passages' => 'geosector/{entity_id}/passages/{operation_id}', 'sectors' => 'geosector/{entity_id}/sectors/{operation_id}', 'operations' => 'geosector/{entity_id}/operations/{operation_id}', 'users' => 'geosector/{entity_id}/users', 'system' => 'geosector/system', ], 'qos' => 1, // Quality of Service (0, 1, ou 2) 'retain' => false, ], ``` **Fichiers impactĂ©s :** - [ ] Modifier `src/Config/AppConfig.php` (3 environnements) --- ##### Étape 4 : CrĂ©er le service MqttService CrĂ©er `src/Services/MqttService.php` : **MĂ©thodes principales :** ```php namespace App\Services; use PhpMqtt\Client\MqttClient; use PhpMqtt\Client\ConnectionSettings; use App\Services\LogService; use Exception; class MqttService { private static ?self $instance = null; private ?MqttClient $client = null; private array $config; private bool $connected = false; private function __construct() { $this->config = AppConfig::getInstance()->get('mqtt'); } public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } /** * Connecte au broker MQTT */ public function connect(): bool /** * DĂ©connecte du broker MQTT */ public function disconnect(): void /** * Publie un message sur un topic * @param string $topic Topic MQTT * @param array $payload DonnĂ©es Ă  publier (sera converti en JSON) * @param int $qos Quality of Service (0, 1, 2) * @param bool $retain Retenir le message */ public function publish(string $topic, array $payload, int $qos = 1, bool $retain = false): bool /** * Publie une notification de passage créé/modifiĂ© */ public function publishPassageUpdate(int $entityId, int $operationId, array $passageData): bool /** * Publie une notification de secteur créé/modifiĂ© */ public function publishSectorUpdate(int $entityId, int $operationId, array $sectorData): bool /** * Publie une notification d'opĂ©ration créée/modifiĂ©e */ public function publishOperationUpdate(int $entityId, int $operationId, array $operationData): bool /** * Publie un message systĂšme (maintenance, dĂ©connexion forcĂ©e, etc.) */ public function publishSystemMessage(string $message, array $metadata = []): bool /** * GĂ©nĂšre le topic en remplaçant les placeholders */ private function buildTopic(string $topicTemplate, array $params): string /** * VĂ©rifie si le service est activĂ© et connectĂ© */ public function isAvailable(): bool } ``` **Gestion des erreurs :** - Reconnexion automatique en cas de dĂ©connexion - Timeout configurable - Logs dĂ©taillĂ©s des publications - Mode dĂ©gradĂ© si MQTT indisponible (ne pas bloquer l'API) **Fichiers impactĂ©s :** - [ ] CrĂ©er `src/Services/MqttService.php` --- ##### Étape 5 : IntĂ©grer MQTT dans les Controllers **A. PassageController** - Notifier crĂ©ation/modification passage : ```php // Dans PassageController::create() aprĂšs ligne 602 if ($passageId) { // Publier notification MQTT (non bloquant) try { $mqttService = MqttService::getInstance(); if ($mqttService->isAvailable()) { $mqttService->publishPassageUpdate( $entityId, $operationId, [ 'passage_id' => $passageId, 'fk_type' => $fkType, 'montant' => $montant, 'action' => 'created', 'timestamp' => time() ] ); } } catch (Exception $e) { // Logger mais ne pas bloquer l'API LogService::log('Erreur publication MQTT passage', [ 'level' => 'warning', 'error' => $e->getMessage() ]); } } ``` **B. SectorController** - Notifier crĂ©ation/modification secteur : ```php // Dans SectorController::create() aprĂšs ligne 531 $mqttService = MqttService::getInstance(); if ($mqttService->isAvailable()) { $mqttService->publishSectorUpdate( $entityId, $operationId, [ 'sector_id' => $sectorId, 'libelle' => $data['libelle'], 'passages_created' => $passagesCreated, 'action' => 'created', 'timestamp' => time() ] ); } ``` **C. OperationController** - Notifier changements opĂ©ration : ```php // Notifications pour : // - Changement de statut (chk_active) // - Ajout/retrait d'utilisateurs // - Modification des dates ``` **Fichiers impactĂ©s :** - [ ] Modifier `src/Controllers/PassageController.php` - [ ] Modifier `src/Controllers/SectorController.php` - [ ] Modifier `src/Controllers/OperationController.php` - [ ] Modifier `src/Controllers/UserController.php` (optionnel) --- ##### Étape 6 : Structure des messages MQTT **Format JSON standardisĂ© :** ```json { "event": "passage.created", "entity_id": 5, "operation_id": 12345, "timestamp": 1699456789, "data": { "passage_id": 19500576, "fk_type": 1, "montant": 10.00, "encrypted_name": "...", "rue": "Rue Example", "ville": "Rennes" }, "metadata": { "user_id": 9999985, "api_version": "3.3.5" } } ``` **Types d'Ă©vĂ©nements :** - `passage.created` - `passage.updated` - `passage.deleted` - `sector.created` - `sector.updated` - `sector.deleted` - `operation.created` - `operation.updated` - `operation.status_changed` - `user.added` - `user.removed` - `system.maintenance` --- ##### Étape 7 : SĂ©curisation **A. Topics hiĂ©rarchiques par entitĂ© :** ``` geosector/{entity_id}/* → Seuls les membres de cette entitĂ© peuvent s'abonner ``` **B. Validation cĂŽtĂ© API :** - VĂ©rifier que l'utilisateur appartient Ă  l'entitĂ© avant publication - Ne jamais publier de donnĂ©es sensibles non chiffrĂ©es **C. Authentification JWT pour Flutter :** ```json { "mqtt_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "mqtt_host": "mqtt.geosector.fr", "mqtt_port": 8883, "topics": ["geosector/5/#"] } ``` **Fichiers impactĂ©s :** - [ ] CrĂ©er endpoint `POST /api/mqtt/token` (gĂ©nĂ©ration token MQTT) - [ ] Modifier ACL Mosquitto pour utiliser JWT --- ##### Étape 8 : Tests et validation **Tests sur DVA :** - [ ] Publier message test via API - [ ] S'abonner avec `mosquitto_sub` et vĂ©rifier rĂ©ception - [ ] CrĂ©er passage et vĂ©rifier publication MQTT - [ ] CrĂ©er secteur et vĂ©rifier publication MQTT - [ ] VĂ©rifier logs MQTT dans `/var/log/mosquitto/` - [ ] Tester reconnexion aprĂšs redĂ©marrage broker **Tests de charge :** - [ ] 100 publications simultanĂ©es - [ ] 50 clients connectĂ©s en parallĂšle - [ ] VĂ©rifier latence (doit ĂȘtre < 100ms) **Tests de non-rĂ©gression :** - [ ] API fonctionne si MQTT dĂ©sactivĂ© - [ ] API fonctionne si broker MQTT inaccessible (mode dĂ©gradĂ©) **Fichiers impactĂ©s :** - [ ] CrĂ©er `scripts/test/test_mqtt_integration.php` --- ##### Étape 9 : IntĂ©gration cĂŽtĂ© Flutter (information) **Package Flutter Ă  utiliser :** ```yaml dependencies: mqtt_client: ^10.0.0 ``` **Exemple de souscription :** ```dart final client = MqttServerClient('mqtt.geosector.fr', ''); await client.connect(username, password); client.subscribe('geosector/$entityId/passages/#', MqttQos.atLeastOnce); client.updates!.listen((List> messages) { final recMess = messages[0].payload as MqttPublishMessage; final payload = MqttPublishPayload.bytesToStringAsString(recMess.payload.message); final data = jsonDecode(payload); // Mettre Ă  jour l'UI en temps rĂ©el if (data['event'] == 'passage.created') { _refreshPassages(); } }); ``` --- ##### Étape 10 : Documentation **Mettre Ă  jour la documentation :** - [ ] `docs/TECHBOOK.md` - Section "MQTT Real-Time Communication" - [ ] `TODO-API.md` - Marquer la tĂąche comme terminĂ©e - [ ] CrĂ©er `docs/MQTT-INTEGRATION.md` avec : - Architecture MQTT - Topics disponibles - Format des messages - Exemples de souscription (PHP, Flutter, JavaScript) - Configuration ACL et sĂ©curitĂ© --- #### 📊 RĂ©sumĂ© des impacts **Infrastructure :** - Broker Mosquitto installĂ© sur chaque environnement - Ports ouverts : 1883 (MQTT), 8883 (MQTTS), 9001 (WebSockets) **Services créés :** - `MqttService.php` : Publication et gestion des messages **Controllers modifiĂ©s :** - `PassageController.php` : Publication Ă©vĂ©nements passages - `SectorController.php` : Publication Ă©vĂ©nements secteurs - `OperationController.php` : Publication Ă©vĂ©nements opĂ©rations **Configuration modifiĂ©e :** - `AppConfig.php` : Section 'mqtt' - `composer.json` : DĂ©pendance `php-mqtt/client` **Nouveaux endpoints :** - `POST /api/mqtt/token` : GĂ©nĂ©ration token JWT pour Flutter --- #### ⚠ Points d'attention 1. **Performance** : MQTT est asynchrone, ne doit JAMAIS bloquer l'API 2. **Mode dĂ©gradĂ©** : L'API doit fonctionner mĂȘme si MQTT est HS 3. **SĂ©curitĂ©** : ACL strictes, authentification obligatoire 4. **ScalabilitĂ©** : PrĂ©voir un broker MQTT dĂ©diĂ© si > 1000 clients 5. **Monitoring** : Logs Mosquitto Ă  surveiller (connexions, dĂ©connexions, erreurs) 6. **Certificats SSL** : Obligatoires en production (Let's Encrypt) 7. **QoS** : Utiliser QoS 1 (at least once) pour garantir la livraison --- #### 🔧 Architecture dĂ©ployĂ©e **Environnement DVA (IN3) :** ``` Container dva-geo (13.23.33.43) ├── API PHP (nginx + php-fpm) └── Mosquitto broker (1883, 8883, 9001) ``` **Environnement RCA (IN3) :** ``` Container rca-geo (13.23.33.23) ├── API PHP └── Mosquitto broker ``` **Environnement PROD (IN4) :** ``` Container pra-geo (13.23.33.22) ├── API PHP └── Mosquitto broker ``` **Alternative (scalable) :** ``` Container mqtt-broker (dĂ©diĂ©) ├── Mosquitto avec authentification JWT └── Monitoring (Prometheus + Grafana) ``` --- **Statut :** 📋 PLANIFIÉ - En attente de validation pour dĂ©marrage **Date de crĂ©ation :** 08/11/2025 **DurĂ©e estimĂ©e :** 2-3 jours (installation + dĂ©veloppement + tests) **DĂ©pendances :** Aucune **PrioritĂ© :** HAUTE - AmĂ©liore significativement l'expĂ©rience utilisateur --- #### 9. Envoi de SMS de reçu en alternative Ă  l'email **DemandĂ© le :** 08/11/2025 **Objectif :** Permettre l'envoi de SMS de reçu au contributeur lors de la crĂ©ation/modification de passages de type 1 ou 5, comme alternative Ă  l'email. **Contexte :** - La table `entites` possĂšde dĂ©jĂ  le champ `chk_accept_sms` (boolean) - Actuellement, les reçus sont envoyĂ©s par email pour les passages `fk_type = 1` ou `5` - Besoin d'envoyer un SMS de reçu si numĂ©ro mobile valide (10 chiffres commençant par 06 ou 07) - Comptabilisation par entitĂ© pour facturation ultĂ©rieure **Cas d'usage :** ``` CrĂ©ation passage avec fk_type = 1 ou 5 ↓ Si entite.chk_accept_sms = 1 ↓ Si encrypted_phone est mobile (06/07...) ↓ Envoyer SMS reçu au lieu d'email ↓ Comptabiliser dans sms_sent_count ``` --- #### 📋 Plan d'action dĂ©taillĂ© ##### Étape 1 : Modifications base de donnĂ©es **A. Table `entites`** - Ajouter compteur et gestion pack SMS : ```sql ALTER TABLE entites ADD COLUMN sms_sent_count INT UNSIGNED DEFAULT 0 COMMENT 'Nombre total de SMS envoyĂ©s pour facturation', ADD COLUMN sms_credits INT UNSIGNED DEFAULT 0 COMMENT 'CrĂ©dits SMS disponibles (si pack dĂ©diĂ©)', ADD COLUMN sms_pack_type ENUM('global', 'entity') DEFAULT 'global' COMMENT 'Type de pack SMS (global partagĂ© ou dĂ©diĂ©)', ADD KEY idx_chk_accept_sms (chk_accept_sms); ``` **B. Nouvelle table `sms_log`** - Historique des envois pour audit : ```sql CREATE TABLE IF NOT EXISTS sms_log ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, fk_entite INT UNSIGNED NOT NULL, fk_passage INT UNSIGNED NULL COMMENT 'Passage concernĂ© par le SMS', phone VARCHAR(20) NOT NULL COMMENT 'NumĂ©ro destinataire (format international)', message TEXT NOT NULL COMMENT 'Contenu du SMS envoyĂ©', status ENUM('pending', 'sent', 'failed', 'error') DEFAULT 'pending', ovh_message_id VARCHAR(100) NULL COMMENT 'ID retournĂ© par API OVH', credits_used DECIMAL(5,2) DEFAULT 1.00 COMMENT 'CrĂ©dits SMS consommĂ©s', error_message TEXT NULL, sent_at TIMESTAMP NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, KEY idx_fk_entite (fk_entite), KEY idx_fk_passage (fk_passage), KEY idx_status (status), KEY idx_sent_at (sent_at), CONSTRAINT sms_log_ibfk_1 FOREIGN KEY (fk_entite) REFERENCES entites(id) ON DELETE CASCADE, CONSTRAINT sms_log_ibfk_2 FOREIGN KEY (fk_passage) REFERENCES ope_pass(id) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` **C. Table de configuration `sms_config`** : ```sql CREATE TABLE IF NOT EXISTS sms_config ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, config_key VARCHAR(50) UNIQUE NOT NULL, config_value TEXT NOT NULL, description VARCHAR(255) NULL, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_config_key (config_key) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Configuration initiale INSERT INTO sms_config (config_key, config_value, description) VALUES ('ovh_service_name', '', 'Nom du service SMS OVH'), ('ovh_app_key', '', 'Application Key OVH'), ('ovh_app_secret', '', 'Application Secret OVH (chiffrĂ©)'), ('ovh_consumer_key', '', 'Consumer Key OVH (chiffrĂ©)'), ('global_sms_credits', '0', 'CrĂ©dits SMS globaux disponibles'), ('default_sender', 'GEOSECTOR', 'Nom d\'expĂ©diteur (11 chars max)'), ('sms_enabled', '1', 'Activation globale du systĂšme SMS'), ('sms_receipt_template', 'Merci pour votre don de {montant}EUR par {reglement}. Recu fiscal disponible sur demande. {entite_nom}', 'Template du SMS de reçu'); ``` **Fichiers impactĂ©s :** - [ ] CrĂ©er `scripts/migrations/add_sms_receipts.sql` - [ ] Appliquer sur DVA, REC, PROD --- ##### Étape 2 : Configuration AppConfig.php Ajouter la configuration OVH SMS : ```php // Dans src/Config/AppConfig.php 'sms' => [ 'enabled' => true, 'provider' => 'ovh', 'ovh' => [ 'endpoint' => 'ovh-eu', 'application_key' => '', // ChargĂ© depuis sms_config 'application_secret' => '', // ChargĂ© depuis sms_config 'consumer_key' => '', // ChargĂ© depuis sms_config 'service_name' => '', // ChargĂ© depuis sms_config ], 'default_sender' => 'GEOSECTOR', 'max_length' => 160, ], ``` **Fichiers impactĂ©s :** - [ ] Modifier `src/Config/AppConfig.php` (3 environnements) --- ##### Étape 3 : Installation SDK OVH ```bash composer require ovh/ovh ``` **Fichiers impactĂ©s :** - [ ] Modifier `composer.json` - [ ] ExĂ©cuter sur DVA, REC, PROD --- ##### Étape 4 : Nouveau service SmsService CrĂ©er `src/Services/SmsService.php` avec les mĂ©thodes principales : - `sendReceiptSms()` - Envoie un SMS de reçu aprĂšs un passage payĂ© - `canEntitySendSms()` - VĂ©rifie si l'entitĂ© peut envoyer des SMS (crĂ©dits, autorisation) - `isValidMobile()` - Valide un numĂ©ro mobile français (06/07) - `formatPhoneNumber()` - Formate au format international (+33...) - `generateReceiptMessage()` - GĂ©nĂšre le message depuis template - `incrementSmsCounter()` - IncrĂ©mente le compteur de facturation - `decrementCredits()` - DĂ©cremente les crĂ©dits (global ou entity) - `logSms()` - Enregistre dans sms_log **Template de message par dĂ©faut :** ``` Merci pour votre don de {montant}EUR par {reglement}. Recu fiscal disponible sur demande. {entite_nom} ``` **Fichiers impactĂ©s :** - [ ] CrĂ©er `src/Services/SmsService.php` --- ##### Étape 5 : Modifier PassageController **Modifications dans `PassageController::create()` (lignes 606-639) :** Ajouter logique de dĂ©cision SMS vs Email : ```php $fkType = isset($data['fk_type']) ? (int)$data['fk_type'] : 0; if ($fkType === 1 || $fkType === 5) { // RĂ©cupĂ©rer l'entity_id de l'opĂ©ration $stmtEntity = $this->db->prepare('SELECT fk_entite FROM operations WHERE id = ?'); $stmtEntity->execute([$operationId]); $entityId = (int)$stmtEntity->fetchColumn(); // VĂ©rifier si l'entitĂ© accepte les SMS $stmtEntite = $this->db->prepare('SELECT chk_accept_sms FROM entites WHERE id = ?'); $stmtEntite->execute([$entityId]); $acceptSms = (bool)$stmtEntite->fetchColumn(); // DĂ©terminer si on envoie SMS ou Email $shouldSendSms = false; $phone = null; if ($acceptSms && !empty($encryptedPhone)) { $phone = ApiService::decryptData($encryptedPhone); $smsService = new \App\Services\SmsService($this->db); if ($smsService->isValidMobile($phone)) { $shouldSendSms = true; } } if ($shouldSendSms && $phone) { // ENVOI SMS via register_shutdown_function // RĂ©cupĂ©rer type de rĂšglement pour le message // Appeler sendReceiptSms() } else { // ENVOI EMAIL (logique existante) } } ``` **MĂȘme logique dans `PassageController::update()` si changement de type.** **Fichiers impactĂ©s :** - [ ] Modifier `src/Controllers/PassageController.php` --- ##### Étape 6 : Endpoints API pour gestion SMS (optionnel) **CrĂ©er `SmsController.php`** avec : - `GET /api/sms/stats/{entity_id}` - Statistiques SMS d'une entitĂ© - `GET /api/sms/history/{entity_id}` - Historique des envois - `GET /api/sms/credits` - Solde crĂ©dits global et par entitĂ© - `PUT /api/sms/config` - Configuration SMS (admin) **Fichiers impactĂ©s :** - [ ] CrĂ©er `src/Controllers/SmsController.php` (optionnel) - [ ] Modifier `src/Core/Router.php` (optionnel) --- ##### Étape 7 : Tests et validation **Tests sur DVA :** - [ ] CrĂ©er entitĂ© test avec `chk_accept_sms = 1` - [ ] CrĂ©er passage `fk_type = 1` avec mobile valide (06/07) - [ ] VĂ©rifier envoi SMS via OVH - [ ] VĂ©rifier enregistrement dans `sms_log` - [ ] VĂ©rifier incrĂ©mentation de `sms_sent_count` - [ ] VĂ©rifier dĂ©bit des crĂ©dits **Tests de non-rĂ©gression :** - [ ] EntitĂ© avec `chk_accept_sms = 0` → Email uniquement - [ ] NumĂ©ro fixe (01-05, 08, 09) → Email uniquement - [ ] Sans tĂ©lĂ©phone mais avec email → Email uniquement - [ ] Passage `fk_type = 2, 3, 4` → Pas d'envoi **Fichiers impactĂ©s :** - [ ] CrĂ©er `scripts/test/test_sms_receipts.php` --- ##### Étape 8 : Documentation **Mettre Ă  jour la documentation :** - [ ] `docs/TECHBOOK.md` - Section "Envoi de SMS de reçu" - [ ] `TODO-API.md` - Marquer la tĂąche comme terminĂ©e - [ ] CrĂ©er `docs/SMS-RECEIPTS.md` avec : - Configuration OVH SMS - Template de message personnalisable - Gestion des crĂ©dits (global vs entitĂ©) - Facturation basĂ©e sur `sms_sent_count` --- #### 📊 RĂ©sumĂ© des impacts **Tables créées :** - `sms_log` : Historique des envois - `sms_config` : Configuration OVH **Tables modifiĂ©es :** - `entites` : +3 colonnes (sms_sent_count, sms_credits, sms_pack_type) **Services créés :** - `SmsService.php` : Gestion envoi SMS via OVH **Controllers modifiĂ©s :** - `PassageController.php` : Logique SMS vs Email selon contexte **Configuration modifiĂ©e :** - `AppConfig.php` : Section 'sms' - `composer.json` : DĂ©pendance `ovh/ovh` --- #### ⚠ Points d'attention 1. **Validation numĂ©ro mobile** : Uniquement 06/07 (10 chiffres) 2. **Longueur SMS** : 160 caractĂšres max (template Ă  optimiser) 3. **CoĂ»t** : ~0.035€/SMS en France 4. **PrioritĂ©** : SMS si mobile valide, sinon Email en fallback 5. **Comptabilisation** : `sms_sent_count` pour facturation 6. **Credentials OVH** : À ne JAMAIS commiter, stockĂ©s en base chiffrĂ©s 7. **Template personnalisable** : Dans `sms_config.sms_receipt_template` --- #### 🔧 Configuration OVH requise **Étapes prĂ©alables :** 1. CrĂ©er application OVH : https://eu.api.ovh.com/createApp/ 2. GĂ©nĂ©rer Consumer Key avec droits `/sms/*` 3. Commander service SMS OVH 4. CrĂ©diter le compte (minimum 100-200 SMS pour tests) **Stockage en base :** ```sql UPDATE sms_config SET config_value = 'sms-ab123456-1' WHERE config_key = 'ovh_service_name'; UPDATE sms_config SET config_value = 'xxxxx' WHERE config_key = 'ovh_app_key'; UPDATE sms_config SET config_value = 'xxxxx' WHERE config_key = 'ovh_app_secret'; UPDATE sms_config SET config_value = 'xxxxx' WHERE config_key = 'ovh_consumer_key'; UPDATE sms_config SET config_value = '1000' WHERE config_key = 'global_sms_credits'; ``` --- **Statut :** 📋 PLANIFIÉ - En attente de dĂ©marrage **Date de crĂ©ation :** 08/11/2025 **DurĂ©e estimĂ©e :** 2-3 jours (dĂ©veloppement + tests + configuration OVH) **DĂ©pendances :** Compte OVH avec service SMS actif --- #### 9. IntĂ©gration de la base "batiments" dans la gestion des secteurs **DemandĂ© le :** 07/11/2025 **Objectif :** IntĂ©grer la base de donnĂ©es "batiments" pour identifier les immeubles et crĂ©er automatiquement un passage par logement lors de la crĂ©ation/modification des secteurs. **Contexte :** - **Nouvelle base** : `batiments` (mĂȘme host/credentials que base `adresses`) - **Structure** : Tables par dĂ©partement `bat01`, `bat02`, etc. - **Lien** : `bat{dept}.cle_interop_adr` → `cp{dept}.id` - **Contenu** : Uniquement les immeubles avec mĂ©tadonnĂ©es (nb_niveau, nb_log, residence, etc.) **Structure de la table bat{dept} :** ```sql batiment_groupe_id VARCHAR(50) PRIMARY KEY code_departement_insee VARCHAR(5) cle_interop_adr VARCHAR(50) -- Lien vers cp{dept}.id nb_niveau INT -- Nombre d'Ă©tages nb_log INT -- Nombre de logements nb_pdl_tot INT -- Compteurs Ă©lectriques annee_construction INT residence VARCHAR(200) -- Nom de la copropriĂ©tĂ© usage_principal VARCHAR(100) altitude_sol_mean DECIMAL(10,2) gps_lat DECIMAL(10,7) gps_lng DECIMAL(10,7) ``` **Filtre appliquĂ© lors de l'import :** - `usage_principal IN ('RĂ©sidentiel individuel', 'RĂ©sidentiel collectif', 'Secondaire', 'Tertiaire')` - `nb_log > 1` (immeubles uniquement, pas les maisons individuelles) --- #### 📋 Plan d'action dĂ©taillĂ© ##### Étape 1 : Modifications base de donnĂ©es **A. Table `sectors_adresses`** - Ajouter colonnes bĂątiment : ```sql ALTER TABLE sectors_adresses ADD COLUMN fk_batiment VARCHAR(50) NULL COMMENT 'batiment_groupe_id' AFTER fk_adresse, ADD COLUMN fk_habitat TINYINT NULL COMMENT '1=individuel, 2=collectif', ADD COLUMN nb_niveau INT NULL COMMENT 'Nombre d\'Ă©tages', ADD COLUMN nb_log INT NULL COMMENT 'Nombre de logements', ADD COLUMN residence VARCHAR(200) NULL COMMENT 'Nom copropriĂ©tĂ©', ADD COLUMN alt_sol DECIMAL(10,2) NULL COMMENT 'Altitude sol', ADD KEY idx_fk_batiment (fk_batiment), ADD KEY idx_fk_habitat (fk_habitat); ``` **B. Table `ope_pass`** - VĂ©rifier/modifier colonne habitat : ```sql -- VĂ©rifier si fk_habitat existe dĂ©jĂ  SHOW COLUMNS FROM ope_pass LIKE 'fk_habitat'; -- Si nĂ©cessaire, modifier la colonne ALTER TABLE ope_pass MODIFY COLUMN fk_habitat TINYINT NULL COMMENT '1=individuel, 2=collectif'; -- Ajouter index si manquant ALTER TABLE ope_pass ADD KEY idx_fk_habitat (fk_habitat); ``` **Fichiers impactĂ©s :** - [ ] CrĂ©er `scripts/migrations/add_batiments_integration.sql` - [ ] Appliquer sur DVA, REC, PROD --- ##### Étape 2 : Configuration AppConfig.php Ajouter la configuration de la base batiments : ```php // Dans src/Config/AppConfig.php 'buildings_database' => [ 'host' => '13.23.33.46', // DVA: 13.23.33.46 'name' => 'batiments', // RCA: 13.23.33.36 'username' => 'adr_geo_user', // PROD: 13.23.33.26 'password' => 'd66,AdrGeoDev.User', ], ``` **Fichiers impactĂ©s :** - [ ] Modifier `src/Config/AppConfig.php` (3 environnements DEV/REC/PROD) --- ##### Étape 3 : Nouveau service BuildingService CrĂ©er `src/Services/BuildingService.php` : **MĂ©thodes Ă  implĂ©menter :** ```php class BuildingService { private \PDO $dbBuildings; public function __construct() { // Connexion PDO vers base "batiments" $config = AppConfig::getInstance()->get('buildings_database'); $this->dbBuildings = new \PDO(...); } /** * RĂ©cupĂšre les bĂątiments dans un polygone * @param array $coordinates Format [[lat, lng], ...] * @param int|null $entityId Pour dĂ©terminer les dĂ©partements * @return array Liste des bĂątiments avec mĂ©tadonnĂ©es */ public function getBuildingsInPolygon(array $coordinates, ?int $entityId = null): array /** * Compte les bĂątiments dans un polygone */ public function countBuildingsInPolygon(array $coordinates, ?int $entityId = null): int /** * VĂ©rifie si la connexion Ă  la base batiments est active */ public function isConnected(): bool } ``` **Logique similaire Ă  AddressService :** - DĂ©tection automatique des dĂ©partements touchĂ©s par le polygone - RequĂȘte SQL spatiale sur toutes les tables `bat{dept}` concernĂ©es - Utilisation de `ST_Contains()` pour filtrer les bĂątiments **Fichiers impactĂ©s :** - [ ] CrĂ©er `src/Services/BuildingService.php` --- ##### Étape 4 : Enrichir AddressService Modifier `src/Services/AddressService.php` : **Nouvelle mĂ©thode Ă  ajouter :** ```php /** * Enrichit les adresses avec les donnĂ©es bĂątiment si disponibles * @param array $addresses Liste des adresses depuis getAddressesInPolygon() * @param int|null $entityId * @return array Adresses enrichies avec fk_batiment, fk_habitat, nb_log, etc. */ public function enrichAddressesWithBuildings(array $addresses, ?int $entityId = null): array { // Pour chaque adresse, chercher si cp{dept}.id correspond Ă  bat{dept}.cle_interop_adr // Enrichir avec : fk_batiment, fk_habitat, nb_niveau, nb_log, residence, alt_sol // Retourner les adresses enrichies } ``` **Logique :** 1. Grouper les adresses par dĂ©partement 2. Pour chaque dĂ©partement, faire un JOIN entre `cp{dept}` et `bat{dept}` 3. Enrichir les adresses avec les donnĂ©es bĂątiment trouvĂ©es 4. DĂ©finir `fk_habitat = 2` (collectif) si bĂątiment trouvĂ©, sinon `fk_habitat = 1` (individuel) **Fichiers impactĂ©s :** - [ ] Modifier `src/Services/AddressService.php` --- ##### Étape 5 : Modifier SectorController::create() **Modifications dans la mĂ©thode `create()` (ligne 88-534) :** ```php // APRÈS ligne 289 - RĂ©cupĂ©ration des adresses $addresses = $this->addressService->getAddressesInPolygon($coordinates, $entityId); // NOUVEAU : Enrichir avec donnĂ©es batiments $addresses = $this->addressService->enrichAddressesWithBuildings($addresses, $entityId); // MODIFIER ligne 292-311 : Stockage dans sectors_adresses avec nouvelles colonnes foreach ($addresses as $address) { $stmtAddress->execute([ 'sector_id' => $sectorId, 'address_id' => $address['id'], 'numero' => $address['numero'], 'rue' => $address['voie'], 'rue_bis' => '', 'cp' => $address['code_postal'], 'ville' => $address['commune'], 'gps_lat' => $address['latitude'], 'gps_lng' => $address['longitude'], // NOUVELLES COLONNES 'fk_batiment' => $address['fk_batiment'] ?? null, 'fk_habitat' => $address['fk_habitat'] ?? 1, 'nb_niveau' => $address['nb_niveau'] ?? null, 'nb_log' => $address['nb_log'] ?? null, 'residence' => $address['residence'] ?? null, 'alt_sol' => $address['alt_sol'] ?? null, ]); } // MODIFIER ligne 314-377 : CrĂ©ation des passages selon fk_habitat foreach ($addresses as $address) { // Exclure les adresses dĂ©jĂ  utilisĂ©es par passages orphelins if (in_array($address['id'], $addressesToExclude)) { continue; } if ($address['fk_habitat'] == 2 && !empty($address['nb_log']) && $address['nb_log'] > 1) { // IMMEUBLE : crĂ©er nb_log passages (1 par logement) for ($appt = 1; $appt <= $address['nb_log']; $appt++) { $passageStmt->execute([ 'operation_id' => $operationId, 'sector_id' => $sectorId, 'user_id' => $firstOpeUserId, 'fk_adresse' => $address['id'], 'numero' => $address['numero'], 'rue' => $address['voie'], 'rue_bis' => '', 'ville' => $address['commune'], 'gps_lat' => $address['latitude'], 'gps_lng' => $address['longitude'], 'fk_habitat' => 2, 'appt' => $appt, // NumĂ©ro d'appartement 'residence' => $address['residence'] ?? null, 'user_creat' => $userId ]); $passagesCreated++; } } else { // MAISON INDIVIDUELLE : 1 seul passage $passageStmt->execute([ // ... mĂȘme structure avec fk_habitat=1, appt=null ]); $passagesCreated++; } } ``` **Fichiers impactĂ©s :** - [ ] Modifier `src/Controllers/SectorController.php` (mĂ©thode `create()`) --- ##### Étape 6 : Modifier SectorController::update() **Modifications dans la mĂ©thode `update()` (ligne 539-938) :** 1. Ligne 702-773 : Mise Ă  jour de `sectors_adresses` avec nouvelles colonnes 2. Ligne 1243-1557 : Modifier `updatePassagesForSector()` pour gĂ©rer les immeubles **Dans `updatePassagesForSector()` :** ```php // Ligne 1346-1350 : RĂ©cupĂ©rer adresses depuis sectors_adresses $addressesQuery = "SELECT * FROM sectors_adresses WHERE fk_sector = :sector_id"; $addresses = $addressesStmt->fetchAll(); // Ligne 1456-1495 : INSERT MULTIPLE - Adapter pour crĂ©er nb_log passages si immeuble foreach ($toInsert as $addr) { if ($addr['fk_habitat'] == 2 && $addr['nb_log'] > 1) { // CrĂ©er nb_log passages pour cet immeuble for ($appt = 1; $appt <= $addr['nb_log']; $appt++) { // Ajouter Ă  la liste d'insertion } } else { // CrĂ©er 1 seul passage } } ``` **Fichiers impactĂ©s :** - [ ] Modifier `src/Controllers/SectorController.php` (mĂ©thodes `update()` et `updatePassagesForSector()`) --- ##### Étape 7 : Tests et validation **Tests sur DVA :** - [ ] CrĂ©er un secteur test contenant des immeubles connus - [ ] VĂ©rifier le nombre de passages créés (doit correspondre au total de logements) - [ ] Valider les donnĂ©es dans `sectors_adresses` (colonnes batiments remplies) - [ ] Valider les donnĂ©es dans `ope_pass` (fk_habitat, appt, residence) - [ ] Tester la modification d'un secteur (agrandissement/rĂ©duction) - [ ] VĂ©rifier le comportement avec passages orphelins **Tests de non-rĂ©gression :** - [ ] Secteurs sans immeubles (maisons individuelles uniquement) - [ ] Secteurs multi-dĂ©partements - [ ] Secteurs sans adresses (base adresses inaccessible) **Fichiers impactĂ©s :** - [ ] CrĂ©er `scripts/test/test_batiments_integration.php` --- ##### Étape 8 : Documentation **Mettre Ă  jour la documentation :** - [ ] `docs/GESTION-SECTORS.md` - Section "Gestion des bĂątiments" - [ ] `docs/TECHBOOK.md` - Section "Base de donnĂ©es" - [ ] `TODO-API.md` - Marquer la tĂąche comme terminĂ©e **Contenu Ă  documenter :** - Structure de la table `bat{dept}` - Logique de crĂ©ation de passages pour immeubles (1 passage par logement) - Format de rĂ©ponse API enrichi avec donnĂ©es bĂątiments - Exemples de requĂȘtes SQL pour interroger les bĂątiments --- #### 📊 RĂ©sumĂ© des impacts **Tables modifiĂ©es :** - `sectors_adresses` : +6 colonnes - `ope_pass` : Modification colonne `fk_habitat` (si nĂ©cessaire) **Services créés/modifiĂ©s :** - **Nouveau** : `BuildingService.php` - **ModifiĂ©** : `AddressService.php` (enrichissement) **Controllers modifiĂ©s :** - `SectorController.php` : MĂ©thodes `create()`, `update()`, `updatePassagesForSector()` **Configuration modifiĂ©e :** - `AppConfig.php` : Ajout `buildings_database` **Impact sur la crĂ©ation de secteur :** - **AVANT** : 1 passage par adresse - **APRÈS** : 1 passage par logement (immeubles = nb_log passages) - **Exemple** : Secteur avec 100 adresses dont 20 immeubles de 10 logements - Avant : 100 passages - AprĂšs : 80 maisons + (20 × 10 immeubles) = 280 passages --- #### ⚠ Points d'attention 1. **Performance** : Les immeubles gĂ©nĂšrent beaucoup plus de passages - Optimiser les INSERT multiple dans `updatePassagesForSector()` - PrĂ©voir un timeout plus long pour les gros secteurs 2. **CohĂ©rence des donnĂ©es** : La base `batiments` doit ĂȘtre Ă  jour - VĂ©rifier la prĂ©sence des dĂ©partements concernĂ©s - GĂ©rer le cas oĂč la base batiments est inaccessible (fallback) 3. **Migration des donnĂ©es existantes** : Les secteurs dĂ©jĂ  créés - Option 1 : Ne pas toucher aux secteurs existants - Option 2 : Script de migration pour enrichir les secteurs existants 4. **Synchronisation** : Base `batiments` vs `adresses` - Le lien `cle_interop_adr` doit ĂȘtre valide - GĂ©rer les adresses sans bĂątiment correspondant --- **Statut :** 📋 PLANIFIÉ - En attente de validation pour dĂ©marrage **Date de crĂ©ation :** 07/11/2025 **DurĂ©e estimĂ©e :** 2-3 jours (dĂ©veloppement + tests) --- ## 🟡 PRIORITÉ MOYENNE ### 9. Migration complĂšte des Services vers namespace App\Services **Date :** 07/11/2025 **Objectif :** Migrer tous les services vers le namespace `App\Services` pour amĂ©liorer l'organisation du code et l'autoloading. **Contexte :** Lors de l'intĂ©gration de la base batiments, nous avons commencĂ© Ă  migrer certains services vers le namespace `App\Services`. Cette migration partielle crĂ©e des conflits d'autoloading et des incohĂ©rences dans le code. **État actuel :** ✅ **Services AVEC namespace App\Services (10)** : - AddressService.php - BuildingService.php - DepartmentBoundaryService.php - LogService.php - PasswordSecurityService.php - PDFGenerator.php - ReceiptPDFGenerator.php - ReceiptService.php - SimplePDF.php - StripeService.php ❌ **Services SANS namespace (8 Ă  migrer)** : - ApiService.php - BackupEncryptionService.php - EmailTemplates.php - EventLogService.php - ExportService.php - FileService.php - MigrationService.php - OperationDataService.php ✅ **Services Security (tous OK)** : - Tous les 5 services dans `App\Services\Security` ont dĂ©jĂ  le namespace **Plan de migration :** #### Étape 1 : PrĂ©parer l'autoloading PSR-4 ```json // composer.json - remplacer classmap par psr-4 "autoload": { "psr-4": { "App\\": "src/" } } ``` Puis exĂ©cuter sur DVA, RCA et PROD : ```bash composer dump-autoload -o ``` #### Étape 2 : Migrer les 8 services restants Pour chaque service : 1. **Ajouter le namespace** au dĂ©but du fichier ```php namespace App\Services; ``` 2. **Ajouter les imports** pour les classes globales ```php use PDO; use Exception; // etc. ``` 3. **VĂ©rifier les dĂ©pendances** : si le service utilise d'autres services, s'assurer que les imports sont corrects 4. **Tester** : s'assurer que le service fonctionne correctement #### Étape 3 : Mettre Ă  jour les fichiers qui utilisent ces services Pour chaque fichier utilisant les services migrĂ©s : - Ajouter `use App\Services\NomDuService;` si le fichier a un namespace diffĂ©rent - Ou utiliser le nom court si dans le mĂȘme namespace #### Ordre de migration recommandĂ© : 1. **EmailTemplates.php** (utilisĂ© par ApiService, ReceiptService) 2. **ApiService.php** (utilisĂ© par beaucoup de contrĂŽleurs) 3. **FileService.php** (utilisĂ© par ExportService, ReceiptService) 4. **ExportService.php** (autonome) 5. **OperationDataService.php** (autonome) 6. **BackupEncryptionService.php** (autonome) 7. **EventLogService.php** (autonome) 8. **MigrationService.php** (scripts uniquement) **Fichiers Ă  vĂ©rifier aprĂšs migration :** Controllers utilisant ces services : - `src/Controllers/*.php` - vĂ©rifier tous les imports - `scripts/cron/*.php` - vĂ©rifier les scripts cron - `scripts/php/*.php` - vĂ©rifier les scripts de migration **Tests Ă  effectuer :** 1. **Sur DVA** : - Tester tous les endpoints principaux - VĂ©rifier les logs (pas d'erreur Class not found) - Tester l'envoi d'emails (ApiService) - Tester les exports (ExportService) - Tester les reçus PDF (ReceiptService + EmailTemplates) 2. **Sur RCA** : - Tests de non-rĂ©gression complets - Validation par un utilisateur test 3. **Sur PROD** : - DĂ©ploiement progressif - Monitoring des logs pendant 24h **Risques et mitigation :** ⚠ **Risque** : Erreur "Class not found" si l'autoloading ne fonctionne pas ✅ **Mitigation** : Tester d'abord sur DVA avec rollback rapide possible ⚠ **Risque** : Services utilisĂ©s par scripts cron qui cassent ✅ **Mitigation** : Lister tous les scripts et les tester individuellement ⚠ **Risque** : Cache d'autoload pas rĂ©gĂ©nĂ©rĂ© ✅ **Mitigation** : Forcer `composer dump-autoload -o` sur chaque environnement **Impact sur les environnements :** - **DVA** : Test complet de la migration - **RCA** : Validation avant production - **PROD** : DĂ©ploiement final **PrĂ©requis :** - ✅ AccĂšs SSH aux 3 environnements (DVA, RCA, PROD) - ✅ Composer installĂ© sur chaque environnement - ✅ Backup de la version actuelle du code --- **Statut :** 🔄 EN COURS - Migration partielle effectuĂ©e (10/18 services) **Date de crĂ©ation :** 07/11/2025 **DurĂ©e estimĂ©e :** 1 jour (migration + tests) **PrioritĂ© :** MOYENNE - Bloque le dĂ©ploiement de l'intĂ©gration batiments --- ### 🟱 PRIORITÉ BASSE #### 10. Documentation API - GĂ©nĂ©ration automatique OpenAPI/Swagger - Documentation interactive - Exemples de code pour chaque endpoint #### 11. Tests automatisĂ©s - Tests unitaires pour les services critiques - Tests d'intĂ©gration pour les endpoints - Tests de charge --- ## 📝 Notes - Les tĂąches marquĂ©es 🔮 doivent ĂȘtre traitĂ©es en prioritĂ© - Chaque tĂąche implĂ©mentĂ©e doit ĂȘtre documentĂ©e - PrĂ©voir des tests pour chaque nouvelle fonctionnalitĂ© --- **DerniĂšre mise Ă  jour :** 07/11/2025