homelab_automation/documentation/REFACTORING_AUDIT.md

19 KiB

Audit et Plan de Refactorisation - app_optimized.py

Date: 2024-12-14
Fichier analysé: app/app_optimized.py
Lignes totales: 7098
Framework: FastAPI + SQLAlchemy async


PHASE 1: AUDIT ET VALIDATION

1.1 Statistiques du Fichier

Métrique Valeur
Lignes de code 7098
Classes 21
Fonctions/Méthodes ~150
Endpoints API ~65
Modèles Pydantic 28
Services 6

1.2 Structure Actuelle

app_optimized.py (7098 lignes)
├── Imports (L1-53)
├── Configuration globale (L55-100)
├── Utilitaires PDF/Markdown (L105-548)
├── Modèles Pydantic (L550-932)
├── Services métier (L937-2893)
│   ├── TaskLogService
│   ├── AdHocHistoryService
│   ├── BootstrapStatusService
│   ├── HostStatusService
│   └── SchedulerService
├── WebSocketManager (L2896-2929)
├── AnsibleService (L2933-3561)
├── Bootstrap SSH (L3564-3932)
├── HybridDB (L3935-4089)
├── Endpoints API (L4092-7023)
│   ├── Help/Documentation
│   ├── Hosts CRUD
│   ├── Groups management
│   ├── Tasks CRUD
│   ├── Logs CRUD
│   ├── Ansible execution
│   ├── Playbooks CRUD
│   ├── Bootstrap
│   ├── Ad-hoc history
│   ├── Health checks
│   ├── Schedules CRUD
│   └── Notifications
└── Startup/Shutdown events (L7025-7098)

1.3 Problèmes Identifiés

🔴 CRITIQUES (Bloquants)

ID Problème Lignes Impact
C1 God Object Anti-pattern 1-7098 Fichier monolithique de 7000+ lignes, impossible à maintenir
C2 Variables globales multiples 2889-2893, 3561, 4089 État partagé entre modules (db, ansible_service, ws_manager, scheduler_service)
C3 Couplage fort Partout Services dépendent directement d'instances globales
C4 Imports circulaires potentiels 44-53, 2105-2106 Mix from models.xxx et from app.models.xxx
C5 Duplication de modèles Pydantic 550-932 Modèles dupliqués avec app/schemas/ existants

🟠 AVERTISSEMENTS (À corriger)

ID Problème Lignes Recommandation
W1 Fonctions trop longues 3676-3932 (bootstrap_host: 256L) Extraire en sous-fonctions
W2 Import re dupliqué 688, 706, 5406 Import unique en tête de fichier
W3 Syntaxe Pydantic dépréciée 572-575, 589-592 class Configmodel_config = ConfigDict(...)
W4 Print statements 1995, 2008, 2058... Utiliser logging
W5 Gestion d'erreur inconsistante Multiple Standardiser avec exceptions personnalisées
W6 Magic strings 564, 581, 597... Utiliser des Enums (Status, Level)
W7 Cache multi-niveau Multiple Risque de désynchronisation DB/mémoire
W8 Méthodes sync/async mélangées 2141-2146 start() sync vs start_async() async
W9 Exceptions génériques 2007, 2059... except Exception trop large
W10 Variables non utilisées Sporadique Nettoyer le code mort

🔵 INFORMATIFS (Améliorations)

ID Problème Recommandation
I1 Type hints manquants Ajouter types sur toutes les fonctions
I2 Docstrings mixtes FR/EN Uniformiser en français
I3 Constantes non regroupées Créer un module constants.py
I4 Tests unitaires insuffisants Ajouter tests pour chaque service
I5 Validation Pydantic incomplète Ajouter validators sur tous les champs critiques

1.4 Analyse de la Complexité

Complexité Cyclomatique Élevée

Fonction Lignes Complexité estimée Action
bootstrap_host 256 Très élevée Refactoriser en étapes
_execute_schedule 270 Très élevée Extraire logique métier
execute_adhoc_command 250 Élevée Simplifier gestion erreurs
execute_ansible_playbook 150 Modérée Extraire en service
_markdown_to_pdf_bytes 190 Élevée Module séparé

Violations SOLID

Principe Violation Exemple
Single Responsibility Oui Fichier fait tout (routing, services, models, utils)
Open/Closed Oui Ajout de fonctionnalité = modification du fichier
Liskov Substitution Non applicable -
Interface Segregation Oui Services trop couplés aux endpoints
Dependency Inversion Oui Dépendances hardcodées, pas d'injection

PHASE 2: PLAN DE REFACTORISATION

2.1 Nouvelle Structure Proposée

projet/
├── main.py                          # Point d'entrée uvicorn
├── app/
│   ├── __init__.py                  # Factory create_app()
│   ├── core/
│   │   ├── __init__.py
│   │   ├── config.py                # Configuration centralisée
│   │   ├── constants.py             # Constantes et enums
│   │   ├── exceptions.py            # Exceptions personnalisées
│   │   └── dependencies.py          # Injection de dépendances FastAPI
│   │
│   ├── models/                      # (existant - conserver)
│   │   ├── __init__.py
│   │   ├── database.py
│   │   ├── host.py
│   │   ├── task.py
│   │   ├── log.py
│   │   ├── schedule.py
│   │   ├── schedule_run.py
│   │   └── user.py
│   │
│   ├── schemas/                     # (existant - enrichir)
│   │   ├── __init__.py
│   │   ├── host.py                  # HostRequest, HostUpdateRequest, Host
│   │   ├── task.py                  # TaskRequest, Task, TaskLogFile
│   │   ├── group.py                 # GroupRequest, GroupUpdateRequest
│   │   ├── ansible.py               # AnsibleExecutionRequest, AdHocCommandRequest
│   │   ├── schedule.py              # Schedule*, ScheduleRun*, ScheduleRecurrence
│   │   ├── health.py                # HealthCheck, SystemMetrics
│   │   └── common.py                # CommandResult, LogEntry
│   │
│   ├── crud/                        # (existant - conserver)
│   │   ├── __init__.py
│   │   ├── host.py
│   │   ├── task.py
│   │   ├── log.py
│   │   ├── schedule.py
│   │   └── schedule_run.py
│   │
│   ├── services/                    # (existant - enrichir)
│   │   ├── __init__.py
│   │   ├── ansible_service.py       # AnsibleService
│   │   ├── task_log_service.py      # TaskLogService
│   │   ├── adhoc_history_service.py # AdHocHistoryService
│   │   ├── bootstrap_service.py     # BootstrapStatusService + bootstrap_host
│   │   ├── host_status_service.py   # HostStatusService
│   │   ├── scheduler_service.py     # SchedulerService
│   │   ├── websocket_service.py     # WebSocketManager
│   │   └── hybrid_db.py             # HybridDB
│   │
│   ├── routes/
│   │   ├── __init__.py              # Router aggregator
│   │   ├── hosts.py                 # /api/hosts/*
│   │   ├── groups.py                # /api/groups/*
│   │   ├── tasks.py                 # /api/tasks/*
│   │   ├── logs.py                  # /api/logs/*
│   │   ├── ansible.py               # /api/ansible/*
│   │   ├── playbooks.py             # /api/playbooks/*
│   │   ├── schedules.py             # /api/schedules/*
│   │   ├── adhoc.py                 # /api/adhoc/*
│   │   ├── bootstrap.py             # /api/bootstrap/*
│   │   ├── health.py                # /api/health/*
│   │   ├── notifications.py         # /api/notifications/*
│   │   ├── help.py                  # /api/help/*
│   │   └── websocket.py             # /ws
│   │
│   ├── utils/
│   │   ├── __init__.py
│   │   ├── pdf_generator.py         # _markdown_to_pdf_bytes
│   │   ├── help_renderer.py         # help.md -> HTML + markdown clean
│   │   ├── ssh_utils.py             # find_ssh_private_key, run_ssh_command
│   │   └── helpers.py               # Fonctions utilitaires diverses
│   │
│   └── middleware/
│       ├── __init__.py
│       └── auth.py                  # verify_api_key (déjà dans auth_service)
│
├── tests/                           # (existant - enrichir)
│   ├── __init__.py
│   ├── test_hosts.py
│   ├── test_tasks.py
│   ├── test_schedules.py
│   └── conftest.py
│
└── requirements.txt

2.2 Mapping Fonctionnalité → Fichier

Fonctionnalité actuelle Lignes Nouveau fichier
Configuration 55-100 app/core/config.py
Modèles Pydantic Host/Task 560-800 app/schemas/host.py, app/schemas/task.py
Modèles Schedule 803-932 app/schemas/schedule.py
Modèles Ad-hoc 718-760 app/schemas/ansible.py
TaskLogService 937-1568 app/services/task_log_service.py
AdHocHistoryService 1571-1951 app/services/adhoc_history_service.py
BootstrapStatusService 1954-2060 app/services/bootstrap_service.py
HostStatusService 2063-2099 app/services/host_status_service.py
SchedulerService 2102-2886 app/services/scheduler_service.py
WebSocketManager 2896-2929 app/services/websocket_service.py
AnsibleService 2933-3561 app/services/ansible_service.py
Bootstrap SSH functions 3564-3932 app/services/bootstrap_service.py
HybridDB 3935-4089 app/services/hybrid_db.py
Endpoints Hosts 4224-4698 app/routes/hosts.py
Endpoints Groups 4234-4416 app/routes/groups.py
Endpoints Tasks 4691-5060 app/routes/tasks.py
Endpoints Logs 5062-5134 app/routes/logs.py
Endpoints Ansible 5157-5530 app/routes/ansible.py
Endpoints Playbooks 5352-5491 app/routes/playbooks.py
Endpoints Ad-hoc 5533-5782, 5972-6070 app/routes/adhoc.py
Endpoints Bootstrap 5785-5969 app/routes/bootstrap.py
Endpoints Health 5902-6112 app/routes/health.py
Endpoints Schedules 6388-6949 app/routes/schedules.py
Endpoints Notifications 6952-7022 app/routes/notifications.py
PDF/Markdown utils 105-548 app/utils/pdf_generator.py, app/utils/help_renderer.py
SSH utils 3581-3673 app/utils/ssh_utils.py
Startup/Shutdown 7025-7098 app/__init__.py (create_app)

PHASE 3: ORDRE D'EXÉCUTION

Étape 1: Core (config, constants, exceptions, dependencies)

  1. Créer app/core/__init__.py
  2. Créer app/core/config.py - Centraliser toute la configuration
  3. Créer app/core/constants.py - Enums et constantes
  4. Créer app/core/exceptions.py - Exceptions personnalisées
  5. Créer app/core/dependencies.py - DI FastAPI

Étape 2: Schemas (enrichir les existants)

  1. Enrichir app/schemas/host.py
  2. Créer app/schemas/task.py
  3. Créer app/schemas/group.py
  4. Enrichir app/schemas/ansible.py
  5. Créer app/schemas/schedule.py
  6. Créer app/schemas/health.py
  7. Créer app/schemas/common.py

Étape 3: Services (nouveaux fichiers)

  1. Créer app/services/ansible_service.py
  2. Créer app/services/task_log_service.py
  3. Créer app/services/adhoc_history_service.py
  4. Créer app/services/bootstrap_service.py
  5. Créer app/services/host_status_service.py
  6. Créer app/services/scheduler_service.py
  7. Créer app/services/websocket_service.py
  8. Créer app/services/hybrid_db.py

Étape 4: Utils

  1. Créer app/utils/__init__.py
  2. Créer app/utils/pdf_generator.py
  3. Créer app/utils/help_renderer.py
  4. Créer app/utils/ssh_utils.py
  5. Créer app/utils/helpers.py

Étape 5: Routes (routers FastAPI)

  1. Créer app/routes/__init__.py
  2. Créer tous les fichiers de routes
  3. Migrer chaque groupe d'endpoints

Étape 6: App Factory et Main

  1. Modifier app/__init__.py - create_app()
  2. Créer main.py - Point d'entrée

Étape 7: Nettoyage et Tests

  1. Supprimer code dupliqué de app_optimized.py
  2. Mettre à jour les imports
  3. Adapter les tests existants
  4. Valider le fonctionnement

CONTRAINTES TECHNIQUES

À Maintenir

  • 100% des fonctionnalités existantes
  • Compatibilité avec les tests existants
  • Support WebSocket temps réel
  • Intégration SQLAlchemy async
  • Intégration APScheduler
  • Service de notifications ntfy

À Éviter

  • Imports circulaires
  • Variables globales (sauf configuration)
  • Couplage fort entre modules
  • Breaking changes dans l'API

Conventions

  • PEP 8 stricte
  • Type hints sur toutes les signatures
  • Docstrings en français
  • Logging au lieu de print()
  • Tests unitaires pour nouveaux modules

ESTIMATION

Phase Durée estimée Fichiers
Étape 1: Core ~30 min 5 fichiers
Étape 2: Schemas ~45 min 7 fichiers
Étape 3: Services ~90 min 8 fichiers
Étape 4: Utils ~30 min 5 fichiers
Étape 5: Routes ~120 min 13 fichiers
Étape 6: Factory ~15 min 2 fichiers
Étape 7: Tests ~60 min Adaptation

Total estimé: ~6-7 heures de travail



IMPLÉMENTATION TERMINÉE

Structure Créée

projet/
├── main.py                              # ✅ Point d'entrée uvicorn
├── app/
│   ├── __init__.py                      # ✅ Export create_app()
│   ├── factory.py                       # ✅ Application factory
│   ├── core/
│   │   ├── __init__.py                  # ✅
│   │   ├── config.py                    # ✅ Configuration centralisée
│   │   ├── constants.py                 # ✅ Enums et constantes
│   │   ├── exceptions.py                # ✅ Exceptions personnalisées
│   │   └── dependencies.py              # ✅ Injection de dépendances
│   ├── schemas/
│   │   ├── common.py                    # ✅ CommandResult, LogEntry, etc.
│   │   ├── host_api.py                  # ✅ Host, HostRequest, etc.
│   │   ├── group.py                     # ✅ GroupRequest, etc.
│   │   ├── ansible.py                   # ✅ AnsibleExecutionRequest, etc.
│   │   ├── task_api.py                  # ✅ Task, TaskRequest, TaskLogFile
│   │   ├── schedule_api.py              # ✅ Schedule, ScheduleRecurrence
│   │   └── health.py                    # ✅ HealthCheck
│   ├── services/
│   │   ├── websocket_service.py         # ✅ WebSocketManager
│   │   ├── host_status_service.py       # ✅ HostStatusService
│   │   ├── bootstrap_status_service.py  # ✅ BootstrapStatusService
│   │   ├── task_log_service.py          # ✅ TaskLogService
│   │   ├── adhoc_history_service.py     # ✅ AdHocHistoryService
│   │   ├── ansible_service.py           # ✅ AnsibleService
│   │   ├── scheduler_service.py         # ✅ SchedulerService
│   │   └── hybrid_db.py                 # ✅ HybridDB
│   ├── routes/
│   │   ├── __init__.py                  # ✅ Router aggregator
│   │   ├── hosts.py                     # ✅ /api/hosts/*
│   │   ├── groups.py                    # ✅ /api/groups/*
│   │   ├── tasks.py                     # ✅ /api/tasks/*
│   │   ├── logs.py                      # ✅ /api/logs/*
│   │   ├── ansible.py                   # ✅ /api/ansible/*
│   │   ├── playbooks.py                 # ✅ /api/playbooks/*
│   │   ├── schedules.py                 # ✅ /api/schedules/*
│   │   ├── adhoc.py                     # ✅ /api/adhoc/*
│   │   ├── bootstrap.py                 # ✅ /api/bootstrap/*
│   │   ├── health.py                    # ✅ /api/health/*
│   │   ├── notifications.py             # ✅ /api/notifications/*
│   │   ├── help.py                      # ✅ /api/help/*
│   │   └── websocket.py                 # ✅ /ws
│   └── utils/
│       ├── __init__.py                  # ✅
│       ├── ssh_utils.py                 # ✅ SSH & Bootstrap
│       ├── pdf_generator.py             # ✅ Markdown to PDF
│       └── help_renderer.py             # ✅ help.md -> HTML + markdown clean

INSTRUCTIONS DE MIGRATION

1. Tester la nouvelle structure

# Depuis le répertoire racine du projet
cd c:\dev\git\python\homelab-automation-api-v2

# Démarrer avec le nouveau point d'entrée
python main.py

# Ou avec uvicorn
uvicorn main:app --host 0.0.0.0 --port 8008 --reload

2. Vérifier les imports

Si des erreurs d'import surviennent, vérifier que tous les modules sont correctement importés dans les fichiers __init__.py.

3. Migration progressive

L'ancienne application (app_optimized.py) reste fonctionnelle. La migration peut être progressive:

  1. Phase 1: Tester la nouvelle structure en parallèle
  2. Phase 2: Basculer le point d'entrée principal
  3. Phase 3: Supprimer app_optimized.py une fois la validation complète

4. Points d'attention

Élément Action requise
Tests existants Adapter les imports pour utiliser les nouveaux modules
Docker Modifier le CMD pour utiliser main.py au lieu de app_optimized.py
Scripts de démarrage Mettre à jour run_dev.sh et run_dev.ps1
Variables d'environnement Aucun changement requis (même configuration)

5. Rollback

En cas de problème, revenir à l'ancienne structure:

uvicorn app.app_optimized:app --host 0.0.0.0 --port 8008 --reload

AMÉLIORATIONS FUTURES

Court terme

  • Ajouter des tests unitaires pour chaque nouveau module
  • Compléter la validation des schémas Pydantic
  • Ajouter la gestion des erreurs avec les exceptions personnalisées

Moyen terme

  • Implémenter l'injection de dépendances complète (pas de singletons)
  • Migrer les dernières fonctionnalités de app_optimized.py
  • Ajouter des tests d'intégration

Long terme

  • Supprimer app_optimized.py complètement
  • Implémenter le versioning de l'API (/api/v1/, /api/v2/)
  • Ajouter la documentation OpenAPI personnalisée

Document généré automatiquement - Refactorisation complétée le 2024-12-14