99 lines
4.1 KiB
Python
99 lines
4.1 KiB
Python
from functools import wraps
|
|
from flask import current_app, flash, jsonify, request, render_template
|
|
from models import db
|
|
from sqlalchemy.exc import SQLAlchemyError, IntegrityError
|
|
from decimal import Decimal, InvalidOperation
|
|
|
|
def handle_db_errors(f):
|
|
"""Décorateur pour gérer les erreurs de base de données"""
|
|
@wraps(f)
|
|
def decorated_function(*args, **kwargs):
|
|
try:
|
|
result = f(*args, **kwargs)
|
|
# Vérifier que le résultat est une réponse Flask valide
|
|
if result is None:
|
|
current_app.logger.error(f'Fonction {f.__name__} a retourné None')
|
|
return render_template('error.html', error="Une erreur inattendue s'est produite."), 500
|
|
return result
|
|
except IntegrityError as e:
|
|
db.session.rollback()
|
|
current_app.logger.error(f'Erreur d\'intégrité dans {f.__name__}: {e}')
|
|
|
|
error_msg = "Une erreur s'est produite lors de l'enregistrement."
|
|
if "UNIQUE constraint failed" in str(e):
|
|
error_msg = "Cette donnée existe déjà."
|
|
elif "CHECK constraint failed" in str(e):
|
|
error_msg = "Les données saisies ne respectent pas les contraintes."
|
|
|
|
if request.is_json:
|
|
return jsonify({'success': False, 'error': error_msg}), 400
|
|
else:
|
|
flash(error_msg, 'error')
|
|
return render_template('error.html', error=error_msg), 400
|
|
|
|
except SQLAlchemyError as e:
|
|
db.session.rollback()
|
|
current_app.logger.error(f'Erreur SQLAlchemy dans {f.__name__}: {e}')
|
|
|
|
error_msg = "Une erreur de base de données s'est produite."
|
|
if request.is_json:
|
|
return jsonify({'success': False, 'error': error_msg}), 500
|
|
else:
|
|
flash(error_msg, 'error')
|
|
return render_template('error.html', error=error_msg), 500
|
|
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
current_app.logger.error(f'Erreur inattendue dans {f.__name__}: {e}')
|
|
|
|
error_msg = "Une erreur inattendue s'est produite."
|
|
if request.is_json:
|
|
return jsonify({'success': False, 'error': error_msg}), 500
|
|
else:
|
|
flash(error_msg, 'error')
|
|
return render_template('error.html', error=error_msg), 500
|
|
|
|
return decorated_function
|
|
|
|
def safe_int_conversion(value, field_name="valeur"):
|
|
"""Conversion sécurisée en entier"""
|
|
if value is None:
|
|
return None
|
|
try:
|
|
return int(value)
|
|
except (ValueError, TypeError):
|
|
raise ValueError(f"La {field_name} doit être un nombre entier valide.")
|
|
|
|
def safe_decimal_conversion(value, field_name="valeur"):
|
|
"""Conversion sécurisée en décimal"""
|
|
if value is None:
|
|
return None
|
|
try:
|
|
return Decimal(str(value))
|
|
except (ValueError, TypeError, InvalidOperation):
|
|
raise ValueError(f"La {field_name} doit être un nombre décimal valide.")
|
|
|
|
def validate_json_data(data, required_fields):
|
|
"""Valide les données JSON et vérifie les champs requis"""
|
|
if not data:
|
|
raise ValueError("Aucune donnée fournie.")
|
|
|
|
missing_fields = [field for field in required_fields if field not in data or data[field] is None]
|
|
if missing_fields:
|
|
raise ValueError(f"Champs requis manquants: {', '.join(missing_fields)}")
|
|
|
|
return True
|
|
|
|
def log_user_action(action, details=None):
|
|
"""Log les actions utilisateur pour audit"""
|
|
current_app.logger.info(f"Action utilisateur: {action}" + (f" - {details}" if details else ""))
|
|
|
|
class ValidationError(Exception):
|
|
"""Exception personnalisée pour les erreurs de validation"""
|
|
pass
|
|
|
|
def handle_error(exception, default_message="Une erreur s'est produite"):
|
|
"""Gestionnaire d'erreur générique"""
|
|
current_app.logger.error(f"Erreur: {exception}")
|
|
flash(default_message, 'error')
|
|
return render_template('error.html', error=default_message), 500 |