# 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 Config` → `model_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 | |----------|-----------|---------| | **S**ingle Responsibility | Oui | Fichier fait tout (routing, services, models, utils) | | **O**pen/Closed | Oui | Ajout de fonctionnalité = modification du fichier | | **L**iskov Substitution | Non applicable | - | | **I**nterface Segregation | Oui | Services trop couplés aux endpoints | | **D**ependency 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 │ │ ├── markdown_parser.py # _HelpHtmlToMarkdownParser │ │ ├── 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/markdown_parser.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/markdown_parser.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 │ └── markdown_parser.py # ✅ HTML to Markdown ``` --- ## INSTRUCTIONS DE MIGRATION ### 1. Tester la nouvelle structure ```bash # 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: ```bash 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*