Files
projet_gestion_commande/backend/gestion/db/redis_session.go
T
2026-06-14 17:50:35 +02:00

185 lines
5.9 KiB
Go

package db
import (
"encoding/json"
"fmt"
"log"
"time"
)
// ============================================
// SESSION REDIS - GESTION UTILISATEUR
// ============================================
// SessionData représente une session client en Redis
type SessionData struct {
ClientID int `json:"client_id"`
Username string `json:"username"`
SessionID string `json:"session_id"`
Role string `json:"role"`
CreatedAt int64 `json:"created_at"`
LastActivity int64 `json:"last_activity"`
ExpiresAt int64 `json:"expires_at"`
BasketVersion int `json:"basket_version"`
PointsCache int `json:"points_cache"`
PenaltyCache float64 `json:"penalty_cache"`
Tags []string `json:"tags,omitempty"`
}
// ============================================
// CRÉER UNE SESSION CLIENT
// ============================================
// CreateClientSession crée une session Redis pour un client authentifié
// Appelé depuis handlers/auth.go après LoginClient réussi
//
// Exemple d'utilisation:
//
// sessionID := uuid.New().String()
// database.CreateClientSession(clientID, username, sessionID)
func (d *Database) CreateClientSession(clientID int, username string, sessionID string) error {
log.Printf("📝 [SESSION] Création session pour client: %s (ID: %d)", username, clientID)
sessionKey := fmt.Sprintf("session:client:%d", clientID)
now := time.Now().Unix()
expiresAt := now + (5 * 3600) // 5 heures
sessionData := SessionData{
ClientID: clientID,
Username: username,
SessionID: sessionID,
Role: "client",
CreatedAt: now,
LastActivity: now,
ExpiresAt: expiresAt,
BasketVersion: 0,
PointsCache: 0,
PenaltyCache: 0,
}
// Sérialiser et sauvegarder
sessionJSON, err := json.Marshal(sessionData)
if err != nil {
log.Printf("❌ [SESSION] Erreur sérialisation: %v", err)
return fmt.Errorf("erreur sérialisation session: %w", err)
}
ttl := time.Duration(expiresAt-now) * time.Second
if err := Redis.Set(RedisCtx, sessionKey, sessionJSON, ttl).Err(); err != nil {
log.Printf("❌ [SESSION] Erreur sauvegarde Redis: %v", err)
return fmt.Errorf("erreur sauvegarde session Redis: %w", err)
}
// Ajouter à l'index des sessions actives
if err := Redis.SAdd(RedisCtx, "session:active:clients", clientID).Err(); err != nil {
log.Printf("⚠️ [SESSION] Erreur ajout index: %v", err)
}
// Charger les infos du client (points, penalty) dans le cache
if client, err := d.GetClientByUsername(username); err == nil {
sessionData.PenaltyCache = float64(client.Amende)
sessionJSON, _ := json.Marshal(sessionData)
Redis.Set(RedisCtx, sessionKey, sessionJSON, ttl)
}
log.Printf("✅ [SESSION] Session créée pour %s - TTL: 5h", username)
return nil
}
// ============================================
// RÉCUPÉRER UNE SESSION CLIENT
// ============================================
// GetClientSession récupère la session Redis d'un client
// Retourne nil si session expirée ou inexistante
func (d *Database) GetClientSession(clientID int) (*SessionData, error) {
sessionKey := fmt.Sprintf("session:client:%d", clientID)
data, err := Redis.Get(RedisCtx, sessionKey).Result()
if err != nil {
log.Printf("⚠️ [SESSION] Pas de session trouvée pour client %d", clientID)
return nil, fmt.Errorf("session non trouvée")
}
var session SessionData
if err := json.Unmarshal([]byte(data), &session); err != nil {
log.Printf("❌ [SESSION] Erreur désérialisation: %v", err)
return nil, fmt.Errorf("erreur désérialisation: %w", err)
}
// Vérifier si session expirée
if time.Now().Unix() > session.ExpiresAt {
log.Printf("⚠️ [SESSION] Session expirée pour client %d", clientID)
Redis.Del(RedisCtx, sessionKey)
return nil, fmt.Errorf("session expirée")
}
return &session, nil
}
// ============================================
// METTRE À JOUR L'ACTIVITÉ DE SESSION
// ============================================
// RefreshSessionTimeout prolonge la durée de vie de la session
// Appelé régulièrement par SessionMiddleware (chaque requête client)
func (d *Database) RefreshSessionTimeout(clientID int) error {
sessionKey := fmt.Sprintf("session:client:%d", clientID)
// Récupérer la session
data, err := Redis.Get(RedisCtx, sessionKey).Result()
if err != nil {
return fmt.Errorf("session non trouvée")
}
var session SessionData
if err := json.Unmarshal([]byte(data), &session); err != nil {
return fmt.Errorf("erreur désérialisation: %w", err)
}
// Mettre à jour lastActivity et expiresAt
now := time.Now().Unix()
session.LastActivity = now
session.ExpiresAt = now + (5 * 3600) // Prolonger de 5 heures
// Resauvegarder
sessionJSON, _ := json.Marshal(session)
ttl := time.Duration(session.ExpiresAt-now) * time.Second
if err := Redis.Set(RedisCtx, sessionKey, sessionJSON, ttl).Err(); err != nil {
log.Printf("⚠️ [SESSION] Erreur refresh: %v", err)
return nil // Pas critique
}
return nil
}
// ============================================
// INVALIDER UNE SESSION (LOGOUT)
// ============================================
// InvalidateSession supprime la session Redis (logout)
// Appelé depuis handlers/auth.go dans LogoutClient
func (d *Database) InvalidateSession(clientID int) error {
sessionKey := fmt.Sprintf("session:client:%d", clientID)
basketKey := fmt.Sprintf("session:basket:%d", clientID)
// Supprimer la session
if err := Redis.Del(RedisCtx, sessionKey).Err(); err != nil {
log.Printf("⚠️ [SESSION] Erreur suppression: %v", err)
}
// Vider le panier Redis
if err := Redis.Del(RedisCtx, basketKey).Err(); err != nil {
log.Printf("⚠️ [SESSION] Erreur suppression panier: %v", err)
}
// Retirer de l'index
if err := Redis.SRem(RedisCtx, "session:active:clients", clientID).Err(); err != nil {
log.Printf("⚠️ [SESSION] Erreur retrait index: %v", err)
}
log.Printf("✅ [SESSION] Session invalidée pour client %d", clientID)
return nil
}