diff --git a/backend/gestion/handlers/auth.go b/backend/gestion/handlers/auth.go
index 3bba4bb9..aa53c5c7 100644
--- a/backend/gestion/handlers/auth.go
+++ b/backend/gestion/handlers/auth.go
@@ -412,9 +412,9 @@ func GetClient2FAStatus(c *gin.Context) {
settings, _ := database.GetSettings()
c.JSON(http.StatusOK, gin.H{
- "two_fa_enabled": client.TwoFAEnabled,
- "telegram_linked": tgLinked,
- "admin_2fa_enabled": settings.Telegram2FAEnabled,
+ "two_fa_enabled": client.TwoFAEnabled,
+ "telegram_linked": tgLinked,
+ "admin_2fa_enabled": settings.Telegram2FAEnabled,
})
}
@@ -520,60 +520,6 @@ func LogoutClient(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "Déconnexion réussie"})
}
-// RegisterAdmin crée un nouvel utilisateur admin/cabine/livreur
-func RegisterAdmin(c *gin.Context) {
- var req models.RegisterAdminRequest
- if err := c.ShouldBindJSON(&req); err != nil {
- log.Printf("❌ [REGISTER_ADMIN] Erreur binding: %v", err)
- c.JSON(http.StatusBadRequest, gin.H{"error": "Données invalides"})
- return
- }
-
- database := c.MustGet("database").(*db.Database)
-
- if existingUser, _ := database.GetUserByUsername(req.Username); existingUser != nil {
- log.Printf("❌ [REGISTER_ADMIN] Username déjà utilisé: %s", req.Username)
- c.JSON(http.StatusConflict, gin.H{"error": "Nom d'utilisateur déjà utilisé"})
- return
- }
-
- hashed, _ := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
- user := &models.User{
- Username: req.Username,
- Password: string(hashed),
- Role: req.Role,
- }
-
- if err := database.CreateUser(user); err != nil {
- log.Printf("❌ [REGISTER_ADMIN] Erreur création: %v", err)
- c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur création utilisateur"})
- return
- }
-
- token, err := generateAdminToken(user)
- if err != nil {
- log.Printf("❌ [REGISTER_ADMIN] Erreur génération token: %v", err)
- c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur génération token"})
- return
- }
-
- expiresAt := time.Now().Add(adminTokenDuration)
- if err := database.SaveToken(user.ID, user.Role, token, expiresAt); err != nil {
- log.Printf("❌ [REGISTER_ADMIN] Erreur SaveToken: %v", err)
- c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur enregistrement token"})
- return
- }
-
- user.Password = ""
-
- c.JSON(http.StatusCreated, models.LoginResponse{
- AccessToken: token,
- TokenType: "Bearer",
- ExpiresIn: int(adminTokenDuration.Seconds()),
- User: user,
- })
-}
-
// LoginAdmin authentifie un admin/cabine/livreur
func LoginAdmin(c *gin.Context) {
var req models.LoginRequest
@@ -707,30 +653,6 @@ func GetCurrentAdmin(c *gin.Context) {
})
}
-// HealthCheck vérifie la santé de l'API
-// GET /api/v1/health
-func HealthCheck(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
- if err := database.DB.Ping(); err != nil {
- log.Printf("⚠️ [HEALTH] Database down: %v", err)
- c.JSON(http.StatusServiceUnavailable, gin.H{
- "status": "unhealthy",
- "database": "disconnected",
- "timestamp": time.Now().Unix(),
- })
- return
- }
-
- log.Printf("✅ [HEALTH] API healthy")
-
- c.JSON(http.StatusOK, gin.H{
- "status": "healthy",
- "database": "connected",
- "timestamp": time.Now().Unix(),
- "version": "2.0.0",
- })
-}
-
// GetAllUsers récupère tous les utilisateurs (Admin only)
func GetAllUsers(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
diff --git a/backend/gestion/handlers/cabine.go b/backend/gestion/handlers/cabine.go
index 6a57d4ac..76ccab24 100644
--- a/backend/gestion/handlers/cabine.go
+++ b/backend/gestion/handlers/cabine.go
@@ -1,244 +1,13 @@
-// ============================================
-// handlers/cabine_handlers.go - COMPLET
-// INCLUT: SetCommandDestinationCoordinates
-// ============================================
-
package handlers
import (
- "encoding/json"
- "fmt"
"gestion/db"
- "gestion/utils"
- "log"
"net/http"
- "slices"
"strconv"
- "time"
"github.com/gin-gonic/gin"
)
-// ============================================
-// 0️⃣ FONCTION ADMIN: SET DESTINATION COORDINATES
-// ============================================
-
-// SetCommandDestinationCoordinates stocke les coordonnées destination en Redis
-func SetCommandDestinationCoordinates(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- userRole := c.GetString("role")
- if userRole != "admin" {
- c.JSON(http.StatusForbidden, gin.H{"error": "Accès réservé aux administrateurs"})
- return
- }
-
- adminUsername := c.GetString("username")
-
- commandID, err := strconv.Atoi(c.Param("id"))
- if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
- return
- }
-
- var req struct {
- Latitude float64 `json:"latitude" binding:"required"`
- Longitude float64 `json:"longitude" binding:"required"`
- }
-
- if err := c.ShouldBindJSON(&req); err != nil {
- c.JSON(http.StatusBadRequest, gin.H{
- "error": "Latitude et longitude requises",
- })
- return
- }
-
- // Validation des coordonnées GPS
- if req.Latitude < -90 || req.Latitude > 90 {
- c.JSON(http.StatusBadRequest, gin.H{
- "error": "Latitude invalide (doit être entre -90 et 90)",
- "value": req.Latitude,
- })
- return
- }
-
- if req.Longitude < -180 || req.Longitude > 180 {
- c.JSON(http.StatusBadRequest, gin.H{
- "error": "Longitude invalide (doit être entre -180 et 180)",
- "value": req.Longitude,
- })
- return
- }
-
- if !utils.CheckCommand(commandID, database) {
- c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"})
- }
-
- destCacheKey := fmt.Sprintf("command:destination:%d", commandID)
- coordsJSON, _ := json.Marshal(map[string]float64{
- "lat": req.Latitude,
- "lon": req.Longitude,
- })
-
- ttlSeconds := 24 * 60 * 60 // 24 heures
- err = db.Redis.Set(db.RedisCtx, destCacheKey, coordsJSON, time.Duration(ttlSeconds)*time.Second).Err()
- if err != nil {
- c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur stockage Redis",
- })
- return
- }
-
- // Ajouter un log
- database.AddCommandLog(commandID, "destination_set",
- fmt.Sprintf("Coordonnées destination définies par admin %s: (%.6f, %.6f) via Redis",
- adminUsername, req.Latitude, req.Longitude),
- adminUsername)
-
- log.Printf("✅ [ADMIN %s] Coordonnées destination définies pour CMD %d: (%.6f, %.6f) en Redis",
- adminUsername, commandID, req.Latitude, req.Longitude)
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "message": "Coordonnées définies avec succès en Redis",
- "command_id": commandID,
- })
-}
-
-// ============================================
-// 1. CLIENT PROFILE
-// ============================================
-
-func GetClientProfile(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
- username := c.Param("username")
-
- if username == "" {
- c.JSON(http.StatusBadRequest, gin.H{"error": "Username requis"})
- return
- }
-
- client, err := database.GetClientByUsername(username)
- if err != nil {
- c.JSON(http.StatusNotFound, gin.H{"error": "Client non trouvé"})
- return
- }
-
- client.Password = ""
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "client": gin.H{
- "id": client.ID,
- "username": client.Username,
- "command": client.Command,
- "amende": client.Amende,
- "points_extra": client.PointsExtra,
- "created_at": client.CreatedAt,
- },
- })
-}
-
-func GetClientFullHistory(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
- username := c.Param("username")
-
- if username == "" {
- c.JSON(http.StatusBadRequest, gin.H{"error": "Username requis"})
- return
- }
-
- client, err := database.GetClientByUsername(username)
- if err != nil {
- c.JSON(http.StatusNotFound, gin.H{"error": "Client non trouvé"})
- return
- }
-
- commands, err := database.GetAllCommands("", username)
- if err != nil {
- c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur récupération historique"})
- return
- }
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "client": gin.H{
- "username": client.Username,
- "total_commands": client.Command,
- "amende": client.Amende,
- "points_extra": client.PointsExtra,
- },
- "commands": commands,
- "count": len(commands),
- })
-}
-
-// ============================================
-// 2. UPDATE ADDRESS
-// ============================================
-
-func UpdateCommandAddressCabine(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- commandID, err := strconv.Atoi(c.Param("id"))
- if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
- return
- }
-
- var req struct {
- DeliveryAddress string `json:"delivery_address" binding:"required"`
- Reason string `json:"reason"`
- }
- if err := c.ShouldBindJSON(&req); err != nil || req.DeliveryAddress == "" {
- c.JSON(http.StatusBadRequest, gin.H{"error": "Adresse de livraison requise"})
- return
- }
-
- command, err := database.GetCommandByID(commandID)
- if err != nil {
- c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"})
- return
- }
-
- status, _ := command["status"].(string)
-
- allowedStatuses := []string{"pending", "", "assigned"}
- if !slices.Contains(allowedStatuses, status) {
- c.JSON(http.StatusBadRequest, gin.H{
- "error": "Impossible de modifier l'adresse d'une commande en cours ou terminée",
- "current_status": status,
- "allowed_statuses": allowedStatuses,
- })
- return
- }
-
- if err := database.UpdateCommandAddress(commandID, req.DeliveryAddress); err != nil {
- c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur lors de la mise à jour de l'adresse",
- })
- return
- }
-
- cabineUsername, _ := c.Get("username")
- message := fmt.Sprintf("Adresse modifiée par cabine: %s", req.DeliveryAddress)
- if req.Reason != "" {
- message += fmt.Sprintf(" (Raison: %s)", req.Reason)
- }
- database.AddCommandLog(commandID, "address_updated", message, cabineUsername.(string))
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "message": "Adresse de livraison mise à jour",
- "command_id": commandID,
- "delivery_address": req.DeliveryAddress,
- })
-}
-
-// ============================================
-// 3. LIVREUR POSITION
-// ============================================
-
func GetLivreurPosition(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
livreurUsername := c.Param("username")
@@ -273,108 +42,6 @@ func GetLivreurPosition(c *gin.Context) {
})
}
-func GetDeliveryTrackingClient(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- username, exists := c.Get("username")
- if !exists {
- c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
- return
- }
-
- commandID, err := strconv.Atoi(c.Param("id"))
- if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
- return
- }
-
- command, err := database.GetCommandByID(commandID)
- if err != nil {
- c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"})
- return
- }
-
- if command["username"].(string) != username.(string) {
- c.JSON(http.StatusForbidden, gin.H{"error": "Cette commande ne vous appartient pas"})
- return
- }
-
- livreurAssign, _ := command["livreur_assign"].(string)
- logs, _ := database.GetCommandLogs(commandID)
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "command_id": commandID,
- "status": command["status"],
- "livreur": livreurAssign,
- "address": command["adresse"],
- "logs": logs,
- "message": "Suivi en cours - ETA disponible via /api/v1/orders/:id/eta",
- })
-}
-
-// ============================================
-// 5. DELIVERY TRACKING ADMIN (AVEC GPS)
-// ============================================
-
-func GetDeliveryTracking(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- userRole := c.GetString("role")
- if userRole != "admin" && userRole != "cabine" {
- c.JSON(http.StatusForbidden, gin.H{
- "error": "Accès refusé - Réservé aux administrateurs et cabines",
- })
- return
- }
-
- commandID, err := strconv.Atoi(c.Param("id"))
- if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
- return
- }
-
- command, err := database.GetCommandByID(commandID)
- if err != nil {
- c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"})
- return
- }
-
- livreurAssign, _ := command["livreur_assign"].(string)
- if livreurAssign == "" {
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "command": command,
- "status": "Aucun livreur assigné",
- })
- return
- }
-
- position, err := database.GetLivreurPosition(livreurAssign)
- logs, _ := database.GetCommandLogs(commandID)
-
- response := gin.H{
- "success": true,
- "command": command,
- "livreur": livreurAssign,
- "logs": logs,
- }
-
- status, _ := command["status"].(string)
- if err != nil && (status == "livre" || status == "approved") {
- response["livreur_position"] = nil
- response["position_status"] = "Livraison terminée - Position non suivie"
- } else if err != nil {
- response["livreur_position"] = nil
- response["position_status"] = "Position non disponible (GPS peut-être désactivé)"
- } else {
- response["livreur_position"] = position
- response["position_status"] = "Position en temps réel"
- }
-
- c.JSON(http.StatusOK, response)
-}
-
func GetDeliveryIssues(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -390,7 +57,7 @@ func GetDeliveryIssues(c *gin.Context) {
issues, err := database.GetDeliveryIssues(status)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur récupération problèmes",
+ "error": "Erreur récupération problèmes",
})
return
}
@@ -426,7 +93,7 @@ func CreateDeliveryIssue(c *gin.Context) {
)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur création problème",
+ "error": "Erreur création problème",
})
return
}
@@ -462,7 +129,7 @@ func UpdateDeliveryIssue(c *gin.Context) {
err = database.UpdateDeliveryIssue(issueID, req.Status, req.Resolution, cabineUsername.(string))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur mise à jour",
+ "error": "Erreur mise à jour",
})
return
}
@@ -473,53 +140,6 @@ func UpdateDeliveryIssue(c *gin.Context) {
})
}
-func AddDeliverySupport(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- commandID, err := strconv.Atoi(c.Param("id"))
- if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": "ID commande invalide"})
- return
- }
-
- var req struct {
- Message string `json:"message"`
- }
-
- c.ShouldBindJSON(&req)
-
- if req.Message == "" {
- c.JSON(http.StatusBadRequest, gin.H{
- "error": "Message requis",
- "example": gin.H{
- "message": "Votre message de support ici",
- },
- })
- return
- }
-
- cabineUsername, _ := c.Get("username")
-
- err = database.AddCommandLog(
- commandID,
- "note",
- fmt.Sprintf("Note cabine: %s", req.Message),
- cabineUsername.(string),
- )
-
- if err != nil {
- c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur ajout support",
- })
- return
- }
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "message": "Support ajouté",
- })
-}
-
func GetCommandLogs(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -532,7 +152,7 @@ func GetCommandLogs(c *gin.Context) {
logs, err := database.GetCommandLogs(commandID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur récupération logs",
+ "error": "Erreur récupération logs",
})
return
}
@@ -543,127 +163,3 @@ func GetCommandLogs(c *gin.Context) {
"count": len(logs),
})
}
-
-// ============================================
-// 7. FORCE VALIDATE DELIVERY
-// ============================================
-
-func ForceValidateDelivery(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- userRole := c.GetString("role")
- if userRole != "admin" {
- c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé - Admin seulement"})
- return
- }
-
- adminUsername, _ := c.Get("username")
-
- commandID, err := strconv.Atoi(c.Param("id"))
- if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
- return
- }
-
- var req struct {
- Reason string `json:"reason" binding:"required"`
- }
-
- if err := c.ShouldBindJSON(&req); err != nil {
- c.JSON(http.StatusBadRequest, gin.H{
- "error": "Raison requise pour validation forcée",
- "example": gin.H{
- "reason": "Client confirmé par téléphone",
- },
- })
- return
- }
-
- if req.Reason == "" {
- c.JSON(http.StatusBadRequest, gin.H{
- "error": "Veuillez fournir une raison pour la validation forcée",
- })
- return
- }
-
- command, err := database.GetCommandByID(commandID)
- if err != nil {
- c.JSON(http.StatusNotFound, gin.H{
- "error": "Commande non trouvée",
- "command_id": commandID,
- })
- return
- }
-
- status, ok := command["status"].(string)
- if !ok {
- c.JSON(http.StatusBadRequest, gin.H{"error": "Statut de commande invalide"})
- return
- }
-
- if status == "livre" {
- c.JSON(http.StatusBadRequest, gin.H{
- "error": "Cette commande a déjà été validée",
- "current_status": status,
- })
- return
- }
-
- validStatuses := []string{"assigned", "en_route", "pending", "priority"}
- if !slices.Contains(validStatuses, status) {
- c.JSON(http.StatusBadRequest, gin.H{
- "error": "Commande ne peut pas être validée de force dans ce statut",
- "current_status": status,
- "valid_statuses": validStatuses,
- })
- return
- }
-
- err = database.UpdateCommandStatus(commandID, "livre")
- if err != nil {
- c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur lors de la validation forcée",
- })
- return
- }
-
- clientUsername, _ := command["username"].(string)
- livreurAssign, _ := command["livreur_assign"].(string)
-
- if clientUsername != "" {
- clientMsg := fmt.Sprintf("Ta commande #%d a bien été livrée ! Bonne dégustation l'ami et à bientôt 😊\n\n⚠️ VALIDE LA RÉCEPTION DE TA COMMANDE DANS LA RUBRIQUE SUIVI POUR RÉCUPÉRER TES POINTS DE FIDÉLITÉ ⚠️", database.GetClientOrderID(commandID))
- database.NotifyClient(clientUsername, commandID, "livre", clientMsg)
- }
-
- if err := database.IncrementClientCommandCount(clientUsername); err != nil {
- log.Printf("⚠️ Erreur compteur commandes: %v", err)
- }
-
- if err := database.AddClientPointsByCategory(clientUsername, 10, ""); err != nil {
- log.Printf("⚠️ Erreur ajout points: %v", err)
- }
-
- if livreurAssign != "" {
- err := database.CompleteDeliveryAndProcessNext(livreurAssign, commandID)
- if err != nil {
- log.Printf("⚠️ Erreur optimisation: %v", err)
- }
- }
-
- message := fmt.Sprintf("VALIDATION FORCÉE par admin %s - Raison: %s", adminUsername.(string), req.Reason)
- database.AddCommandLog(commandID, "livre", message, adminUsername.(string))
-
- log.Printf("🔴 Commande %d validée de force par %s - Raison: %s", commandID, adminUsername.(string), req.Reason)
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "message": "Livraison validée de force (sans vérification GPS)",
- "command_id": commandID,
- "validation_type": "forced",
- "reason": req.Reason,
- "validated_by": adminUsername.(string),
- "new_status": "livre",
- "points_awarded": 10,
- "queue_optimized": livreurAssign != "",
- })
-}
diff --git a/backend/gestion/handlers/cancel_command.go b/backend/gestion/handlers/cancel_command.go
index d0b7b840..ee437f8f 100644
--- a/backend/gestion/handlers/cancel_command.go
+++ b/backend/gestion/handlers/cancel_command.go
@@ -1,9 +1,3 @@
-// ============================================
-// handlers/cancel_command_handler.go
-// ANNULATION DE COMMANDES AVEC SANCTIONS ÉVOLUTIVES
-// VERSION SÉCURISÉE - FIX ETA CHECK
-// ============================================
-
package handlers
import (
@@ -218,9 +212,6 @@ func CancelCommandByClient(c *gin.Context) {
return
}
- // ============================================
- // SUCCÈS
- // ============================================
log.Printf("✅ [CANCEL_CLIENT] Commande %d annulée", commandID)
response := gin.H{
@@ -242,10 +233,6 @@ func CancelCommandByClient(c *gin.Context) {
c.JSON(http.StatusOK, response)
}
-// ============================================
-// HISTORIQUE DES ANNULATIONS - VERSION SÉCURISÉE
-// ============================================
-
func GetMyCancellationHistory(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
diff --git a/backend/gestion/handlers/commands.go b/backend/gestion/handlers/commands.go
index 68a23b1d..56174424 100644
--- a/backend/gestion/handlers/commands.go
+++ b/backend/gestion/handlers/commands.go
@@ -595,10 +595,6 @@ func ValidateDelivery(c *gin.Context) {
})
}
-// ============================================
-// GESTION ADMIN
-// ============================================
-
// GetAvailableDeliveryPersons récupère les livreurs disponibles
// GET /api/v1/admin/delivery-persons/available
func GetAvailableDeliveryPersons(c *gin.Context) {
@@ -778,13 +774,6 @@ func GetClientCommandsHistory(c *gin.Context) {
c.JSON(http.StatusOK, resp)
}
-// ============================================
-// NOTIFICATIONS CLIENT
-// ============================================
-
-// NotifyClientToDescend envoie une notification push au client pour descendre récupérer sa commande
-// POST /api/v2/admin/protected/orders/:id/notify-client
-// POST /api/v1/cabine/commands/:id/notify-client
func NotifyClientToDescend(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
diff --git a/backend/gestion/handlers/delivery_admin.go b/backend/gestion/handlers/delivery_admin.go
index d026df9f..a905908a 100644
--- a/backend/gestion/handlers/delivery_admin.go
+++ b/backend/gestion/handlers/delivery_admin.go
@@ -11,6 +11,7 @@ import (
"gestion/utils"
"log"
"net/http"
+ "slices"
"strconv"
"time"
@@ -39,7 +40,7 @@ func GetDeliveryPersonDetails(c *gin.Context) {
if err != nil {
log.Printf("❌ [GET_DELIVERY_DETAILS] Livreur non trouvé: %v", err)
c.JSON(http.StatusNotFound, gin.H{
- "error": "Livreur non trouvé",
+ "error": "Livreur non trouvé",
})
return
}
@@ -116,22 +117,14 @@ func UpdateDeliveryPersonStatusAdmin(c *gin.Context) {
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
- "error": "Statut requis",
+ "error": "Statut requis",
})
return
}
- // Valider le statut
validStatuses := []string{"available", "busy", "offline"}
- isValid := false
- for _, vs := range validStatuses {
- if req.Status == vs {
- isValid = true
- break
- }
- }
- if !isValid {
+ if !slices.Contains(validStatuses, req.Status) {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Statut invalide",
"valid_statuses": validStatuses,
@@ -160,7 +153,7 @@ func UpdateDeliveryPersonStatusAdmin(c *gin.Context) {
if err != nil {
log.Printf("❌ [UPDATE_DELIVERY_STATUS] Erreur mise à jour: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur mise à jour statut",
+ "error": "Erreur mise à jour statut",
})
return
}
@@ -321,7 +314,7 @@ func GetDeliveryPersonHistory(c *gin.Context) {
if err != nil {
log.Printf("❌ [GET_DELIVERY_HISTORY] Erreur récupération: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur récupération historique",
+ "error": "Erreur récupération historique",
})
return
}
@@ -368,7 +361,7 @@ func UpdateDeliveryPersonLocationAdmin(c *gin.Context) {
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
- "error": "Coordonnées GPS requises",
+ "error": "Coordonnées GPS requises",
})
return
}
@@ -415,7 +408,7 @@ func UpdateDeliveryPersonLocationAdmin(c *gin.Context) {
if err != nil {
log.Printf("❌ [UPDATE_DELIVERY_LOCATION] Erreur mise à jour: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur mise à jour position",
+ "error": "Erreur mise à jour position",
})
return
}
@@ -437,12 +430,6 @@ func UpdateDeliveryPersonLocationAdmin(c *gin.Context) {
})
}
-// ============================================
-// 🗑️ REMOVE COMMAND FROM QUEUE
-// ============================================
-
-// RemoveCommandFromQueue retire une commande de la queue d'un livreur
-// DELETE /api/v2/admin/protected/delivery-persons/:username/queue/:command_id
func RemoveCommandFromQueue(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -474,9 +461,6 @@ func RemoveCommandFromQueue(c *gin.Context) {
log.Printf("🗑️ [REMOVE_FROM_QUEUE] Suppression: cmd %d de la queue de %s", commandID, username)
- // ============================================
- // Vérifier que le livreur existe
- // ============================================
livreur, err := database.GetUserByUsername(username)
if err != nil {
log.Printf("❌ [REMOVE_FROM_QUEUE] Livreur non trouvé")
@@ -491,9 +475,6 @@ func RemoveCommandFromQueue(c *gin.Context) {
return
}
- // ============================================
- // Vérifier que la commande existe
- // ============================================
command, err := database.GetCommandByID(commandID)
if err != nil {
log.Printf("❌ [REMOVE_FROM_QUEUE] Commande non trouvée")
@@ -501,19 +482,15 @@ func RemoveCommandFromQueue(c *gin.Context) {
return
}
- // ============================================
- // Retirer de la queue
- // ============================================
err = database.RemoveCommandFromDeliverymanQueue(username, commandID)
if err != nil {
log.Printf("❌ [REMOVE_FROM_QUEUE] Erreur suppression: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur suppression de la queue",
+ "error": "Erreur suppression de la queue",
})
return
}
- // Optionnel: Réassigner la commande en "pending"
currentStatus, _ := command["status"].(string)
if currentStatus == "assigned" || currentStatus == "en_route" {
err = database.UpdateCommandStatus(commandID, "pending")
diff --git a/backend/gestion/handlers/eta.go b/backend/gestion/handlers/eta.go
index e1565463..96ed5ba2 100644
--- a/backend/gestion/handlers/eta.go
+++ b/backend/gestion/handlers/eta.go
@@ -1,8 +1,3 @@
-// ============================================
-// handlers/eta_handler_corrected.go
-// CORRECTION: ETA visible UNIQUEMENT après en_route
-// ============================================
-
package handlers
import (
diff --git a/backend/gestion/handlers/geoloca.go b/backend/gestion/handlers/geoloca.go
index b79ad92e..70245fe5 100644
--- a/backend/gestion/handlers/geoloca.go
+++ b/backend/gestion/handlers/geoloca.go
@@ -13,11 +13,6 @@ import (
"github.com/gin-gonic/gin"
)
-// ============================================
-// GÉOCODAGE D'ADRESSES
-// ============================================
-// geo_handlers.go
-
func GeocodeAddress(c *gin.Context) {
geoService := c.MustGet("geoService").(*services.GeoService)
@@ -323,9 +318,6 @@ func AutoAssignNearestDeliveryPerson(c *gin.Context) {
log.Printf("📍 Adresse géocodée: %s -> (%.6f, %.6f)", address, location.Latitude, location.Longitude)
- // ============================================
- // 🔹 SAUVEGARDER LES COORDONNÉES DANS LE CACHE REDIS
- // ============================================
destCacheKey := fmt.Sprintf("command:destination:%d", commandID)
coordsJSON, _ := json.Marshal(map[string]float64{
"lat": location.Latitude,
@@ -555,10 +547,6 @@ func AutoAssignNearestDeliveryPerson(c *gin.Context) {
})
}
-// ============================================
-// ASSIGNATION EN MASSE (TOUTES LES COMMANDES PENDING)
-// ============================================
-
// AutoAssignAllPendingCommands assigne toutes les commandes en attente
// POST /api/v2/admin/protected/commands/auto-assign-all
func AutoAssignAllPendingCommands(c *gin.Context) {
@@ -716,10 +704,6 @@ func AutoAssignAllPendingCommands(c *gin.Context) {
})
}
-// ============================================
-// RÉCUPÉRER L'ÉTAT DES QUEUES DES LIVREURS
-// ============================================
-
// GetAllDeliveryQueues retourne l'état de toutes les queues des livreurs
// GET /api/v2/admin/protected/delivery/queues
func GetAllDeliveryQueues(c *gin.Context) {
@@ -769,12 +753,6 @@ func GetAllDeliveryQueues(c *gin.Context) {
})
}
-// ============================================
-// RÉCUPÉRER LA QUEUE D'UN LIVREUR SPÉCIFIQUE
-// ============================================
-
-// GetDeliverymanQueue retourne la queue d'un livreur spécifique
-// GET /api/v2/admin/protected/delivery/:username/queue
func GetDeliverymanQueue(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
diff --git a/backend/gestion/handlers/gps.go b/backend/gestion/handlers/gps.go
index 21d8343d..4897de64 100644
--- a/backend/gestion/handlers/gps.go
+++ b/backend/gestion/handlers/gps.go
@@ -13,7 +13,6 @@ import (
"net/url"
"strconv"
-
"github.com/gin-gonic/gin"
)
@@ -79,56 +78,6 @@ func GetDeliveryPersonMapLinks(c *gin.Context) {
})
}
-// GetCommandNavigationLinks génère les liens de navigation pour une commande
-// GET /api/v2/admin/protected/commands/:id/navigation-links
-func GetCommandNavigationLinks(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- userRole := c.GetString("role")
- if userRole != "admin" {
- c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
- return
- }
-
- commandID, err := strconv.Atoi(c.Param("id"))
- if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
- return
- }
-
- // Récupérer la commande
- command, err := database.GetCommandByID(commandID)
- if err != nil {
- c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"})
- return
- }
-
- // Vérifier qu'un livreur est assigné
- livreurAssign, ok := command["livreur_assign"].(string)
- if !ok || livreurAssign == "" {
- c.JSON(http.StatusNotFound, gin.H{
- "error": "Aucun livreur assigné à cette commande",
- })
- return
- }
-
- // Générer les liens de navigation
- links, err := database.GenerateMapLinksForCommand(commandID, livreurAssign)
- if err != nil {
- c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur génération des liens",
- })
- return
- }
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "command_id": commandID,
- "deliveryman": livreurAssign,
- "navigation_links": links,
- })
-}
-
// GetLivreurNavLink retourne le lien Waze App pour une livraison assignée au livreur connecté
// GET /api/v1/livreur/deliveries/:id/nav-link
func GetLivreurNavLink(c *gin.Context) {
diff --git a/backend/gestion/handlers/history.go b/backend/gestion/handlers/history.go
index d05ab4ce..8fbc858b 100644
--- a/backend/gestion/handlers/history.go
+++ b/backend/gestion/handlers/history.go
@@ -1,8 +1,3 @@
-// ============================================
-// handlers/history_handlers.go
-// ============================================
-// Gestion de l'historique des commandes terminées
-
package handlers
import (
@@ -12,14 +7,9 @@ import (
"net/http"
"strconv"
-
"github.com/gin-gonic/gin"
)
-// GetMyCompletedOrders récupère l'historique des commandes terminées du client
-// GET /api/v1/my-commands/history
-// ✅ Authentification requise (ClientMiddleware)
-// ✅ Retourne uniquement les commandes avec status = "approved"
func GetMyCompletedOrders(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -41,7 +31,7 @@ func GetMyCompletedOrders(c *gin.Context) {
if err != nil {
log.Printf("❌ [HISTORY] Erreur: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur lors de la récupération de l'historique",
+ "error": "Erreur lors de la récupération de l'historique",
})
return
}
@@ -96,8 +86,6 @@ func GetMyCompletedOrders(c *gin.Context) {
// GetMyCompletedOrdersWithItems récupère l'historique avec les détails des items
// GET /api/v1/my-commands/history/detailed
-// ✅ Authentification requise (ClientMiddleware)
-// ✅ Retourne les commandes approved avec tous les items
func GetMyCompletedOrdersWithItems(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -119,13 +107,13 @@ func GetMyCompletedOrdersWithItems(c *gin.Context) {
if err != nil {
log.Printf("❌ [HISTORY_DETAILED] Erreur: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur lors de la récupération de l'historique",
+ "error": "Erreur lors de la récupération de l'historique",
})
return
}
// ✅ Enrichir chaque commande avec ses items
- var enrichedCommands []map[string]interface{}
+ var enrichedCommands []map[string]any
for _, command := range commands {
commandID, _ := strconv.Atoi(fmt.Sprintf("%v", command["id"]))
@@ -137,11 +125,11 @@ func GetMyCompletedOrdersWithItems(c *gin.Context) {
items, err := database.GetCommandItems(commandID)
if err != nil {
log.Printf("⚠️ [HISTORY_DETAILED] Erreur items pour cmd %d: %v", commandID, err)
- items = []map[string]interface{}{}
+ items = []map[string]any{}
}
// Ajouter les items à la commande
- enrichedCommand := make(map[string]interface{})
+ enrichedCommand := make(map[string]any)
for k, v := range command {
enrichedCommand[k] = v
}
@@ -177,10 +165,6 @@ func GetMyCompletedOrdersWithItems(c *gin.Context) {
c.JSON(http.StatusOK, response)
}
-// GetOrderHistory récupère l'historique d'une commande spécifique avec logs
-// GET /api/v1/commands/:id/history
-// ✅ Authentification requise
-// ✅ Vérifie que la commande appartient au client
func GetOrderHistory(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
diff --git a/backend/gestion/handlers/panier.go b/backend/gestion/handlers/panier.go
index 3b1d7930..8892eec9 100644
--- a/backend/gestion/handlers/panier.go
+++ b/backend/gestion/handlers/panier.go
@@ -1,7 +1,3 @@
-// ============================================
-// handlers/basket_handlers_CORRIGES.go
-// ============================================
-
package handlers
import (
@@ -78,10 +74,6 @@ func AddProductsBasket(c *gin.Context) {
})
}
-// ============================================
-// ============================================
-// GET /api/v1/panier/:username
-// Récupère le panier du client authentifié
func GetAllBaskets(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
username := c.Param("username")
@@ -148,11 +140,6 @@ func GetAllBaskets(c *gin.Context) {
})
}
-// ============================================
-// ✅ SÉCURISÉ: DeleteProductFromBasket
-// ============================================
-// DELETE /api/v1/panier/remove
-// Supprime un produit du panier
func DeleteProductFromBasket(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -212,11 +199,6 @@ func DeleteProductFromBasket(c *gin.Context) {
})
}
-// ============================================
-// ✅ SÉCURISÉ: ClearBasket
-// ============================================
-// DELETE /api/v1/panier/clear
-// Vide le panier du client
func ClearBasket(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -391,9 +373,6 @@ func ValidateBasket(c *gin.Context) {
log.Printf("✅ [CHECKOUT] Zone OK: %s, total=%.2f€ >= %.2f€, crédit parrainage utilisé: %.2f€", zoneResult.ZoneName, cartTotal, zoneResult.MinAmount, referralUsed)
- // ============================================
- // 2️⃣ Débiter le parrainage AVANT la commande (évite double-spend)
- // ============================================
if referralUsed > 0 {
if err := database.DebitReferralBalance(usernameStr, referralUsed); err != nil {
log.Printf("❌ [CHECKOUT] Solde parrainage insuffisant pour %s: %v", usernameStr, err)
@@ -451,9 +430,6 @@ func ValidateBasket(c *gin.Context) {
log.Printf("✅ [CHECKOUT] Commande %d créée", commandID)
- // ============================================
- // PAIEMENT CRYPTO - créer le paiement NowPayments
- // ============================================
if isCrypto {
np := c.MustGet("nowpayments").(*services.NowPaymentsClient)
ipnURL := fmt.Sprintf("%s/api/v1/webhooks/nowpayments", getBaseURL(c))
diff --git a/backend/gestion/handlers/product.go b/backend/gestion/handlers/product.go
index 0bee9888..ea504405 100644
--- a/backend/gestion/handlers/product.go
+++ b/backend/gestion/handlers/product.go
@@ -17,10 +17,6 @@ import (
"github.com/gin-gonic/gin"
)
-// ============================================
-// CONFIGURATION & LIMITES
-// ============================================
-
const (
MaxFileSize = 10 * 1024 * 1024 // 10MB par fichier
MaxTotalUploadSize = 50 * 1024 * 1024 // 50MB total
@@ -30,7 +26,6 @@ const (
MaxProductsPerUser = 100 // Limite pour éviter spam
)
-// ✅ MIME types autorisés (vérification réelle du contenu)
var allowedMimeTypes = map[string]bool{
"image/jpeg": true,
"image/png": true,
@@ -41,28 +36,6 @@ var allowedMimeTypes = map[string]bool{
"video/quicktime": true,
}
-// ============================================
-// MIDDLEWARE D'AUTHORIZATION
-// ============================================
-
-func RequireAdminOrCabine() gin.HandlerFunc {
- return func(c *gin.Context) {
- role := c.GetString("role")
- if role != "admin" && role != "cabine" {
- c.JSON(http.StatusForbidden, gin.H{
- "error": "Accès refusé - Admin ou Cabine requis",
- })
- c.Abort()
- return
- }
- c.Next()
- }
-}
-
-// ============================================
-// HELPERS DE VALIDATION
-// ============================================
-
func validateProductName(name string) error {
if len(name) == 0 {
return fmt.Errorf("nom requis")
@@ -187,10 +160,6 @@ func sanitizeFilePath(path string) (string, error) {
return cleaned, nil
}
-// ============================================
-// CREATE PRODUCT - VERSION SÉCURISÉE
-// ============================================
-
func CreateProduct(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -475,10 +444,6 @@ func CreateProduct(c *gin.Context) {
})
}
-// ============================================
-// GET ENDPOINTS - SÉCURISÉS (lecture publique OK)
-// ============================================
-
func GetAllProducts(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -567,10 +532,6 @@ func GetProductByID(c *gin.Context) {
})
}
-// ============================================
-// UPDATE PRODUCT - VERSION SÉCURISÉE
-// ============================================
-
func UpdateProduct(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
diff --git a/backend/gestion/handlers/redis_services.go b/backend/gestion/handlers/redis_services.go
index 687837c7..6ded3e41 100644
--- a/backend/gestion/handlers/redis_services.go
+++ b/backend/gestion/handlers/redis_services.go
@@ -1,8 +1,3 @@
-// ============================================
-// handlers/redis_handlers.go - VERSION FINALE
-// UTILISE UNIQUEMENT LES MÉTHODES DB PostgreSQL
-// ============================================
-
package handlers
import (
@@ -13,6 +8,7 @@ import (
"gestion/utils"
"log"
"net/http"
+ "slices"
"strconv"
"strings"
"time"
@@ -20,10 +16,6 @@ import (
"github.com/gin-gonic/gin"
)
-// ============================================
-// GESTION DE LA FILE DE COMMANDES
-// ============================================
-
func validatePenaltyPoints(points int) error {
if points <= 0 {
return fmt.Errorf("points invalides: %d (doit être > 0)", points)
@@ -47,72 +39,6 @@ func sanitizeReason(reason string) string {
return strings.TrimSpace(reason)
}
-// GetCommandQueue récupère toutes les commandes en attente dans la file Redis
-// GET /api/v2/admin/protected/queue/pending
-func GetCommandQueue(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- userRole := c.GetString("role")
- if userRole != "admin" {
- c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
- return
- }
-
- nextCommand, err := database.GetNextCommandInQueue()
- if err != nil {
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "message": "Aucune commande en attente",
- "queue": []interface{}{},
- })
- return
- }
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "next_command": nextCommand,
- })
-}
-
-// AutoAssignNextCommand assigne automatiquement la prochaine commande en file
-// POST /api/v2/admin/protected/queue/auto-assign
-func AutoAssignNextCommand(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- userRole := c.GetString("role")
- if userRole != "admin" {
- c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
- return
- }
-
- nextCommand, err := database.GetNextCommandInQueue()
- if err != nil {
- c.JSON(http.StatusNotFound, gin.H{
- "error": "Aucune commande en attente",
- })
- return
- }
-
- err = database.AutoAssignCommand(nextCommand.CommandID)
- if err != nil {
- utils.ServerErr(c, "Erreur lors de l'assignation automatique", err)
- return
- }
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "message": "Commande assignée automatiquement",
- "command_id": nextCommand.CommandID,
- })
-}
-
-// ============================================
-// GESTION DES LIVREURS - LOCALISATION
-// ============================================
-
-// UpdateLivreurLocation met à jour la position GPS du livreur
-// POST /api/v1/livreur/location/update
-// Body: {"latitude": 48.8566, "longitude": 2.3522}
func UpdateLivreurLocation(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -171,7 +97,7 @@ func UpdateLivreurLocation(c *gin.Context) {
usernameStr, req.Latitude, req.Longitude)
// ✅ Recalculer l'ETA en temps réel si livreur en_route
- go refreshETAForActivDelivery(database, usernameStr, req.Latitude, req.Longitude)
+ go refreshETAForActivDelivery(usernameStr, req.Latitude, req.Longitude)
// ✅ 2. Vérifier/Initialiser le statut du livreur
statusKey := fmt.Sprintf("delivery:status:%s", usernameStr)
@@ -290,14 +216,6 @@ func GetDeliveryPersonLocation(c *gin.Context) {
})
}
-// ============================================
-// LOCALISATION DU LIVREUR POUR UNE COMMANDE
-// ============================================
-
-// GetDeliverymanLocationForCommand récupère la position GPS du livreur assigné à une commande
-// GET /api/v2/admin/protected/commands/:id/deliveryman/location (ADMIN)
-// GET /api/v1/cabine/commands/:id/deliveryman/location (CABINE)
-// Accessible uniquement par les admins et la cabine
func GetDeliverymanLocationForCommand(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -460,13 +378,6 @@ func GetDeliverymanLocationForCommand(c *gin.Context) {
})
}
-// ============================================
-// GESTION DES LIVREURS - STATUT
-// ============================================
-
-// UpdateDeliveryPersonStatus met à jour le statut de disponibilité du livreur
-// POST /api/v1/livreur/status
-// Body: {"status": "available" | "busy" | "offline"}
func UpdateDeliveryPersonStatus(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -490,18 +401,9 @@ func UpdateDeliveryPersonStatus(c *gin.Context) {
utils.BindErr(c, err)
return
}
-
- // Validation du statut
validStatuses := []string{"available", "busy", "offline"}
- isValid := false
- for _, s := range validStatuses {
- if req.Status == s {
- isValid = true
- break
- }
- }
- if !isValid {
+ if !slices.Contains(validStatuses, req.Status) {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Statut invalide",
"valid_statuses": validStatuses,
@@ -509,7 +411,6 @@ func UpdateDeliveryPersonStatus(c *gin.Context) {
})
return
}
-
usernameStr := username.(string)
err := database.SetDeliveryPersonStatus(usernameStr, req.Status, 0)
@@ -604,118 +505,6 @@ func GetMyQueue(c *gin.Context) {
})
}
-// GetAvailableDeliveryPersonsRealtime récupère les livreurs disponibles depuis Redis
-// GET /api/v2/admin/protected/delivery/available-realtime
-func GetAvailableDeliveryPersonsRealtime(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- userRole := c.GetString("role")
- if userRole != "admin" {
- c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
- return
- }
-
- livreurs, err := database.GetAvailableDeliveryPersonsRedis()
- if err != nil {
- utils.ServerErr(c, "Erreur lors de la récupération des livreurs", err)
- return
- }
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "livreurs": livreurs,
- "count": len(livreurs),
- })
-}
-
-// ============================================
-// GESTION ETA (Estimated Time of Arrival)
-// ============================================
-
-// SetCommandETAHandler permet au livreur de définir l'ETA d'une livraison
-// POST /api/v1/livreur/deliveries/:id/set-eta
-// Body: {"eta_minutes": 25}
-func SetCommandETAHandler(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- username, exists := c.Get("username")
- if !exists {
- c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
- return
- }
-
- userRole := c.GetString("role")
- if userRole != "livreur" {
- c.JSON(http.StatusForbidden, gin.H{"error": "Accès réservé aux livreurs"})
- return
- }
-
- commandID, err := strconv.Atoi(c.Param("id"))
- if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
- return
- }
-
- var req struct {
- ETAMinutes int `json:"eta_minutes" binding:"required"`
- }
-
- if err := c.ShouldBindJSON(&req); err != nil {
- utils.BindErr(c, err)
- return
- }
-
- // Validation de l'ETA
- if req.ETAMinutes < 1 || req.ETAMinutes > 120 {
- c.JSON(http.StatusBadRequest, gin.H{
- "error": "L'ETA doit être entre 1 et 120 minutes",
- })
- return
- }
-
- usernameStr := username.(string)
-
- // Vérifier que la commande existe et est assignée au livreur
- command, err := database.GetCommandByID(commandID)
- if err != nil {
- c.JSON(http.StatusNotFound, gin.H{
- "error": "Commande non trouvée",
- })
- return
- }
-
- livreurAssign, ok := command["livreur_assign"].(string)
- if !ok || livreurAssign != usernameStr {
- c.JSON(http.StatusForbidden, gin.H{
- "error": "Cette commande ne vous est pas assignée",
- })
- return
- }
-
- // Mettre à jour l'ETA dans Redis
- err = database.SetCommandETA(commandID, req.ETAMinutes)
- if err != nil {
- utils.ServerErr(c, "Erreur lors de la mise à jour de l'ETA", err)
- return
- }
-
- log.Printf("⏱️ ETA défini pour commande %d par %s: %d minutes", commandID, usernameStr, req.ETAMinutes)
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "message": "ETA mis à jour avec succès",
- "command_id": commandID,
- "eta_minutes": req.ETAMinutes,
- })
-}
-
-// ============================================
-// PÉNALITÉS - UTILISE PostgreSQL
-// ============================================
-
-// ApplyClientPenalty applique une pénalité à un client (Admin seulement)
-// POST /api/v2/admin/protected/penalty
-// Body: {"username": "john", "points": 50, "reason": "Retard paiement"}
func ApplyClientPenalty(c *gin.Context) {
database := c.MustGet("database").(*db.Database)
@@ -993,9 +782,6 @@ func ResetClientPenaltiesAdmin(c *gin.Context) {
})
}
-// AddClientPointsAdmin ajoute des points à un client dans un pool donné (Admin/Cabine)
-// POST /api/v2/admin/protected/client/:username/points/add
-// Body: {"pool_key": "pool_0", "points": 10}
func AddClientPointsAdmin(c *gin.Context) {
userRole := c.GetString("role")
if userRole != "admin" && userRole != "cabine" {
@@ -1040,7 +826,7 @@ func AddClientPointsAdmin(c *gin.Context) {
}
if !poolExists {
c.JSON(http.StatusBadRequest, gin.H{
- "error": "Pool de points invalide",
+ "error": "Pool de points invalide",
"pools_valides": func() []string {
keys := make([]string, 0, len(settings.PointsPools))
for _, p := range settings.PointsPools {
@@ -1073,9 +859,6 @@ func AddClientPointsAdmin(c *gin.Context) {
})
}
-// SubtractClientPointsAdmin retire des points à un client (plancher à 0)
-// POST /api/v2/admin/protected/client/:username/points/subtract
-// Body: {"pool_key": "pool_0", "points": 10}
func SubtractClientPointsAdmin(c *gin.Context) {
userRole := c.GetString("role")
if userRole != "admin" && userRole != "cabine" {
@@ -1164,12 +947,6 @@ func SubtractClientPointsAdmin(c *gin.Context) {
})
}
-// ============================================
-// STATISTIQUES TEMPS RÉEL
-// ============================================
-
-// GetRealtimeStats récupère les statistiques en temps réel
-// GET /api/v2/admin/protected/stats/realtime
func GetRealtimeStats(c *gin.Context) {
userRole := c.GetString("role")
if userRole != "admin" {
@@ -1200,13 +977,7 @@ func GetRealtimeStats(c *gin.Context) {
})
}
-// ============================================
-// RECALCUL ETA EN TEMPS RÉEL (appelé à chaque update GPS)
-// ============================================
-
-// refreshETAForActivDelivery recalcule l'ETA depuis la position actuelle du livreur.
-// Appelé en goroutine à chaque mise à jour GPS (toutes les ~15s).
-func refreshETAForActivDelivery(database *db.Database, username string, lat, lon float64) {
+func refreshETAForActivDelivery(username string, lat, lon float64) {
// 1. Récupérer le statut actuel du livreur
statusKey := fmt.Sprintf("delivery:status:%s", username)
statusData, err := db.Redis.Get(db.RedisCtx, statusKey).Result()
diff --git a/backend/gestion/handlers/stats.go b/backend/gestion/handlers/stats.go
index a476c533..585b40b0 100644
--- a/backend/gestion/handlers/stats.go
+++ b/backend/gestion/handlers/stats.go
@@ -2,30 +2,12 @@ package handlers
import (
"gestion/db"
+ "gestion/models"
"net/http"
- "time"
"github.com/gin-gonic/gin"
)
-type weekdayRow struct {
- DOW int `gorm:"column:dow"`
- Count int `gorm:"column:count"`
-}
-
-type dayRow struct {
- Day time.Time `gorm:"column:day"`
- Count int `gorm:"column:count"`
-}
-
-type productRow struct {
- ProductID int `gorm:"column:product_id"`
- Name string `gorm:"column:name"`
- Quantity float64 `gorm:"column:total_quantity"`
- OrderCount int `gorm:"column:order_count"`
- Revenue float64 `gorm:"column:revenue"`
-}
-
var weekdayNames = []string{"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"}
// GetAdminStats returns aggregated order & product statistics for the admin dashboard.
@@ -34,7 +16,7 @@ func GetAdminStats(c *gin.Context) {
gdb := database.GDB
// ── Commandes par jour de la semaine (all time, non annulées) ──────────────
- var wdRows []weekdayRow
+ var wdRows []models.WeekdayRow
gdb.Raw(`
SELECT EXTRACT(DOW FROM created_at)::int AS dow, COUNT(*) AS count
FROM commandes
@@ -59,7 +41,7 @@ func GetAdminStats(c *gin.Context) {
}
// ── Commandes par jour sur 30 jours ───────────────────────────────────────
- var dayRows []dayRow
+ var dayRows []models.DayRow
gdb.Raw(`
SELECT DATE(created_at) AS day, COUNT(*) AS count
FROM commandes
@@ -79,7 +61,7 @@ func GetAdminStats(c *gin.Context) {
}
// ── Top produits (quantité vendue, commandes terminées) ───────────────────
- var prodRows []productRow
+ var prodRows []models.ProductRow
gdb.Raw(`
SELECT
ci.product_id,
@@ -137,11 +119,11 @@ func GetAdminStats(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"summary": gin.H{
- "total_orders": totalOrders,
- "total_revenue": totalRevenue,
- "peak_weekday": peakWeekday,
- "top_product": topProductName,
- "avg_per_day": avgPerDay,
+ "total_orders": totalOrders,
+ "total_revenue": totalRevenue,
+ "peak_weekday": peakWeekday,
+ "top_product": topProductName,
+ "avg_per_day": avgPerDay,
},
"by_weekday": byWeekday,
"by_day_30": byDay,
diff --git a/backend/gestion/handlers/traffic.go b/backend/gestion/handlers/traffic.go
index 40f65650..92d88281 100644
--- a/backend/gestion/handlers/traffic.go
+++ b/backend/gestion/handlers/traffic.go
@@ -10,7 +10,7 @@ import (
)
// getFloatFromMap récupère un float64 depuis une map avec différents types
-func getFloatFromMap(m map[string]interface{}, key string) (float64, bool) {
+func getFloatFromMap(m map[string]any, key string) (float64, bool) {
value, exists := m[key]
if !exists || value == nil {
return 0, false
diff --git a/backend/gestion/handlers/update_profile.go b/backend/gestion/handlers/update_profile.go
index 7465f1d8..337b6fba 100644
--- a/backend/gestion/handlers/update_profile.go
+++ b/backend/gestion/handlers/update_profile.go
@@ -169,10 +169,6 @@ func GetMyProfile(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"success": true, "client": sanitizeClient(client)})
}
-// ============================================
-// MODIFICATION PROFIL CLIENT (PAR ADMIN)
-// ============================================
-
// UpdateClientByAdmin permet à un admin de modifier n'importe quel profil client
// PUT /api/v2/admin/protected/clients/:id
func UpdateClientByAdmin(c *gin.Context) {
@@ -349,10 +345,6 @@ func UpdateClientByAdmin(c *gin.Context) {
})
}
-// ============================================
-// MODIFICATION PROFIL USER (PAR ADMIN)
-// ============================================
-
// UpdateUserByAdmin permet à un admin de modifier n'importe quel profil user
// PUT /api/v2/admin/protected/users/:id
func UpdateUserByAdmin(c *gin.Context) {
@@ -468,10 +460,6 @@ func UpdateUserByAdmin(c *gin.Context) {
})
}
-// ============================================
-// UTILITAIRES
-// ============================================
-
func sanitizeClient(client *models.Client) gin.H {
return gin.H{
"id": client.ID,
diff --git a/backend/gestion/handlers/validation_deleviry.go b/backend/gestion/handlers/validation_deleviry.go
index 8aee1110..9d8abfbb 100644
--- a/backend/gestion/handlers/validation_deleviry.go
+++ b/backend/gestion/handlers/validation_deleviry.go
@@ -1,10 +1,8 @@
package handlers
import (
- "encoding/json"
"fmt"
"gestion/db"
- "gestion/services"
"log"
"net/http"
"strconv"
@@ -24,249 +22,6 @@ const (
MAX_DELIVERY_VALIDATION_DISTANCE_KM = 0.1 // 100 mètres = 0.1 km
)
-// ============================================
-// 1️⃣ VALIDATION LIVRAISON PAR LE LIVREUR (AVEC VÉRIFICATION GPS)
-// ============================================
-
-func ValidateDeliveryByLivreur(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- // ✅ SÉCURITÉ: Livreur seulement
- username, exists := c.Get("username")
- if !exists || c.GetString("role") != "livreur" {
- log.Printf("❌ [VALIDATE_LIVREUR] Accès refusé")
- c.JSON(http.StatusForbidden, gin.H{"error": "Accès réservé aux livreurs"})
- return
- }
-
- usernameStr := username.(string)
-
- commandID, err := strconv.Atoi(c.Param("id"))
- if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
- return
- }
-
- var req struct {
- Latitude float64 `json:"latitude" binding:"required"`
- Longitude float64 `json:"longitude" binding:"required"`
- }
-
- if err := c.ShouldBindJSON(&req); err != nil {
- log.Printf("❌ [VALIDATE_LIVREUR] Erreur JSON: %v", err)
- c.JSON(http.StatusBadRequest, gin.H{
- "error": "Coordonnées GPS requises",
- })
- return
- }
-
- log.Printf("📍 [VALIDATE_LIVREUR] Livreur %s valide cmd %d avec GPS: (%.6f, %.6f)",
- usernameStr, commandID, req.Latitude, req.Longitude)
-
- // ✅ ÉTAPE 1: Récupérer la commande
- command, err := database.GetCommandByID(commandID)
- if err != nil {
- log.Printf("❌ [VALIDATE_LIVREUR] Commande non trouvée")
- c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"})
- return
- }
-
- // ✅ ÉTAPE 2: VÉRIFIER PROPRIÉTÉ
- livreurAssign, _ := command["livreur_assign"].(string)
- if livreurAssign != usernameStr {
- log.Printf("❌ [VALIDATE_LIVREUR] ⚠️ TENTATIVE D'ACCÈS NON AUTORISÉ!")
- c.JSON(http.StatusForbidden, gin.H{
- "error": "Cette commande ne vous est pas assignée",
- })
- return
- }
-
- // ÉTAPE 3: Coordonnées GPS reçues et valides
- log.Printf("📍 [VALIDATE_LIVREUR] GPS reçu: (%.6f, %.6f)", req.Latitude, req.Longitude)
-
- // ÉTAPE 4: Sauvegarder les coordonnées du livreur
- _, err = database.Exec(
- "UPDATE commandes SET livreur_latitude = $1, livreur_longitude = $2 WHERE id = $3",
- req.Latitude, req.Longitude, commandID,
- )
- if err != nil {
- log.Printf("⚠️ [VALIDATE_LIVREUR] Erreur sauvegarde GPS: %v", err)
- }
-
- // ✅ ÉTAPE 5: Marquer la livraison comme "livre"
- if err := database.UpdateCommandStatus(commandID, "livre"); err != nil {
- log.Printf("❌ [VALIDATE_LIVREUR] Erreur update statut: %v", err)
- c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur validation",
- })
- return
- }
-
- // ✅ ÉTAPE 6: Ajouter un log
- database.AddCommandLog(commandID, "livre",
- fmt.Sprintf("Livraison confirmée par livreur - GPS: (%.6f, %.6f)", req.Latitude, req.Longitude),
- usernameStr)
-
- // ✅ ÉTAPE 7: Optimiser la queue
- log.Printf("📦 [VALIDATE_LIVREUR] Optimisation queue de %s...", usernameStr)
- err = database.CompleteDeliveryAndProcessNext(usernameStr, commandID)
- if err != nil {
- log.Printf("⚠️ [VALIDATE_LIVREUR] Erreur optimisation: %v", err)
- }
-
- log.Printf("✅ [VALIDATE_LIVREUR] Commande %d validée et marquée 'livre'", commandID)
-
- c.JSON(http.StatusOK, gin.H{
- "success": true,
- "message": "Livraison validée avec succès",
- "command_id": commandID,
- "new_status": "livre",
- "gps_verified": true,
- })
-}
-
-// ============================================
-// 2️⃣ VÉRIFIER SI LE LIVREUR PEUT VALIDER (SANS VALIDER)
-// ============================================
-
-// CheckDeliveryValidationEligibility vérifie si le livreur peut valider une livraison
-// GET /api/v1/deliveries/:id/can-validate
-func CheckDeliveryValidationEligibility(c *gin.Context) {
- database := c.MustGet("database").(*db.Database)
-
- username, exists := c.Get("username")
- if !exists {
- c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
- return
- }
-
- userRole := c.GetString("role")
- if userRole != "livreur" {
- c.JSON(http.StatusForbidden, gin.H{"error": "Accès réservé aux livreurs"})
- return
- }
-
- commandID, err := strconv.Atoi(c.Param("id"))
- if err != nil {
- c.JSON(http.StatusBadRequest, gin.H{"error": "ID de commande invalide"})
- return
- }
-
- command, err := database.GetCommandByID(commandID)
- if err != nil {
- c.JSON(http.StatusNotFound, gin.H{"error": "Commande non trouvée"})
- return
- }
-
- // Vérifier l'assignation
- livreurAssign, _ := command["livreur_assign"].(string)
- if livreurAssign != username.(string) {
- c.JSON(http.StatusOK, gin.H{
- "can_validate": false,
- "reason": "Commande non assignée à vous",
- })
- return
- }
-
- // Récupérer la position du livreur
- livreurLat, livreurLon, err := database.GetDeliveryPersonLocation(username.(string))
- if err != nil {
- c.JSON(http.StatusOK, gin.H{
- "can_validate": false,
- "reason": "Position GPS non disponible",
- "action": "Mettez à jour votre position GPS",
- })
- return
- }
-
- // Récupérer les coordonnées de destination (même priorité que ValidateDeliveryByLivreur)
- var destLat, destLon float64
- var coordsSource string
-
- // ✅ PRIORITÉ 1: Cache Redis
- destCacheKey := fmt.Sprintf("command:destination:%d", commandID)
- destData, redisErr := db.Redis.Get(db.RedisCtx, destCacheKey).Result()
- if redisErr == nil && destData != "" {
- var coords struct {
- Lat float64 `json:"lat"`
- Lon float64 `json:"lon"`
- }
- if err := json.Unmarshal([]byte(destData), &coords); err == nil && coords.Lat != 0 && coords.Lon != 0 {
- destLat = coords.Lat
- destLon = coords.Lon
- coordsSource = "REDIS"
- log.Printf("📍 [CAN-VALIDATE] Coords depuis Redis: (%.6f, %.6f)", destLat, destLon)
- }
- }
-
- // ✅ PRIORITÉ 2: DB
- if coordsSource == "" {
- if dLat, ok := getFloatFromMap(command, "dest_latitude"); ok && dLat != 0 {
- destLat = dLat
- }
- if dLon, ok := getFloatFromMap(command, "dest_longitude"); ok && dLon != 0 {
- destLon = dLon
- }
- if destLat != 0 && destLon != 0 {
- coordsSource = "DB"
- }
- }
-
- // ✅ PRIORITÉ 3: Géocodage
- if coordsSource == "" {
- geoService := c.MustGet("geoService").(*services.GeoService)
- address, _ := command["adresse"].(string)
- if address != "" && address != "Adresse non spécifiée" {
- location, err := geoService.GeocodeAddress(address)
- if err == nil {
- destLat = location.Latitude
- destLon = location.Longitude
- coordsSource = "GEOCODING"
- }
- }
- }
-
- if destLat == 0 || destLon == 0 {
- c.JSON(http.StatusOK, gin.H{
- "can_validate": false,
- "reason": "Coordonnées de destination non disponibles",
- })
- return
- }
-
- // Calculer la distance
- distance := services.CalculateDistance(
- services.Coordinates{Latitude: livreurLat, Longitude: livreurLon},
- services.Coordinates{Latitude: destLat, Longitude: destLon},
- )
-
- distanceMeters := distance * 1000
- canValidate := distance <= MAX_DELIVERY_VALIDATION_DISTANCE_KM
-
- c.JSON(http.StatusOK, gin.H{
- "can_validate": canValidate,
- "your_position": gin.H{
- "latitude": livreurLat,
- "longitude": livreurLon,
- },
- "destination": gin.H{
- "latitude": destLat,
- "longitude": destLon,
- "address": command["adresse"],
- "source": coordsSource,
- },
- "distance_meters": int(distanceMeters),
- "max_allowed_meters": MAX_DELIVERY_VALIDATION_DISTANCE_METERS,
- "remaining_meters": maxInt(0, int(distanceMeters)-MAX_DELIVERY_VALIDATION_DISTANCE_METERS),
- "message": func() string {
- if canValidate {
- return "Vous pouvez valider cette livraison"
- }
- return fmt.Sprintf("Rapprochez-vous de %.0f mètres pour valider", distanceMeters-float64(MAX_DELIVERY_VALIDATION_DISTANCE_METERS))
- }(),
- })
-}
-
// ============================================
// 3️⃣ DÉMARRER UNE LIVRAISON (PASSER EN IN_ROUTE)
// ============================================
@@ -395,14 +150,3 @@ func StartDelivery(c *gin.Context) {
"status": "en_route",
})
}
-
-// ============================================
-// HELPERS
-// ============================================
-
-func maxInt(a, b int) int {
- if a > b {
- return a
- }
- return b
-}
diff --git a/backend/gestion/middleware/session_middleware.go b/backend/gestion/middleware/session_middleware.go
index 0fbc8685..180523cb 100644
--- a/backend/gestion/middleware/session_middleware.go
+++ b/backend/gestion/middleware/session_middleware.go
@@ -54,7 +54,7 @@ var (
// ============================================
// validateClientToken valide un token client
-func validateClientToken(tokenString string, database *db.Database) (*ClientClaims, error) {
+func validateClientToken(tokenString string) (*ClientClaims, error) {
tokenString = strings.TrimSpace(tokenString)
if tokenString == "" {
return nil, fmt.Errorf("token vide")
@@ -103,7 +103,7 @@ func validateClientToken(tokenString string, database *db.Database) (*ClientClai
}
// validateAdminToken valide un token admin
-func validateAdminToken(tokenString string, database *db.Database) (*AdminClaims, error) {
+func validateAdminToken(tokenString string) (*AdminClaims, error) {
tokenString = strings.TrimSpace(tokenString)
if tokenString == "" {
return nil, fmt.Errorf("token vide")
@@ -161,7 +161,7 @@ func ClientMiddleware(c *gin.Context) {
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
database := c.MustGet("database").(*db.Database)
- claims, err := validateClientToken(tokenStr, database)
+ claims, err := validateClientToken(tokenStr)
if err != nil {
log.Printf("❌ [CLIENT-MWARE] Token invalide: %v", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token invalide"})
@@ -205,7 +205,7 @@ func AdminMiddleware(c *gin.Context) {
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
database := c.MustGet("database").(*db.Database)
- claims, err := validateAdminToken(tokenStr, database)
+ claims, err := validateAdminToken(tokenStr)
if err != nil {
log.Printf("❌ [ADMIN-MWARE] Token invalide: %v", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token admin invalide"})
@@ -258,7 +258,7 @@ func CabineMiddleware(c *gin.Context) {
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
database := c.MustGet("database").(*db.Database)
- claims, err := validateAdminToken(tokenStr, database)
+ claims, err := validateAdminToken(tokenStr)
if err != nil {
log.Printf("❌ [CABINE-MWARE] Token invalide: %v", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token invalide"})
@@ -312,7 +312,7 @@ func LivreurMiddleware(c *gin.Context) {
tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
database := c.MustGet("database").(*db.Database)
- claims, err := validateAdminToken(tokenStr, database)
+ claims, err := validateAdminToken(tokenStr)
if err != nil {
log.Printf("❌ [LIVREUR-MWARE] Token invalide: %v", err)
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token invalide"})
@@ -507,94 +507,3 @@ func LoginRateLimitMiddleware(c *gin.Context) {
c.Header("X-RateLimit-Remaining", strconv.FormatInt(10-count, 10))
c.Next()
}
-
-// ============================================
-// HELPER MIDDLEWARE
-// ============================================
-
-// VerifyAuthHeader vérifie que le header Authorization est valide
-func VerifyAuthHeader(c *gin.Context) {
- authHeader := c.GetHeader("Authorization")
-
- if authHeader == "" {
- log.Printf("❌ [AUTH-HEADER] Authorization header manquant")
- c.JSON(http.StatusUnauthorized, gin.H{
- "error": "Authorization header manquant",
- "hint": "Utilisez: Authorization: Bearer ",
- })
- c.Abort()
- return
- }
-
- // Vérifier le format "Bearer "
- parts := strings.Split(authHeader, " ")
- if len(parts) != 2 || parts[0] != "Bearer" {
- log.Printf("❌ [AUTH-HEADER] Format invalide: %s", authHeader)
- c.JSON(http.StatusUnauthorized, gin.H{
- "error": "Format Authorization invalide",
- "hint": "Utilisez: Authorization: Bearer ",
- })
- c.Abort()
- return
- }
-
- log.Printf("✅ [AUTH-HEADER] Format valide")
- c.Next()
-}
-
-// SessionErrorRecovery récupère les erreurs de session
-func SessionErrorRecovery(c *gin.Context) {
- defer func() {
- if err := recover(); err != nil {
- log.Printf("❌ [SESSION-ERROR] Erreur système: %v", err)
- c.JSON(http.StatusInternalServerError, gin.H{
- "error": "Erreur serveur - Session compromise",
- })
- }
- }()
-
- c.Next()
-
- if len(c.Errors) > 0 {
- log.Printf("⚠️ [SESSION] Erreur handler: %v", c.Errors)
- }
-}
-
-// LogSessionMiddleware log toutes les infos de session
-func LogSessionMiddleware(c *gin.Context) {
- username, _ := c.Get("username")
- clientID, _ := c.Get("client_id")
- sessionID, _ := c.Get("session_id")
-
- log.Printf("📊 [SESSION-LOG] %s %s | user=%v | client_id=%v | session=%v",
- c.Request.Method, c.Request.URL.Path, username, clientID, sessionID)
-
- c.Next()
-
- log.Printf("📊 [SESSION-LOG] Response: %d", c.Writer.Status())
-}
-
-// LoadClientContext charge les infos du client en contexte
-func LoadClientContext(c *gin.Context, database *db.Database) (*db.SessionData, error) {
- clientID, ok := c.Get("client_id")
- if !ok {
- return nil, fmt.Errorf("client_id manquant du contexte")
- }
-
- clientIDInt := clientID.(int)
-
- // Récupérer la session
- session, err := database.GetClientSession(clientIDInt)
- if err != nil {
- return nil, err
- }
-
- return session, nil
-}
-
-func DatabaseMiddleware(db *db.Database) gin.HandlerFunc {
- return func(c *gin.Context) {
- c.Set("database", db)
- c.Next()
- }
-}
diff --git a/backend/gestion/models/stats.go b/backend/gestion/models/stats.go
new file mode 100644
index 00000000..57da85d6
--- /dev/null
+++ b/backend/gestion/models/stats.go
@@ -0,0 +1,21 @@
+package models
+
+import "time"
+
+type WeekdayRow struct {
+ DOW int `gorm:"column:dow"`
+ Count int `gorm:"column:count"`
+}
+
+type DayRow struct {
+ Day time.Time `gorm:"column:day"`
+ Count int `gorm:"column:count"`
+}
+
+type ProductRow struct {
+ ProductID int `gorm:"column:product_id"`
+ Name string `gorm:"column:name"`
+ Quantity float64 `gorm:"column:total_quantity"`
+ OrderCount int `gorm:"column:order_count"`
+ Revenue float64 `gorm:"column:revenue"`
+}
diff --git a/backend/gestion/routes/routes.go b/backend/gestion/routes/routes.go
index 08d422ea..bc7f789d 100644
--- a/backend/gestion/routes/routes.go
+++ b/backend/gestion/routes/routes.go
@@ -139,7 +139,6 @@ func SetupRoutes(router *gin.Engine, database *db.Database, geoService *services
// ============================================
adminAuthGroupV2 := router.Group("/api/v2/admin/auth")
{
- //adminAuthGroupV2.POST("/register", handlers.RegisterAdmin)
adminAuthGroupV2.POST("/login", middleware.LoginRateLimitMiddleware, handlers.LoginAdmin)
adminAuthGroupV2.POST("/logout", handlers.LogoutAdmin)
}