7. Admin Manager : Régulateur de la Citadelle (v5.25)

Le admin-manager est le cœur opérationnel hors-bande du framework ECHO. Il opère en tant que micro-service Python (Flask) isolé. Cette documentation détaille l'intégralité des fonctions, algorithmes et choix architecturaux du code source.

Architecture : Micro-service Stateless | Runtime : Python 3.11+ | Port : 3001

1. Résilience & Dégradation Gracieuse (Graceful Degradation)

Le script s'initialise avec une série de blocs try...except ImportError. Cela permet au Manager de démarrer même si l'environnement est partiellement dégradé ou incomplet. Des drapeaux booléens gèrent la disponibilité des sous-systèmes :

2. Helpers & Utilitaires Bas Niveau

Plusieurs fonctions de bas niveau assurent la traduction entre le système de fichiers brut et l'interface utilisateur :

human_size(size) :
Convertit itérativement un entier (octets) en format lisible (KB, MB, GB) avec une précision d'une décimale.

get_cpu_model_name() :
Lit directement /proc/cpuinfo au lieu d'invoquer des commandes lourdes, en cherchant la première occurrence de "model name".

get_echo_version() :
Tente de lire le fichier statique /app/ECHO_VERSION injecté au build. Retourne "v?.?" en cas d'échec (Fail-Safe).

3. Vérité Sémantique & Monitoring Holistique

La fonction get_dir_stats(path, filter_ext) parcourt récursivement les dossiers avec os.walk. Elle intègre un bloc try...except autour de os.path.getsize pour ignorer les fichiers verrouillés en cours d'écriture (ex: .db-shm ou journaux actifs).

4. L'Engine d'Élagage Sémantique (LIFECYCLE)

La fonction run_semantic_pruning() prévient l'atrophie et la saturation :

  1. Purge des Orphelins : Connexion SQLite en lecture seule (mode=ro) sur webui.db pour extraire les UIDs valides. Tout dossier dans /data/users/ non référencé et dont la taille du nom dépasse 30 caractères (UUID) est détruit via shutil.rmtree().
  2. Atrophie Temporelle : prune_recursive() utilise un cutoff = time.time() - (days * 86400). Le fichier identity.db est passé en argument de sanctuary_files et est immunisé contre la suppression.
  3. Optimisation Physique (VACUUM) : Chaque base de données de chat subit un db.execute("VACUUM;") avec un timeout de 10.0s pour défragmenter les index.

5. Gestion Dynamique des Configurations

Pour éviter les plantages dus à des fichiers JSON malformés, le Manager utilise un pattern de "Fusion avec dictionnaire par défaut" (Dictionary Merging).

load_settings() / load_maint_config() :
1. Crée une copie locale de DEFAULT_BACKUP_CONFIG (ou maint).
2. Lit le fichier cible (ex: settings.json) via orjson.loads().
3. Applique un c.update(json.load(f)) pour écraser les valeurs par défaut avec les valeurs utilisateur, tout en préservant les clés manquantes.

save_settings(new_s) :
Met à jour le dictionnaire et force l'écriture asynchrone, puis déclenche immédiatement update_backup_schedule() pour recompiler les tâches CRON.

6. Sauvegardes & Atomicité

La fonction perform_backup_task() orchestre l'état Docker via le socket :

# Séquence :
target.stop()  # Figement immédiat du conteneur
subprocess.run(['tar', '-czf', fpath, ...])  # Compression
target.start() # Redémarrage

Logique d'Horodatage

La fonction update_backup_schedule() recalcule dynamiquement le point de départ de la prochaine sauvegarde. Si l'heure cible (ex: "03:00") est déjà passée aujourd'hui (start <= now), elle ajoute un datetime.timedelta(days=1) pour programmer à demain, évitant un déclenchement immédiat non désiré.

7. Matrice des Routes API & Multiplexeur d'Actions

Le serveur expose une API REST Flask robuste. Les actions longues sont détachées dans des threads démons pour ne pas bloquer l'interface Web (Gunicorn/Flask Worker).

8. SSH Stealth & Sécurité

L'Admin Manager utilise paramiko via host.docker.internal.

change_system_password() :
Instancie un terminal virtuel interactif (invoke_shell) car la commande Linux passwd exige un TTY. Elle injecte des délais (time.sleep) entre les envois (ancien mot de passe, nouveau x2) pour pallier la latence du shell hôte, puis parse la réponse à la recherche de la sous-chaîne de succès.

9. Schedulers & Threads Démons

Le bloc d'exécution principal (if __name__ == '__main__':) initialise deux moteurs distincts :