Skip to content

# [Bug] Première reconnexion après déconnexion : faux message « Erreur réseau — serveur inaccessible » #13

Description

@sirius911

Contexte

Lors d'une installation de test de SCRIBE, nous avons rencontré un problème
systématique lors des reconnexions utilisateur.

L'instance testée est déployée :

  • dans un conteneur Podman ;
  • sur un serveur Oracle Linux ;
  • avec les données applicatives persistées hors du conteneur ;
  • SCRIBE version 2.5.0 / build v2500 ;
  • commit testé :
    e96c9f5d844d3ad697ee20a3b4254aa2e97c24eb.

Le problème a été reproduit avec plusieurs comptes utilisateurs et depuis
un navigateur Chromium / Microsoft Edge.

Description du problème

Après une première connexion réussie, si l'utilisateur se déconnecte puis
essaie de se reconnecter sans recharger complètement la page, le premier
clic sur le bouton de connexion affiche :

Erreur réseau — serveur inaccessible

Pourtant le serveur reste accessible et l'API fonctionne normalement.

Un deuxième clic sur le bouton de connexion permet immédiatement d'entrer
dans l'application.

Un rechargement complet de la page avec Ctrl+F5 avant la reconnexion fait
également disparaître le problème.

Étapes pour reproduire

  1. Ouvrir SCRIBE.
  2. Se connecter avec un compte valide.
  3. Utiliser le bouton de déconnexion de SCRIBE.
  4. Sans recharger la page, saisir de nouveau des identifiants valides.
  5. Cliquer une seule fois sur le bouton de connexion.

Résultat observé

SCRIBE affiche :

Erreur réseau — serveur inaccessible

La connexion ne semble pas aboutir.

Un deuxième clic sur le bouton de connexion fonctionne immédiatement.

Résultat attendu

Après une déconnexion, l'utilisateur doit pouvoir se reconnecter dès le
premier clic, sans avoir besoin de recharger la page.

Analyse

Le problème ne semble pas provenir du réseau ni du backend d'authentification.

L'authentification HTTP réussit, mais une erreur survient ensuite pendant
la réinitialisation de l'interface frontend.

1. Les cartes Leaflet restent initialisées après la déconnexion

doLogout() remet notamment :

_appInitDone = false;

mais les instances Leaflet map et mapSoins ne sont pas détruites.

Lors de la reconnexion, initAfterLogin() appelle de nouveau initMap().

initMap() exécute alors :

map = L.map('map', ...)

sur un conteneur DOM ayant déjà été initialisé par Leaflet lors de la
session précédente.

Cela peut provoquer une exception de type :

Map container is already initialized

Le fait qu'un Ctrl+F5 résolve systématiquement le problème est cohérent
avec cette hypothèse : le rechargement complet détruit l'état JavaScript
et les anciennes instances Leaflet.

2. _appInitDone est positionné trop tôt

Dans la version actuelle :

async function initAfterLogin() {
    if (_appInitDone) return;
    _appInitDone = true;

Le drapeau est donc positionné avant que l'initialisation soit réellement
terminée.

Si initMap() ou une autre étape déclenche une exception, l'application
reste malgré tout considérée comme initialisée.

Cela explique également pourquoi le deuxième clic fonctionne :
initAfterLogin() est alors immédiatement ignoré puisque
_appInitDone === true.

3. Le message « Erreur réseau » masque l'erreur JavaScript réelle

doLogin() englobe actuellement dans le même try/catch :

  • l'appel fetch('/api/v1/auth/login', ...);
  • l'enregistrement du token ;
  • la mise à jour de l'état utilisateur ;
  • initAfterLogin() ;
  • l'initialisation de l'interface.

Ainsi, une exception JavaScript produite après une authentification réussie
arrive dans :

catch(e) {
    errEl.textContent = 'Erreur réseau — serveur inaccessible';
}

L'utilisateur reçoit donc un diagnostic réseau alors que le serveur est
parfaitement accessible.

Correctif testé

Nous avons appliqué localement le correctif suivant sur
app/static/js/scribe.js.

Nettoyage des cartes avant leur réinitialisation

 function initMap() {
+  // Une reconnexion peut réinitialiser l'interface sans recharger la page.
+  // Détruire proprement une éventuelle instance Leaflet précédente.
+  if (map) {
+    try { map.remove(); } catch(e) {
+      console.warn('initMap: cleanup map', e);
+    }
+    map = null;
+  }
+  if (mapSoins) {
+    try { mapSoins.remove(); } catch(e) {
+      console.warn('initMap: cleanup mapSoins', e);
+    }
+    mapSoins = null;
+  }
+  markers = {};
+
   map = L.map('map', {zoomControl:true}).setView([45.9, 6.1], 10);

Ne positionner _appInitDone qu'après une initialisation réussie

 async function initAfterLogin() {
   if (_appInitDone) return;
-  _appInitDone = true;
+
   // Afficher l'interface d'abord

Puis à la fin de l'initialisation :

   await loadTransfertsEntrants();

+  // L'initialisation complète a réussi.
+  _appInitDone = true;
+
   // Forcer recalcul taille carte après affichage

Nettoyage des cartes lors de la déconnexion

 function doLogout() {
   authToken = null;
   currentUser = null;
   _appInitDone = false;
+
+  // Nettoyer les cartes Leaflet avant une future reconnexion.
+  if (map) {
+    try { map.remove(); } catch(e) {
+      console.warn('logout: cleanup map', e);
+    }
+    map = null;
+  }
+  if (mapSoins) {
+    try { mapSoins.remove(); } catch(e) {
+      console.warn('logout: cleanup mapSoins', e);
+    }
+    mapSoins = null;
+  }
+  markers = {};

Validation du correctif

Après modification :

  1. reconstruction de l'image Podman SCRIBE ;
  2. recréation du conteneur en conservant les volumes de données persistants ;
  3. redémarrage de l'instance ;
  4. contrôle de /health :
{"status":"ok","version":"2.5.0","build":"v2500"}
  1. contrôle de l'intégrité de la base persistante : OK ;
  2. tests successifs :
connexion
→ déconnexion
→ reconnexion sans rechargement de page
→ déconnexion
→ reconnexion sans rechargement de page

La reconnexion fonctionne désormais dès le premier clic.

Le message :

Erreur réseau — serveur inaccessible

ne réapparaît plus.

Le correctif n'a nécessité aucune modification de la base de données ni de
la configuration de l'instance.

Amélioration complémentaire possible

Indépendamment du correctif Leaflet, il serait probablement préférable de
séparer dans doLogin() :

  1. les erreurs liées à l'appel réseau / authentification ;
  2. les erreurs liées à l'initialisation du frontend après authentification.

Une exception dans initAfterLogin() ne devrait pas être présentée à
l'utilisateur comme une indisponibilité du serveur.

Cela faciliterait également le diagnostic d'éventuels futurs problèmes
d'initialisation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions