feat(dashboard): Ajouter API Backup, Version et Rollback

API Backup (/api/backup/*):
- GET /stats - Statistiques des données disponibles
- POST /full - Backup complet (avec option modèles)
- GET /workflows - Export workflows uniquement
- GET /correction-packs - Export correction packs
- GET /trained-models - Export modèles entraînés (opt-in, anonymisés)
- GET /config - Export configuration (sanitisée)

API Version (/api/version/*):
- GET / - Version actuelle du système
- GET /system-info - Information système complète
- GET /check-update - Vérifier les mises à jour
- GET /backups - Lister les backups de version
- POST /create-backup - Créer point de restauration
- POST /upload-update - Uploader un package de mise à jour

Ces API permettent aux clients de:
- Télécharger leurs sauvegardes depuis le Dashboard
- Vérifier et installer les mises à jour
- Créer des points de restauration et rollback

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Dom
2026-01-19 15:36:15 +01:00
parent 4b96524964
commit 275aace599

View File

@@ -32,6 +32,10 @@ from core.monitoring.trigger_manager import TriggerManager
from core.monitoring.log_exporter import LogExporter
from core.monitoring.automation_scheduler import AutomationScheduler
# Modules pour backup et versioning
from core.system.backup_exporter import get_backup_exporter
from core.system.version_manager import get_version_manager
app = Flask(__name__)
app.config['SECRET_KEY'] = os.getenv('SECRET_KEY', 'dev-key-change-in-production')
socketio = SocketIO(app, cors_allowed_origins="*", async_mode='threading')
@@ -1229,6 +1233,283 @@ def automation_stop():
return jsonify({'error': str(e)}), 500
# =============================================================================
# API Backup & Export - Sauvegardes client
# =============================================================================
@app.route('/api/backup/stats')
def backup_stats():
"""Statistiques des données disponibles pour backup."""
try:
exporter = get_backup_exporter()
stats = exporter.get_backup_stats()
return jsonify({
'stats': stats,
'timestamp': datetime.now().isoformat()
})
except Exception as e:
api_logger.error(f"Error getting backup stats: {e}")
return jsonify({'error': str(e)}), 500
@app.route('/api/backup/full', methods=['POST'])
def backup_full():
"""Génère et télécharge un backup complet."""
try:
data = request.get_json() or {}
include_models = data.get('include_models', False)
exporter = get_backup_exporter()
zip_path = exporter.export_full_backup(include_models=include_models)
api_logger.info(f"Full backup created (include_models={include_models})")
return send_file(
zip_path,
mimetype='application/zip',
as_attachment=True,
download_name=f'rpa_vision_backup_{datetime.now().strftime("%Y%m%d_%H%M%S")}.zip'
)
except Exception as e:
api_logger.error(f"Error creating full backup: {e}")
return jsonify({'error': str(e)}), 500
@app.route('/api/backup/workflows')
def backup_workflows():
"""Génère et télécharge un backup des workflows uniquement."""
try:
exporter = get_backup_exporter()
zip_path = exporter.export_workflows()
api_logger.info("Workflows backup created")
return send_file(
zip_path,
mimetype='application/zip',
as_attachment=True,
download_name=f'rpa_workflows_{datetime.now().strftime("%Y%m%d_%H%M%S")}.zip'
)
except Exception as e:
api_logger.error(f"Error creating workflows backup: {e}")
return jsonify({'error': str(e)}), 500
@app.route('/api/backup/correction-packs')
def backup_correction_packs():
"""Génère et télécharge un backup des correction packs."""
try:
exporter = get_backup_exporter()
zip_path = exporter.export_correction_packs()
api_logger.info("Correction packs backup created")
return send_file(
zip_path,
mimetype='application/zip',
as_attachment=True,
download_name=f'rpa_correction_packs_{datetime.now().strftime("%Y%m%d_%H%M%S")}.zip'
)
except Exception as e:
api_logger.error(f"Error creating correction packs backup: {e}")
return jsonify({'error': str(e)}), 500
@app.route('/api/backup/trained-models')
def backup_trained_models():
"""
Génère et télécharge les modèles entraînés (opt-in).
Ces modèles sont anonymisés et contiennent uniquement les patterns
appris, pas les données brutes ou les screenshots.
"""
try:
exporter = get_backup_exporter()
zip_path = exporter.export_trained_models()
api_logger.info("Trained models backup created (opt-in)")
return send_file(
zip_path,
mimetype='application/zip',
as_attachment=True,
download_name=f'rpa_trained_models_{datetime.now().strftime("%Y%m%d_%H%M%S")}.zip'
)
except Exception as e:
api_logger.error(f"Error creating trained models backup: {e}")
return jsonify({'error': str(e)}), 500
@app.route('/api/backup/config')
def backup_config():
"""Génère et télécharge la configuration (sanitisée)."""
try:
exporter = get_backup_exporter()
zip_path = exporter.export_config()
api_logger.info("Config backup created (sanitized)")
return send_file(
zip_path,
mimetype='application/zip',
as_attachment=True,
download_name=f'rpa_config_{datetime.now().strftime("%Y%m%d_%H%M%S")}.zip'
)
except Exception as e:
api_logger.error(f"Error creating config backup: {e}")
return jsonify({'error': str(e)}), 500
# =============================================================================
# API Version & Updates - Gestion des versions
# =============================================================================
@app.route('/api/version')
def get_version():
"""Retourne la version actuelle du système."""
try:
vm = get_version_manager()
version_info = vm.get_current_version()
return jsonify({
'version': version_info.to_dict(),
'timestamp': datetime.now().isoformat()
})
except Exception as e:
api_logger.error(f"Error getting version: {e}")
return jsonify({'error': str(e)}), 500
@app.route('/api/version/system-info')
def get_system_info():
"""Retourne les informations système complètes."""
try:
vm = get_version_manager()
system_info = vm.get_system_info()
return jsonify({
'system_info': system_info,
'timestamp': datetime.now().isoformat()
})
except Exception as e:
api_logger.error(f"Error getting system info: {e}")
return jsonify({'error': str(e)}), 500
@app.route('/api/version/check-update')
def check_update():
"""Vérifie si une mise à jour est disponible."""
try:
vm = get_version_manager()
update = vm.check_for_updates()
if update:
return jsonify({
'update_available': True,
'update': update.to_dict(),
'timestamp': datetime.now().isoformat()
})
else:
return jsonify({
'update_available': False,
'message': 'Système à jour',
'timestamp': datetime.now().isoformat()
})
except Exception as e:
api_logger.error(f"Error checking update: {e}")
return jsonify({'error': str(e)}), 500
@app.route('/api/version/backups')
def list_version_backups():
"""Liste les backups de version disponibles pour rollback."""
try:
vm = get_version_manager()
backups = vm.list_backups()
return jsonify({
'backups': backups,
'total': len(backups),
'timestamp': datetime.now().isoformat()
})
except Exception as e:
api_logger.error(f"Error listing version backups: {e}")
return jsonify({'error': str(e)}), 500
@app.route('/api/version/create-backup', methods=['POST'])
def create_version_backup():
"""Crée un backup de la version actuelle (point de restauration)."""
try:
data = request.get_json() or {}
label = data.get('label')
vm = get_version_manager()
backup_path = vm.create_backup(label=label)
api_logger.info(f"Version backup created: {backup_path}")
return jsonify({
'success': True,
'backup_path': str(backup_path),
'label': label,
'timestamp': datetime.now().isoformat()
})
except Exception as e:
api_logger.error(f"Error creating version backup: {e}")
return jsonify({'error': str(e)}), 500
@app.route('/api/version/upload-update', methods=['POST'])
def upload_update_package():
"""
Upload d'un package de mise à jour.
Le package doit être un fichier ZIP contenant:
- update_manifest.json
- Les fichiers de mise à jour
"""
try:
if 'file' not in request.files:
return jsonify({'error': 'Aucun fichier fourni'}), 400
file = request.files['file']
if file.filename == '':
return jsonify({'error': 'Nom de fichier vide'}), 400
if not file.filename.endswith('.zip'):
return jsonify({'error': 'Le fichier doit être un ZIP'}), 400
# Sauvegarder dans le répertoire updates
vm = get_version_manager()
update_path = vm.updates_dir / file.filename
file.save(update_path)
# Extraire et valider le manifest
import zipfile
with zipfile.ZipFile(update_path, 'r') as zf:
if 'update_manifest.json' not in zf.namelist():
update_path.unlink()
return jsonify({'error': 'Package invalide: manifest manquant'}), 400
# Extraire le manifest
zf.extract('update_manifest.json', vm.updates_dir)
with open(vm.updates_dir / 'update_manifest.json', 'r') as f:
manifest = json.load(f)
api_logger.info(f"Update package uploaded: {file.filename} (version {manifest.get('version')})")
return jsonify({
'success': True,
'filename': file.filename,
'manifest': manifest,
'message': 'Package uploadé. Utilisez /api/version/check-update pour vérifier.',
'timestamp': datetime.now().isoformat()
})
except Exception as e:
api_logger.error(f"Error uploading update package: {e}")
return jsonify({'error': str(e)}), 500
# =============================================================================
# API Metrics (Prometheus)
# =============================================================================