Spaces:
Sleeping
Sleeping
import json | |
from datetime import datetime, timedelta | |
import pytz | |
from typing import Optional, List, Dict, Any | |
import logging | |
from utils.logging_utils import log_to_file | |
from utils.cache_manager import CacheManager | |
from utils.admin import supabase | |
class AuthenticationCache: | |
def __init__(self): | |
self.cache_manager = CacheManager() # initialise la connexion Redis | |
self.cache_duration = timedelta(hours=24) # Duree de validité du cache (24h) | |
self.cache_version_key = "auth_cache_version" # Pour suivre la version du cache | |
self.batch_size = 2 # Taille du batch pour le chargement par lots | |
self.session_cache_key = "user_session:" | |
self.history_cache_key = "chat_history:" # Ajout de cet attribut | |
async def is_cache_initialized(self) -> bool: | |
"""Vérifie si le cache est déjà initialisé""" | |
try: | |
all_keys = self.cache_manager.redis_client.keys("auth_user:*") | |
return len(all_keys) > 0 | |
except Exception as e: | |
log_to_file(f"Erreur lors de la vérification du cache : {str(e)}", level=logging.ERROR) | |
return False | |
async def get_cache_version(self) -> str: | |
"""Récupère la version actuelle du cache""" | |
try: | |
return await self.cache_manager.get(self.cache_version_key) or "" | |
except Exception as e: | |
log_to_file(f"Erreur lors de la récupération de la version du cache : {str(e)}", level=logging.ERROR) | |
return "" | |
async def update_cache_version(self) -> None: | |
"""Met à jour la version du cache""" | |
try: | |
current_time = datetime.now(pytz.UTC).isoformat() | |
await self.cache_manager.set(self.cache_version_key, current_time, expiry=self.cache_duration) | |
except Exception as e: | |
log_to_file(f"Erreur lors de la mise à jour de la version du cache : {str(e)}", level=logging.ERROR) | |
async def is_cache_outdated(self) -> bool: | |
"""Vérifie si le cache doit être mis à jour""" | |
try: | |
# Vérifier la dernière mise à jour du cache | |
cache_version = await self.get_cache_version() | |
if not cache_version: | |
return True | |
# Vérifier les modifications récentes dans Supabase | |
last_update = datetime.fromisoformat(cache_version) | |
recent_changes = supabase.table("users").select("updated_at").gt( | |
"updated_at", last_update.isoformat() | |
).execute() | |
return bool(recent_changes.data) | |
except Exception as e: | |
log_to_file(f"Erreur lors de la vérification de l'état du cache : {str(e)}", level=logging.ERROR) | |
return True | |
async def get_user_from_cache(self, email: str) -> Optional[Dict[str, Any]]: | |
"""Récupère les informations utilisateur depuis le cache. | |
Args: | |
email: L'adresse email de l'utilisateur à rechercher dans le cache. | |
Returns: | |
Un dictionnaire contenant les informations de l'utilisateur si trouvé dans le cache, None sinon. | |
""" | |
try: | |
# Construit la clé de cache en utilisant l'email. | |
cache_key = f"auth_user:{email}" | |
# Récupère l'utilisateur depuis le cache en utilisant la clé. | |
return await self.cache_manager.get(cache_key) | |
except Exception as e: | |
# Enregistre une erreur si une exception se produit pendant la récupération du cache. | |
log_to_file(f"Erreur lors de la récupération du cache utilisateur : {str(e)}", level=logging.ERROR) | |
# Retourne None en cas d'erreur. | |
return None | |
async def set_user_in_cache(self, user_data: Dict[str, Any]) -> bool: | |
"""Stocke les informations utilisateur dans le cache""" | |
try: | |
cache_key = f"auth_user:{user_data['email']}" | |
return await self.cache_manager.set(cache_key, user_data, expiry=self.cache_duration) | |
except Exception as e: | |
log_to_file(f"Erreur lors du stockage en cache : {str(e)}", level=logging.ERROR) | |
return False | |
async def load_all_users_to_cache(self) -> bool: | |
try: | |
log_to_file("Début du chargement des utilisateurs dans Redis", level=logging.INFO) | |
# Récupérer le nombre total d'utilisateurs | |
total_users = supabase.table("users").select("id", count="exact").execute() | |
total_count = total_users.count | |
log_to_file(f"Nombre total d'utilisateurs : {total_count}", level=logging.INFO) | |
# Charger les utilisateurs par lots | |
loaded_count = 0 | |
for offset in range(0, total_count, self.batch_size): | |
log_to_file(f"Chargement du lot {offset} - {offset + self.batch_size - 1}", level=logging.INFO) | |
users = supabase.table("users").select("*").range(offset, offset + self.batch_size - 1).execute() | |
if users.data: | |
for user in users.data: | |
if await self.set_user_in_cache(user): | |
loaded_count += 1 | |
log_to_file(f"Utilisateur {user['email']} mis en cache", level=logging.INFO) | |
else: | |
log_to_file(f"Échec du chargement de l'utilisateur : {user['email']}", level=logging.ERROR) | |
else: | |
log_to_file(f"Aucun utilisateur trouvé dans la plage {offset} - {offset + self.batch_size - 1}", level=logging.WARNING) | |
log_to_file(f"Chargement terminé : {loaded_count}/{total_count} utilisateurs mis en cache", level=logging.INFO) | |
# Mettre à jour la version du cache | |
await self.update_cache_version() | |
# Debug | |
await self.debug_redis_content() | |
return True | |
except Exception as e: | |
log_to_file(f"Erreur lors du chargement des utilisateurs : {str(e)}", level=logging.ERROR) | |
return False | |
async def sync_cache_with_db(self) -> bool: | |
"""Synchronise le cache avec la base de données""" | |
try: | |
cache_version = await self.get_cache_version() | |
if not cache_version: | |
return await self.load_all_users_to_cache() | |
last_update = datetime.fromisoformat(cache_version) | |
updated_users = supabase.table("users").select("*").gt( | |
"updated_at", last_update.isoformat() | |
).execute() | |
if updated_users.data: | |
for user in updated_users.data: | |
await self.set_user_in_cache(user) | |
await self.update_cache_version() | |
log_to_file(f"Cache mis à jour avec {len(updated_users.data)} modifications", level=logging.INFO) | |
return True | |
except Exception as e: | |
log_to_file(f"Erreur lors de la synchronisation du cache : {str(e)}", level=logging.ERROR) | |
return False | |
async def load_user_sessions_to_cache(self) -> bool: | |
"""Charge toutes les sessions actives dans Redis""" | |
try: | |
log_to_file("Chargement des sessions utilisateurs dans Redis", level=logging.INFO) | |
# Récupérer toutes les sessions actives | |
sessions = supabase.table("chat_sessions").select("*").eq( | |
"is_active", True | |
).execute() | |
if not sessions.data: | |
log_to_file("Aucune session active trouvée", level=logging.INFO) | |
return True | |
# Charger chaque session dans Redis | |
for session in sessions.data: | |
session_key = f"{self.session_cache_key}{session['user_id']}" | |
await self.cache_manager.set( | |
session_key, | |
session, | |
expiry=self.cache_duration | |
) | |
# Charger l'historique associé | |
history = supabase.table("messages").select("*").eq( | |
"session_id", session['session_id'] | |
).order('created_at', desc=False).execute() | |
if history.data: | |
history_key = f"{self.history_cache_key}{session['session_id']}" | |
await self.cache_manager.set( | |
history_key, | |
history.data, | |
expiry=self.cache_duration | |
) | |
log_to_file(f"Sessions et historiques chargés : {len(sessions.data)} sessions", level=logging.INFO) | |
return True | |
except Exception as e: | |
log_to_file(f"Erreur lors du chargement des sessions : {str(e)}", level=logging.ERROR) | |
return False | |
async def get_active_session(self, user_id: int) -> Optional[Dict]: | |
"""Récupère la session active d'un utilisateur depuis le cache""" | |
try: | |
session_key = f"{self.session_cache_key}{user_id}" | |
session = await self.cache_manager.get(session_key) | |
if not session: | |
# Si pas dans le cache, chercher dans Supabase et mettre en cache | |
db_session = supabase.table("chat_sessions").select("*").eq( | |
"user_id", user_id | |
).eq("is_active", True).execute() | |
if db_session.data: | |
session = db_session.data[0] | |
await self.cache_manager.set( | |
session_key, | |
session, | |
expiry=self.cache_duration | |
) | |
return session | |
except Exception as e: | |
log_to_file(f"Erreur lors de la récupération de la session : {str(e)}", level=logging.ERROR) | |
return None | |
async def get_chat_history(self, session_id: str) -> List[Dict]: | |
"""Récupère l'historique du chat depuis le cache""" | |
try: | |
history_key = f"{self.history_cache_key}{session_id}" | |
history = await self.cache_manager.get(history_key) | |
if not history: | |
# Si pas dans le cache, chercher dans Supabase et mettre en cache | |
db_history = supabase.table("messages").select("*").eq( | |
"session_id", session_id | |
).order('created_at', desc=False).execute() | |
if db_history.data: | |
history = db_history.data | |
await self.cache_manager.set( | |
history_key, | |
history, | |
expiry=self.cache_duration | |
) | |
return history or [] | |
except Exception as e: | |
log_to_file(f"Erreur lors de la récupération de l'historique : {str(e)}", level=logging.ERROR) | |
return [] | |
async def update_session_and_history(self, session_data: Dict, new_message: Dict = None): | |
"""Met à jour la session et l'historique dans le cache""" | |
try: | |
session_key = f"{self.session_cache_key}{session_data['user_id']}" | |
await self.cache_manager.set( | |
session_key, | |
session_data, | |
expiry=self.cache_duration | |
) | |
if new_message: | |
history_key = f"{self.history_cache_key}{session_data['session_id']}" | |
current_history = await self.get_chat_history(session_data['session_id']) | |
current_history.append(new_message) | |
await self.cache_manager.set( | |
history_key, | |
current_history, | |
expiry=self.cache_duration | |
) | |
except Exception as e: | |
log_to_file(f"Erreur lors de la mise à jour de la session : {str(e)}", level=logging.ERROR) | |
async def debug_redis_content(self): | |
"""Affiche le contenu de Redis pour debug""" | |
try: | |
all_keys = self.cache_manager.redis_client.keys("auth_user:*") | |
log_to_file(f"Nombre d'utilisateurs en cache : {len(all_keys)}", level=logging.INFO) | |
for key in all_keys: | |
log_to_file(f"Utilisateur en cache : {key}", level=logging.INFO) | |
except Exception as e: | |
log_to_file(f"Erreur debug Redis : {str(e)}", level=logging.ERROR) | |