Files
geo/app/docs/TODO-APP.md
pierre b6584c83fa feat: Version 3.3.4 - Nouvelle architecture pages, optimisations widgets Flutter et API
- Mise à jour VERSION vers 3.3.4
- Optimisations et révisions architecture API (deploy-api.sh, scripts de migration)
- Ajout documentation Stripe Tap to Pay complète
- Migration vers polices Inter Variable pour Flutter
- Optimisations build Android et nettoyage fichiers temporaires
- Amélioration système de déploiement avec gestion backups
- Ajout scripts CRON et migrations base de données

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-05 20:11:15 +02:00

30 KiB

TODO-APP.md

📋 Liste des tâches à effectuer sur l'application GEOSECTOR

🧪 Tests unitaires Flutter à implémenter

📁 Structure des tests

Le dossier /test doit contenir des tests unitaires pour valider le comportement de l'application.

📝 Tests prioritaires à créer

1. Tests des Repositories (test/repositories/)

test/repositories/membre_repository_test.dart

  • Test de création d'un membre avec succès
  • Test de création avec email déjà existant (409)
  • Test de mise à jour d'un membre
  • Test de suppression d'un membre
  • Test de gestion des erreurs réseau
  • Test du cache Hive local

test/repositories/user_repository_test.dart

  • Test de connexion réussie
  • Test de connexion avec mauvais identifiants
  • Test de mise à jour du profil utilisateur
  • Test de synchronisation des données
  • Test de gestion de session

test/repositories/operation_repository_test.dart

  • Test de création d'opération
  • Test d'activation/désactivation
  • Test de récupération des opérations par amicale
  • Test de suppression avec transfert de passages

2. Tests des Services (test/services/)

test/services/api_service_test.dart

  • Test de détection d'environnement (DEV/REC/PROD)
  • Test de gestion des sessions
  • Test de conversion DioException vers ApiException
  • Test des différents codes d'erreur HTTP
  • Test du retry automatique

test/services/logger_service_test.dart

  • Test de désactivation des logs en PROD
  • Test des différents niveaux de log
  • Test du formatage des messages

test/services/hive_service_test.dart

  • Test d'initialisation des boîtes Hive
  • Test de sauvegarde et récupération
  • Test de suppression de données
  • Test de migration de schéma

3. Tests des Models (test/models/)

test/models/membre_model_test.dart

  • Test de sérialisation JSON
  • Test de désérialisation JSON
  • Test de conversion vers UserModel
  • Test des valeurs par défaut

test/models/amicale_model_test.dart

  • Test de sérialisation/désérialisation
  • Test des nouveaux champs booléens (chk_mdp_manuel, chk_username_manuel)
  • Test de validation des données

4. Tests des Widgets (test/widgets/)

test/widgets/user_form_test.dart

  • Test d'affichage conditionnel des champs (username/password)
  • Test de validation du mot de passe (regex)
  • Test de validation de l'username (pas d'espaces)
  • Test de génération automatique d'username
  • Test du mode création vs modification

test/widgets/membre_table_widget_test.dart

  • Test d'affichage responsive (mobile vs web)
  • Test de masquage des colonnes sur mobile
  • Test des icônes de statut
  • Test du tri et filtrage

5. Tests d'intégration (test/integration/)

test/integration/auth_flow_test.dart

  • Test du flow complet de connexion
  • Test de persistance de session
  • Test de déconnexion
  • Test de redirection après expiration

test/integration/membre_management_test.dart

  • Test de création complète d'un membre
  • Test de modification avec validation
  • Test de suppression avec transfert
  • Test de gestion des erreurs

🛠️ Configuration des tests

Fichier test/test_helpers.dart

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:dio/dio.dart';
import 'package:hive_flutter/hive_flutter.dart';

// Mocks nécessaires
class MockDio extends Mock implements Dio {}
class MockBox<t> extends Mock implements Box<t> {}
class MockApiService extends Mock implements ApiService {}

// Helper pour initialiser Hive en tests
Future<void> setupTestHive() async {
  await Hive.initFlutter();
  // Initialiser les boîtes de test
}

// Helper pour nettoyer après les tests
Future<void> tearDownTestHive() async {
  await Hive.deleteFromDisk();
}

// Helper pour créer des données de test
class TestDataFactory {
  static UserModel createTestUser() {
    return UserModel(
      id: 1,
      username: 'test_user',
      email: 'test@example.com',
      // ...
    );
  }

  static MembreModel createTestMembre() {
    return MembreModel(
      id: 1,
      fkEntite: 100,
      name: 'Test',
      firstName: 'User',
      // ...
    );
  }
}

📋 Commandes de test

# Lancer tous les tests
flutter test

# Lancer un test spécifique
flutter test test/repositories/membre_repository_test.dart

# Lancer avec coverage
flutter test --coverage

# Générer un rapport HTML de couverture
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.html

🎯 Objectifs de couverture

  • Repositories : 80% minimum
  • Services : 90% minimum
  • Models : 95% minimum
  • Widgets critiques : 70% minimum
  • Couverture globale : 75% minimum

📚 Dépendances de test à ajouter

Dans pubspec.yaml :

dev_dependencies:
  flutter_test:
    sdk: flutter
  mockito: ^5.4.4
  build_runner: ^2.4.8
  test: ^1.25.2
  flutter_lints: ^4.0.0
  coverage: ^1.7.2

🔄 Intégration CI/CD

Ajouter dans le pipeline CI :

test:
  stage: test
  script:
    - flutter test --coverage
    - genhtml coverage/lcov.info -o coverage/html
  artifacts:
    paths:
      - coverage/
  coverage: '/lines\.*: \d+\.\d+\%/'

📝 Bonnes pratiques

  1. Isolation : Chaque test doit être indépendant
  2. Mocking : Utiliser des mocks pour les dépendances externes
  3. Nommage : Utiliser des noms descriptifs (test_should_xxx_when_yyy)
  4. AAA : Suivre le pattern Arrange-Act-Assert
  5. Edge cases : Tester les cas limites et erreurs
  6. Performance : Les tests unitaires doivent être rapides (<100ms)

💬 Module Chat en ligne GEOSECTOR

📋 Vue d'ensemble

Le module chat est partiellement implémenté avec une architecture MQTT pour les notifications en temps réel. Il nécessite des développements supplémentaires pour être pleinement fonctionnel.

🏗️ Architecture existante

lib/chat/
├── models/                        # ✅ Modèles créés avec Hive
│   ├── conversation_model.dart    # Conversations (one-to-one, groupe, annonce)
│   ├── message_model.dart         # Messages avec pièces jointes
│   ├── participant_model.dart     # Participants aux conversations
│   └── audience_target_model.dart # Cibles pour les annonces
├── repositories/                  # ⚠️ À compléter
│   └── chat_repository.dart       # Logique métier du chat
├── services/                      # ⚠️ Partiellement implémenté
│   ├── chat_api_service.dart      # Communication avec l'API
│   ├── offline_queue_service.dart # File d'attente hors ligne
│   └── notifications/             # 🔧 MQTT configuré
│       ├── mqtt_notification_service.dart
│       └── mqtt_config.dart
├── widgets/                       # ⚠️ À implémenter
│   ├── chat_screen.dart          # Écran principal du chat
│   ├── conversations_list.dart   # Liste des conversations
│   ├── message_bubble.dart       # Bulles de messages
│   └── chat_input.dart           # Zone de saisie
└── pages/                        # ⚠️ À compléter
    └── chat_page.dart            # Page principale

📝 Tâches de développement Chat

1. Finalisation des modèles Hive

  • Compléter les adaptateurs Hive pour tous les modèles
  • Ajouter la gestion des pièces jointes (images, documents)
  • Implémenter le modèle AnonymousUserModel pour les utilisateurs temporaires
  • Ajouter les timestamps et statuts de lecture

2. Repository ChatRepository

  • Implémenter createConversation() avec participants
  • Implémenter sendMessage() avec queue hors ligne
  • Implémenter getConversations() avec pagination
  • Implémenter getMessages() avec lazy loading
  • Ajouter la gestion des participants (ajout/suppression)
  • Implémenter les annonces ciblées par groupe

3. Services

  • Compléter ChatApiService avec endpoints REST
  • Implémenter la synchronisation bidirectionnelle
  • Configurer OfflineQueueService pour messages en attente
  • Implémenter le retry automatique avec exponential backoff
  • Ajouter la compression des images avant envoi

4. Notifications MQTT

  • Installer et configurer Mosquitto dans le container Incus
  • Configurer SSL/TLS pour MQTT (port 8883)
  • Implémenter l'authentification par token JWT
  • Créer les topics par utilisateur/groupe/conversation
  • Implémenter le système de présence (online/offline/typing)
  • Ajouter les ACLs pour sécuriser les topics

5. Interface utilisateur

  • Créer ChatScreen avec liste de messages
  • Implémenter ConversationsList avec badges non-lus
  • Designer MessageBubble (texte, images, documents)
  • Créer ChatInput avec:
    • Saisie de texte avec emoji picker
    • Bouton d'envoi de fichiers
    • Indicateur "en train d'écrire"
    • Enregistrement vocal (optionnel)
  • Ajouter les animations (apparition messages, typing indicator)
  • Implémenter le swipe pour répondre
  • Ajouter la recherche dans les conversations

6. Fonctionnalités avancées

  • Notifications push locales via flutter_local_notifications
  • Chiffrement end-to-end des messages sensibles
  • Réactions aux messages (emojis)
  • Messages éphémères avec auto-suppression
  • Partage de localisation en temps réel
  • Appels audio/vidéo via WebRTC (phase 2)

🔧 Configuration backend requise

Base de données

-- Tables à créer (voir chat_tables.sql)
CREATE TABLE chat_conversations (
  id INT PRIMARY KEY AUTO_INCREMENT,
  type ENUM('one_to_one', 'group', 'announcement'),
  created_by INT,
  created_at TIMESTAMP,
  updated_at TIMESTAMP
);

CREATE TABLE chat_messages (
  id INT PRIMARY KEY AUTO_INCREMENT,
  conversation_id INT,
  sender_id INT,
  content TEXT,
  type ENUM('text', 'image', 'file', 'location'),
  status ENUM('sent', 'delivered', 'read'),
  created_at TIMESTAMP
);

CREATE TABLE chat_participants (
  conversation_id INT,
  user_id INT,
  role ENUM('admin', 'member'),
  joined_at TIMESTAMP,
  last_read_message_id INT
);

Configuration MQTT

# Installation Mosquitto
apt-get install mosquitto mosquitto-clients

# Configuration /etc/mosquitto/mosquitto.conf
listener 1883
listener 8883
cafile /etc/mosquitto/ca.crt
certfile /etc/mosquitto/server.crt
keyfile /etc/mosquitto/server.key
allow_anonymous false
password_file /etc/mosquitto/passwd

📦 Dépendances à ajouter

dependencies:
  # MQTT et notifications
  mqtt5_client: ^4.0.0
  flutter_local_notifications: ^17.0.0

  # UI et UX
  emoji_picker_flutter: ^2.0.0
  cached_network_image: ^3.3.1
  photo_view: ^0.14.0
  file_picker: ^6.1.1

  # Utilitaires
  path_provider: ^2.1.2
  image_picker: ^1.0.7
  image: ^4.1.7  # Pour compression
  intl: ^0.19.0  # Pour formatage dates

🧪 Tests à implémenter

  • Tests unitaires des repositories
  • Tests d'intégration MQTT
  • Tests de performance (1000+ messages)
  • Tests hors ligne/online
  • Tests de sécurité (injection, XSS)

💳 Module de paiement Stripe

📋 Vue d'ensemble

Intégration de Stripe pour permettre aux amicales ayant activé chk_stripe d'accepter les paiements par carte bancaire avec une commission de 1.4%.

🎯 Objectifs

  1. Permettre le paiement en ligne lors des passages
  2. Gérer les comptes Stripe des amicales
  3. Suivre les transactions et commissions
  4. Offrir une expérience de paiement fluide

📝 Tâches de développement Stripe

1. Configuration initiale Stripe

  • Créer un compte Stripe Connect Platform
  • Configurer les webhooks Stripe
  • Mettre en place l'environnement de test (sandbox)
  • Configurer les clés API (publishable/secret)
  • Implémenter la gestion sécurisée des clés

2. Onboarding des amicales

  • Créer un workflow d'inscription Stripe pour les amicales
  • Implémenter Stripe Connect Onboarding
  • Gérer le KYC (Know Your Customer) requis par Stripe
  • Stocker de manière sécurisée le stripe_account_id
  • Créer une page de statut du compte Stripe
  • Gérer les documents requis (RIB, statuts, etc.)

3. Modèles de données

  • Créer StripeAccountModel pour les comptes Connect
  • Créer PaymentIntentModel pour les intentions de paiement
  • Créer TransactionModel pour l'historique
  • Ajouter les champs Stripe dans PassageModel
  • Implémenter la table des commissions

4. Service StripeService

class StripeService {
  // Compte Connect
  Future<string> createConnectAccount(AmicaleModel amicale);
  Future<void> updateAccountStatus(String accountId);
  Future<string> createAccountLink(String accountId);

  // Paiements
  Future<paymentintent> createPaymentIntent({
    required double amount,
    required String currency,
    required String connectedAccountId,
  });

  Future<void> confirmPayment(String paymentIntentId);
  Future<void> refundPayment(String paymentIntentId);

  // Commissions
  double calculateApplicationFee(double amount); // 1.4%
}

5. Interface de paiement dans PassageForm

  • Détecter si l'amicale accepte Stripe (chk_stripe)
  • Ajouter l'option "Paiement par carte" dans le dropdown
  • Intégrer Stripe Elements pour la saisie de carte
  • Implémenter le flow de paiement 3D Secure
  • Gérer les erreurs de paiement
  • Afficher le reçu de paiement
  • Permettre l'envoi du reçu par email

6. Widget StripePaymentSheet

class StripePaymentSheet extends StatefulWidget {
  final double amount;
  final String currency;
  final AmicaleModel amicale;
  final Function(String) onSuccess;
  final Function(String) onError;

  // UI avec:
  // - Montant à payer
  // - Formulaire de carte (Stripe Elements)
  // - Bouton de validation
  // - Indicateur de traitement
  // - Gestion 3D Secure
}

7. Tableau de bord financier

  • Page de suivi des transactions Stripe
  • Graphiques des paiements par période
  • Export des transactions (CSV/Excel)
  • Calcul automatique des commissions
  • Rapprochement bancaire
  • Dashboard temps réel des paiements

8. Webhooks Stripe

// Backend PHP pour gérer les webhooks
class StripeWebhookHandler {
  // Events à gérer:
  - payment_intent.succeeded
  - payment_intent.failed
  - account.updated
  - payout.created
  - refund.created
}

9. Sécurité

  • Chiffrement des données sensibles
  • Validation PCI DSS
  • Audit trail des transactions
  • Détection de fraude
  • Rate limiting sur les API
  • Tokenisation des cartes

10. Tests et conformité

  • Tests avec cartes de test Stripe
  • Tests des cas d'erreur (carte refusée, etc.)
  • Tests 3D Secure
  • Tests de performance
  • Conformité RGPD pour les données de paiement
  • Documentation utilisateur

🔧 Configuration backend requise

Tables base de données

CREATE TABLE stripe_accounts (
  id INT PRIMARY KEY AUTO_INCREMENT,
  fk_entite INT NOT NULL,
  stripe_account_id VARCHAR(255) ENCRYPTED,
  status ENUM('pending', 'active', 'restricted'),
  charges_enabled BOOLEAN DEFAULT FALSE,
  payouts_enabled BOOLEAN DEFAULT FALSE,
  created_at TIMESTAMP,
  updated_at TIMESTAMP
);

CREATE TABLE stripe_transactions (
  id INT PRIMARY KEY AUTO_INCREMENT,
  fk_passage INT,
  stripe_payment_intent_id VARCHAR(255),
  amount DECIMAL(10,2),
  currency VARCHAR(3),
  status VARCHAR(50),
  application_fee DECIMAL(10,2),
  created_at TIMESTAMP
);

Variables d'environnement

STRIPE_PUBLISHABLE_KEY=pk_test_xxx
STRIPE_SECRET_KEY=sk_test_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
STRIPE_APPLICATION_FEE_PERCENT=1.4

📦 Dépendances Stripe

dependencies:
  # Stripe
  flutter_stripe: ^10.1.1

  # Sécurité
  flutter_secure_storage: ^9.0.0

  # UI
  shimmer: ^3.0.0  # Loading states
  lottie: ^3.1.0   # Animations succès/échec

🚀 Roadmap d'implémentation

Phase 1 (2 semaines)

  • Configuration Stripe Connect
  • Onboarding basique des amicales
  • Tests en sandbox

Phase 2 (3 semaines)

  • Intégration dans PassageForm
  • Gestion des paiements simples
  • Webhooks essentiels

Phase 3 (2 semaines)

  • Dashboard financier
  • Export et rapports
  • Tests complets

Phase 4 (1 semaine)

  • Mise en production
  • Monitoring
  • Documentation

💰 Estimation des coûts

  • Stripe Connect : 0.25€ par compte actif/mois
  • Transactions : 1.4% + 0.25€ par transaction
  • Commission application : 1.4% (reversée à GEOSECTOR)
  • Coût net pour l'amicale : ~2.8% + 0.25€ par transaction

Date de création : 2025-08-07
Auteur : Architecture Team
Version : 1.3.0

🧪 Recette - Points à corriger/améliorer

📊 Gestion des membres et statistiques

Affichage et listes

  • Ajouter la liste des membres avec leurs statistiques comme dans l'ancienne version
  • Historique avec choix des membres - Permettre la sélection du membre dans l'historique
  • Membres cochés en haut - Dans la modification de secteur, afficher les membres sélectionnés en priorité
  • Filtres sur la liste des membres - Ajouter des filtres dans la page "Amicale et membres"

Modification des secteurs

  • Bug : Changement de membre non pris en compte - CORRIGÉ - La modification n'est pas sauvegardée lors du changement de membre sur un secteur

📝 Formulaires et saisie

Passage

  • Nom obligatoire seulement si email - CORRIGÉ - Le nom n'est obligatoire que si un email est renseigné

Membre

  • Email non obligatoire - Si identifiant et mot de passe sont saisis manuellement, l'email ne doit pas être obligatoire
  • Helpers lisibles - Améliorer les textes d'aide dans la fiche membre
  • Modification de l'identifiant - Permettre la modification de l'identifiant utilisateur
  • Bug mot de passe généré - Le mot de passe généré contient des espaces, ce qui pose problème

💬 Module Chat

  • Bouton "Envoyer un message" - Améliorer la visibilité
  • Police plus grasse - Augmenter l'épaisseur de la police pour une meilleure lisibilité

🗺️ Carte et géolocalisation

Configuration carte

  • Zoom maximal - Définir et implémenter une limite de zoom maximum
  • Carte type Snapchat - Étudier l'utilisation d'un style de carte similaire à Snapchat

Mode terrain

  • Revoir la géolocalisation - Améliorer la précision et le fonctionnement de la géolocalisation en mode terrain

📅 Historique et dates

  • Dates de début et fin - Ajouter des sélecteurs de dates de début et fin dans l'historique

🔐 Authentification et connexion

Connexion

  • Admin uniquement en web - Restreindre l'accès admin au web uniquement (pas sur mobile)
  • Bug F5 - Corriger la déconnexion lors du rafraîchissement de la page (F5)
  • Connexion multi-rôles - En connexion utilisateur, permettre de se connecter soit comme admin, soit comme membre

Inscription

  • Double envoi email - Envoyer 2 emails lors de l'inscription : un pour l'identifiant, un pour le mot de passe, avec informations complémentaires

💳 Module Stripe

  • Intégration dans le formulaire de passage - Créer la gestion du paiement en ligne au niveau du formulaire passage si l'amicale a un compte Stripe actif
  • Mode hors connexion - Étudier les possibilités de paiement Stripe en mode hors ligne

👑 Mode Super Admin

Gestion des amicales

  • Bug suppression - Corriger le ralentissement après 3 suppressions d'amicales (problème de purge)
  • Filtres sur les amicales - Ajouter des filtres de recherche/tri sur la liste des amicales
  • Mode démo - Implémenter un mode démo pour les présentations
  • Statuts actifs/inactifs - Distinguer les amicales actives (qui ont réglé) des autres

Gestion des opérations

  • Bug suppression opération active - Si suppression de l'opération active, la précédente doit redevenir active automatiquement

Deadline

  • ⚠️ DATE BUTOIR : 08/10 pour le Congrès

🌐 Gestion du cache Flutter Web

📋 Vue d'ensemble

Stratégie de gestion du cache pour l'application Flutter Web selon l'environnement (DEV/REC/PROD).

🎯 Objectifs

  • DEV/REC : Aucun cache - rechargement forcé à chaque visite pour voir immédiatement les changements
  • PROD : Cache intelligent avec versioning pour optimiser les performances

📝 Solution par environnement

1. Environnements DEV et RECETTE

Stratégie : No-Cache radical

Configuration serveur web (Apache)

Créer/modifier le fichier .htaccess dans le dossier racine web :

# .htaccess pour DEV/REC - Aucun cache
<IfModule mod_headers.c>
    # Désactiver complètement le cache
    Header set Cache-Control "no-cache, no-store, must-revalidate, private"
    Header set Pragma "no-cache"
    Header set Expires "0"

    # Forcer le rechargement pour tous les assets
    <FilesMatch "\.(js|css|html|json|wasm|ttf|otf|woff|woff2|ico|png|jpg|jpeg|gif|svg)$">
        Header set Cache-Control "no-cache, no-store, must-revalidate"
        Header set Pragma "no-cache"
        Header set Expires "0"
    </FilesMatch>
</IfModule>

# Désactiver le cache du navigateur via ETags
FileETag None
<IfModule mod_headers.c>
    Header unset ETag
</IfModule>
Modification du service worker

Dans web/flutter_service_worker.js, ajouter au début :

// DEV/REC: Forcer le rechargement complet
if (location.hostname === 'dapp.geosector.fr' || location.hostname === 'rapp.geosector.fr') {
    // Nettoyer tous les caches existants
    caches.keys().then(function(names) {
        for (let name of names) caches.delete(name);
    });

    // Bypass le service worker pour toutes les requêtes
    self.addEventListener('fetch', function(event) {
        event.respondWith(fetch(event.request));
    });

    // Désactiver le cache complètement
    return;
}
Headers Meta HTML

Dans web/index.html, ajouter dans le <head> :

<!-- DEV/REC: No-cache meta tags -->
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">

<!-- Forcer le rechargement des ressources avec timestamp -->
<script>
    // Ajouter un timestamp unique à toutes les ressources
    if (location.hostname === 'dapp.geosector.fr' || location.hostname === 'rapp.geosector.fr') {
        const timestamp = new Date().getTime();
        window.flutterConfiguration = {
            assetBase: './?t=' + timestamp,
            canvasKitBaseUrl: 'canvaskit/?t=' + timestamp
        };
    }
</script>

2. Environnement PRODUCTION

Stratégie : Cache intelligent avec versioning

Configuration serveur web (Apache)
# .htaccess pour PRODUCTION - Cache intelligent
<IfModule mod_headers.c>
    # Cache par défaut modéré pour HTML
    Header set Cache-Control "public, max-age=3600, must-revalidate"

    # Cache long pour les assets statiques versionnés
    <FilesMatch "\.(js|css|wasm)$">
        Header set Cache-Control "public, max-age=31536000, immutable"
    </FilesMatch>

    # Cache modéré pour les images et fonts
    <FilesMatch "\.(ttf|otf|woff|woff2|ico|png|jpg|jpeg|gif|svg)$">
        Header set Cache-Control "public, max-age=604800"
    </FilesMatch>

    # Pas de cache pour le service worker et manifeste
    <FilesMatch "(flutter_service_worker\.js|manifest\.json)$">
        Header set Cache-Control "no-cache, no-store, must-revalidate"
    </FilesMatch>
</IfModule>

# Activer ETags pour validation du cache
FileETag MTime Size
Script de build avec versioning

Créer build_web.sh :

#!/bin/bash
# Script de build pour production avec versioning automatique

# Générer un hash de version basé sur le timestamp
VERSION=$(date +%Y%m%d%H%M%S)
COMMIT_HASH=$(git rev-parse --short HEAD 2>/dev/null || echo "no-git")

# Build Flutter
flutter build web --release --dart-define=APP_VERSION=$VERSION

# Modifier index.html pour inclure la version
sed -i "s/main.dart.js/main.dart.js?v=$VERSION/g" build/web/index.html
sed -i "s/flutter.js/flutter.js?v=$VERSION/g" build/web/index.html

# Ajouter version dans le service worker
echo "const APP_VERSION = '$VERSION-$COMMIT_HASH';" | cat - build/web/flutter_service_worker.js > temp && mv temp build/web/flutter_service_worker.js

# Créer un fichier version.json
echo "{\"version\":\"$VERSION\",\"build\":\"$COMMIT_HASH\",\"date\":\"$(date -Iseconds)\"}" > build/web/version.json

echo "Build completed with version: $VERSION-$COMMIT_HASH"
Service worker intelligent

Modifier web/flutter_service_worker.js pour production :

// PRODUCTION: Cache intelligent avec versioning
const CACHE_VERSION = 'v1-' + APP_VERSION; // APP_VERSION injecté par le build
const RUNTIME = 'runtime';

// Installation : mettre en cache les ressources essentielles
self.addEventListener('install', (event) => {
    event.waitUntil(
        caches.open(CACHE_VERSION).then((cache) => {
            return cache.addAll([
                '/',
                'main.dart.js',
                'flutter.js',
                'manifest.json'
            ]);
        })
    );
    self.skipWaiting();
});

// Activation : nettoyer les vieux caches
self.addEventListener('activate', (event) => {
    event.waitUntil(
        caches.keys().then((cacheNames) => {
            return Promise.all(
                cacheNames.map((cacheName) => {
                    if (cacheName !== CACHE_VERSION && cacheName !== RUNTIME) {
                        return caches.delete(cacheName);
                    }
                })
            );
        })
    );
    self.clients.claim();
});

// Fetch : stratégie cache-first pour assets, network-first pour API
self.addEventListener('fetch', (event) => {
    const url = new URL(event.request.url);

    // Network-first pour API et données dynamiques
    if (url.pathname.startsWith('/api/')) {
        event.respondWith(
            fetch(event.request)
                .then((response) => {
                    const responseClone = response.clone();
                    caches.open(RUNTIME).then((cache) => {
                        cache.put(event.request, responseClone);
                    });
                    return response;
                })
                .catch(() => caches.match(event.request))
        );
        return;
    }

    // Cache-first pour assets statiques
    event.respondWith(
        caches.match(event.request).then((cachedResponse) => {
            return cachedResponse || fetch(event.request).then((response) => {
                return caches.open(RUNTIME).then((cache) => {
                    cache.put(event.request, response.clone());
                    return response;
                });
            });
        })
    );
});

🔧 Détection automatique de nouvelle version

Ajouter dans l'application Flutter :

// lib/services/version_check_service.dart
class VersionCheckService {
  static const Duration _checkInterval = Duration(minutes: 5);
  Timer? _timer;
  String? _currentVersion;

  void startVersionCheck() {
    if (!kIsWeb) return;

    // Vérifier la version toutes les 5 minutes
    _timer = Timer.periodic(_checkInterval, (_) => _checkVersion());

    // Vérification initiale
    _checkVersion();
  }

  Future<void> _checkVersion() async {
    try {
      final response = await http.get(
        Uri.parse('/version.json?t=${DateTime.now().millisecondsSinceEpoch}')
      );

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        final newVersion = data['version'];

        if (_currentVersion != null && _currentVersion != newVersion) {
          // Nouvelle version détectée
          _showUpdateDialog();
        }
        _currentVersion = newVersion;
      }
    } catch (e) {
      print('Erreur vérification version: $e');
    }
  }

  void _showUpdateDialog() {
    // Afficher une notification ou dialog
    showDialog(
      context: navigatorKey.currentContext!,
      barrierDismissible: false,
      builder: (context) => AlertDialog(
        title: Text('Nouvelle version disponible'),
        content: Text('Une nouvelle version de l\'application est disponible. '
                     'L\'application va se recharger.'),
        actions: [
          TextButton(
            onPressed: () {
              // Forcer le rechargement complet
              html.window.location.reload();
            },
            child: Text('Recharger maintenant'),
          ),
        ],
      ),
    );
  }

  void dispose() {
    _timer?.cancel();
  }
}

📋 Commandes de déploiement

# DEV/REC - Déploiement sans cache
flutter build web --release
rsync -av --delete build/web/ user@server:/var/www/dapp/

# PRODUCTION - Déploiement avec versioning
./build_web.sh
rsync -av --delete build/web/ user@server:/var/www/app/

🧪 Validation du no-cache

Pour vérifier que le cache est désactivé en DEV/REC :

  1. Ouvrir Chrome DevTools → Network
  2. Vérifier les headers de réponse :
    • Cache-Control: no-cache, no-store, must-revalidate
    • Pragma: no-cache
    • Expires: 0
  3. Recharger la page : tous les fichiers doivent être rechargés (status 200, pas 304)
  4. Vérifier dans Application → Storage → Clear site data

📝 Notes importantes

  • DEV/REC : Les utilisateurs verront toujours la dernière version immédiatement
  • PROD : Les utilisateurs bénéficient d'un cache optimisé avec détection automatique des mises à jour
  • Service Worker : Géré différemment selon l'environnement
  • Versioning : Utilise timestamp + hash git pour identifier uniques les builds
  • Fallback : En cas d'échec réseau en PROD, utilise le cache pour maintenir l'app fonctionnelle

Date d'ajout : 2025-09-23 Auteur : Solution de gestion du cache Version : 1.0.0