colibri.assistant.ai / utils /auth_cache.py
Gouzi Mohaled
Ajoute des fichiers et sous-dossiers supplémentaires
fe4792e
raw
history blame
12.5 kB
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)