Files
projet_gestion_commande/backend/gestion/handlers/update_profile.go
T
2026-03-27 22:23:46 +01:00

450 lines
14 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================
// handlers/profile_handlers.go - VERSION CORRIGÉE
// ============================================
// Gestion des modifications de profils
package handlers
import (
"log"
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt"
"gestion/db"
"gestion/models"
"gestion/utils"
)
// ============================================
// MODIFICATION PROFIL CLIENT (PAR LE CLIENT)
// ============================================
// UpdateMyProfile permet à un client de modifier son propre profil
// PUT /api/v1/profile/update
func UpdateMyProfile(c *gin.Context) {
clientID := c.GetInt("client_id")
database := c.MustGet("database").(*db.Database)
var req models.UpdateClientProfileRequest
if err := c.ShouldBindJSON(&req); err != nil {
log.Printf("❌ [UPDATE_MY_PROFILE] Erreur binding: %v", err)
c.JSON(http.StatusBadRequest, gin.H{
"error": "Données invalides",
"details": err.Error(),
})
return
}
// Récupérer le client actuel
client, err := database.GetClientByID(clientID)
if err != nil {
log.Printf("❌ [UPDATE_MY_PROFILE] Client non trouvé: ID=%d", clientID)
c.JSON(http.StatusNotFound, gin.H{"error": "Client non trouvé"})
return
}
// Vérifier si des modifications sont demandées
hasChanges := false
// Mise à jour du username
if req.Username != "" && req.Username != client.Username {
// Vérifier que le nouveau username n'existe pas
if existingClient, _ := database.GetClientByUsername(req.Username); existingClient != nil {
log.Printf("❌ [UPDATE_MY_PROFILE] Username déjà utilisé: %s", req.Username)
c.JSON(http.StatusConflict, gin.H{"error": "Nom d'utilisateur déjà utilisé"})
return
}
client.Username = req.Username
hasChanges = true
}
// Mise à jour du mot de passe
if req.Password != "" {
if len(req.Password) < 8 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Le mot de passe doit contenir au moins 8 caractères"})
return
}
hashed, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
log.Printf("❌ [UPDATE_MY_PROFILE] Erreur bcrypt: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur traitement mot de passe"})
return
}
client.Password = string(hashed)
hasChanges = true
}
// Mise à jour du nom
if req.Nom != "" && req.Nom != client.Nom {
if len(strings.TrimSpace(req.Nom)) < 2 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Le nom doit contenir au moins 2 caractères"})
return
}
client.Nom = strings.TrimSpace(req.Nom)
hasChanges = true
}
// Mise à jour du prénom
if req.Prenom != "" && req.Prenom != client.Prenom {
if len(strings.TrimSpace(req.Prenom)) < 2 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Le prénom doit contenir au moins 2 caractères"})
return
}
client.Prenom = strings.TrimSpace(req.Prenom)
hasChanges = true
}
// Mise à jour du téléphone
if req.Telephone != "" && req.Telephone != client.Telephone {
if !utils.ValidatePhoneNumber(req.Telephone) {
log.Printf("❌ [UPDATE_MY_PROFILE] Téléphone invalide: %s", req.Telephone)
c.JSON(http.StatusBadRequest, gin.H{"error": "Numéro de téléphone invalide"})
return
}
normalizedPhone := utils.NormalizePhoneNumber(req.Telephone)
// Vérifier que le téléphone n'est pas déjà utilisé
if existingClient, _ := database.GetClientByTelephone(normalizedPhone); existingClient != nil && existingClient.ID != clientID {
log.Printf("❌ [UPDATE_MY_PROFILE] Téléphone déjà utilisé: %s", normalizedPhone)
c.JSON(http.StatusConflict, gin.H{"error": "Ce numéro de téléphone est déjà utilisé"})
return
}
client.Telephone = normalizedPhone
hasChanges = true
}
if !hasChanges {
c.JSON(http.StatusOK, gin.H{
"message": "Aucune modification détectée",
"client": sanitizeClient(client),
})
return
}
// Sauvegarder les modifications
if err := database.UpdateClient(client); err != nil {
log.Printf("❌ [UPDATE_MY_PROFILE] Erreur mise à jour: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur lors de la mise à jour"})
return
}
log.Printf("✅ [UPDATE_MY_PROFILE] Profil mis à jour: %s (ID=%d)", client.Username, client.ID)
c.JSON(http.StatusOK, gin.H{
"message": "Profil mis à jour avec succès",
"client": sanitizeClient(client),
})
}
// GetMyProfile retourne le profil du client connecté
// GET /api/v1/profile
func GetMyProfile(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
}
client, err := database.GetClientByUsername(username.(string))
if err != nil || client == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "Client non trouvé"})
return
}
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) {
database := c.MustGet("database").(*db.Database)
role, exists := c.Get("role")
if !exists || role != "admin" {
c.JSON(http.StatusForbidden, gin.H{
"error": "Accès réservé aux administrateurs",
})
return
}
// Récupérer l'ID du client à modifier
clientIDStr := c.Param("id")
clientID, err := strconv.Atoi(clientIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID client invalide"})
return
}
var req models.AdminUpdateClientRequest
if err := c.ShouldBindJSON(&req); err != nil {
log.Printf("❌ [UPDATE_CLIENT_ADMIN] Erreur binding: %v", err)
c.JSON(http.StatusBadRequest, gin.H{
"error": "Données invalides",
"details": err.Error(),
})
return
}
// ✅ LOG DEBUG - Voir ce qui est reçu
log.Printf("📝 [UPDATE_CLIENT_ADMIN] Requête reçue: %+v", req)
// Récupérer le client actuel
client, err := database.GetClientByID(clientID)
if err != nil {
log.Printf("❌ [UPDATE_CLIENT_ADMIN] Client non trouvé: ID=%d", clientID)
c.JSON(http.StatusNotFound, gin.H{"error": "Client non trouvé"})
return
}
// ✅ LOG DEBUG - État initial
log.Printf("📊 [UPDATE_CLIENT_ADMIN] État initial - Command: %d, Amende: %.2f",
client.Command, client.Amende)
// Vérifier si des modifications sont demandées
hasChanges := false
// Mise à jour du username
if req.Username != "" && req.Username != client.Username {
// Vérifier que le nouveau username n'existe pas
if existingClient, _ := database.GetClientByUsername(req.Username); existingClient != nil {
log.Printf("❌ [UPDATE_CLIENT_ADMIN] Username déjà utilisé: %s", req.Username)
c.JSON(http.StatusConflict, gin.H{"error": "Nom d'utilisateur déjà utilisé"})
return
}
client.Username = req.Username
hasChanges = true
log.Printf("✏️ [UPDATE_CLIENT_ADMIN] Username modifié: %s", req.Username)
}
// Mise à jour du mot de passe
if req.Password != "" {
if len(req.Password) < 8 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Le mot de passe doit contenir au moins 8 caractères"})
return
}
hashed, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
log.Printf("❌ [UPDATE_CLIENT_ADMIN] Erreur bcrypt: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur traitement mot de passe"})
return
}
client.Password = string(hashed)
hasChanges = true
log.Printf("✏️ [UPDATE_CLIENT_ADMIN] Mot de passe modifié")
}
// Mise à jour du nom
if req.Nom != "" && req.Nom != client.Nom {
client.Nom = strings.TrimSpace(req.Nom)
hasChanges = true
log.Printf("✏️ [UPDATE_CLIENT_ADMIN] Nom modifié: %s", req.Nom)
}
// Mise à jour du prénom
if req.Prenom != "" && req.Prenom != client.Prenom {
client.Prenom = strings.TrimSpace(req.Prenom)
hasChanges = true
log.Printf("✏️ [UPDATE_CLIENT_ADMIN] Prénom modifié: %s", req.Prenom)
}
// Mise à jour du téléphone
if req.Telephone != "" && req.Telephone != client.Telephone {
if !utils.ValidatePhoneNumber(req.Telephone) {
log.Printf("❌ [UPDATE_CLIENT_ADMIN] Téléphone invalide: %s", req.Telephone)
c.JSON(http.StatusBadRequest, gin.H{"error": "Numéro de téléphone invalide"})
return
}
normalizedPhone := utils.NormalizePhoneNumber(req.Telephone)
// Vérifier que le téléphone n'est pas déjà utilisé
if existingClient, _ := database.GetClientByTelephone(normalizedPhone); existingClient != nil && existingClient.ID != clientID {
log.Printf("❌ [UPDATE_CLIENT_ADMIN] Téléphone déjà utilisé: %s", normalizedPhone)
c.JSON(http.StatusConflict, gin.H{"error": "Ce numéro de téléphone est déjà utilisé"})
return
}
client.Telephone = normalizedPhone
hasChanges = true
log.Printf("✏️ [UPDATE_CLIENT_ADMIN] Téléphone modifié: %s", normalizedPhone)
}
if req.Command != nil && *req.Command != client.Command {
client.Command = *req.Command
hasChanges = true
log.Printf("✏️ [UPDATE_CLIENT_ADMIN] Commandes modifiées: %d → %d", client.Command, *req.Command)
}
if req.Amende != nil && *req.Amende != client.Amende {
client.Amende = *req.Amende
hasChanges = true
log.Printf("✏️ [UPDATE_CLIENT_ADMIN] Amendes modifiées: %.2f → %.2f", client.Amende, *req.Amende)
}
if !hasChanges {
log.Printf("️ [UPDATE_CLIENT_ADMIN] Aucune modification détectée pour client ID=%d", clientID)
c.JSON(http.StatusOK, gin.H{
"message": "Aucune modification détectée",
"client": sanitizeClient(client),
})
return
}
log.Printf("📊 [UPDATE_CLIENT_ADMIN] État avant save - Command: %d, Amende: %.2f",
client.Command, client.Amende)
// Sauvegarder les modifications
if err := database.UpdateClient(client); err != nil {
log.Printf("❌ [UPDATE_CLIENT_ADMIN] Erreur mise à jour: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur lors de la mise à jour"})
return
}
log.Printf("✅ [UPDATE_CLIENT_ADMIN] Client mis à jour par admin: %s (ID=%d)", client.Username, client.ID)
c.JSON(http.StatusOK, gin.H{
"message": "Client mis à jour avec succès",
"client": sanitizeClient(client),
})
}
// ============================================
// 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) {
database := c.MustGet("database").(*db.Database)
if c.GetString("role") != "admin" {
c.JSON(http.StatusForbidden, gin.H{
"error": "Accès réservé aux administrateurs",
})
return
}
// Récupérer l'ID de l'utilisateur à modifier
userIDStr := c.Param("id")
userID, err := strconv.Atoi(userIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "ID utilisateur invalide"})
return
}
var req models.UpdateUserProfileRequest
if err := c.ShouldBindJSON(&req); err != nil {
log.Printf("❌ [UPDATE_USER_ADMIN] Erreur binding: %v", err)
c.JSON(http.StatusBadRequest, gin.H{
"error": "Données invalides",
})
return
}
// Récupérer l'utilisateur actuel
user, err := database.GetUserByID(userID)
if err != nil {
log.Printf("❌ [UPDATE_USER_ADMIN] User non trouvé: ID=%d", userID)
c.JSON(http.StatusNotFound, gin.H{"error": "Utilisateur non trouvé"})
return
}
// Vérifier si des modifications sont demandées
hasChanges := false
// Mise à jour du username
if req.Username != "" && req.Username != user.Username {
// Vérifier que le nouveau username n'existe pas
if existingUser, _ := database.GetUserByUsername(req.Username); existingUser != nil {
log.Printf("❌ [UPDATE_USER_ADMIN] Username déjà utilisé: %s", req.Username)
c.JSON(http.StatusConflict, gin.H{"error": "Nom d'utilisateur déjà utilisé"})
return
}
user.Username = req.Username
hasChanges = true
}
// Mise à jour du mot de passe
if req.Password != "" {
if len(req.Password) < 8 {
c.JSON(http.StatusBadRequest, gin.H{"error": "Le mot de passe doit contenir au moins 8 caractères"})
return
}
hashed, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
if err != nil {
log.Printf("❌ [UPDATE_USER_ADMIN] Erreur bcrypt: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur traitement mot de passe"})
return
}
user.Password = string(hashed)
hasChanges = true
}
// Mise à jour du rôle
if req.Role != "" && req.Role != user.Role {
// Valider le rôle
validRoles := map[string]bool{
"admin": true,
"cabine": true,
"livreur": true,
}
if !validRoles[req.Role] {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Rôle invalide. Valeurs acceptées: admin, cabine, livreur",
})
return
}
user.Role = req.Role
hasChanges = true
}
if !hasChanges {
c.JSON(http.StatusOK, gin.H{
"message": "Aucune modification détectée",
"user": sanitizeUser(user),
})
return
}
// Sauvegarder les modifications
if err := database.UpdateUser(user); err != nil {
log.Printf("❌ [UPDATE_USER_ADMIN] Erreur mise à jour: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur lors de la mise à jour"})
return
}
log.Printf("✅ [UPDATE_USER_ADMIN] User mis à jour par admin: %s (ID=%d, Role=%s)", user.Username, user.ID, user.Role)
c.JSON(http.StatusOK, gin.H{
"message": "Utilisateur mis à jour avec succès",
"user": sanitizeUser(user),
})
}
// ============================================
// UTILITAIRES
// ============================================
func sanitizeClient(client *models.Client) gin.H {
return gin.H{
"id": client.ID,
"username": client.Username,
"nom": client.Nom,
"prenom": client.Prenom,
"telephone": client.Telephone,
"command": client.Command,
"points_extra": client.PointsExtra,
"amende": client.Amende,
}
}
func sanitizeUser(user *models.User) gin.H {
return gin.H{
"id": user.ID,
"username": user.Username,
"role": user.Role,
}
}