homelab_automation/documentation/REFACTORING_AUDIT.md

465 lines
19 KiB
Markdown

# 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
│ │ ├── 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
```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*