Spaces:
Sleeping
Sleeping
File size: 12,453 Bytes
fe4792e |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
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)
|