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 }