chore: fix UI
This commit is contained in:
@@ -81,6 +81,11 @@ func RegisterClient(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Sanitize text inputs
|
||||
req.Username = utils.StripHTML(req.Username)
|
||||
req.Nom = utils.StripHTML(req.Nom)
|
||||
req.Prenom = utils.StripHTML(req.Prenom)
|
||||
|
||||
// Validation téléphone
|
||||
if !utils.ValidatePhoneNumber(req.Telephone) {
|
||||
log.Printf("❌ [REGISTER_CLIENT] Téléphone invalide: %s", req.Telephone)
|
||||
@@ -180,6 +185,11 @@ func AdminCreateClient(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Sanitize text inputs
|
||||
req.Username = utils.StripHTML(req.Username)
|
||||
req.Nom = utils.StripHTML(req.Nom)
|
||||
req.Prenom = utils.StripHTML(req.Prenom)
|
||||
|
||||
if !utils.ValidatePhoneNumber(req.Telephone) {
|
||||
log.Printf("❌ [ADMIN_CREATE_CLIENT] Téléphone invalide: %q", req.Telephone)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Numéro de téléphone invalide"})
|
||||
|
||||
@@ -50,7 +50,7 @@ func IPNWebhook(c *gin.Context) {
|
||||
payAmount, _ := payload.PayAmount.Float64()
|
||||
if err := database.UpdateCryptoPaymentStatus(payment.ID, payload.PaymentStatus, payAmount); err != nil {
|
||||
log.Printf("[IPN] erreur mise à jour paiement %d: %v", payment.ID, err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur base de données"})
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "erreur base de données"})
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -754,8 +754,26 @@ func ApplyClientPenalty(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Vérifier si amende == solde parrainage → compensation automatique
|
||||
referralBalance, err := database.GetClientReferralBalance(req.Username)
|
||||
if err == nil && float64(req.Points) == referralBalance && referralBalance > 0 {
|
||||
if err := database.DebitReferralBalance(req.Username, referralBalance); err != nil {
|
||||
utils.ServerErr(c, "Erreur débit solde parrainage", err)
|
||||
return
|
||||
}
|
||||
log.Printf("🔄 [PENALTY] Compensation parrainage pour %s: amende %d€ annulée, solde parrainage %.2f€ débité par %s",
|
||||
req.Username, req.Points, referralBalance, adminUsername)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"username": req.Username,
|
||||
"compensated": true,
|
||||
"message": "Amende annulée — solde parrainage débité en compensation",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// ✅ APPLIQUER PÉNALITÉ
|
||||
err := database.AddClientPenalty(req.Username, req.Points)
|
||||
err = database.AddClientPenalty(req.Username, req.Points)
|
||||
if err != nil {
|
||||
log.Printf("❌ [PENALTY] Erreur: %v", err)
|
||||
|
||||
@@ -1055,6 +1073,97 @@ 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" {
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "Accès refusé"})
|
||||
return
|
||||
}
|
||||
|
||||
username := c.Param("username")
|
||||
if username == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Username requis"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
PoolKey string `json:"pool_key" binding:"required"`
|
||||
Points int `json:"points" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Données invalides"})
|
||||
return
|
||||
}
|
||||
if req.Points <= 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Le nombre de points à retirer doit être positif"})
|
||||
return
|
||||
}
|
||||
|
||||
database := c.MustGet("database").(*db.Database)
|
||||
|
||||
settings, err := database.GetSettings()
|
||||
if err != nil {
|
||||
utils.ServerErr(c, "Erreur récupération paramètres", err)
|
||||
return
|
||||
}
|
||||
poolExists := false
|
||||
for _, pool := range settings.PointsPools {
|
||||
if pool.Key == req.PoolKey {
|
||||
poolExists = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !poolExists {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Pool de points invalide"})
|
||||
return
|
||||
}
|
||||
|
||||
// Récupérer le client pour vérifier le solde actuel
|
||||
client, err := database.GetClientByUsername(username)
|
||||
if err != nil || client == nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Client non trouvé"})
|
||||
return
|
||||
}
|
||||
|
||||
current := client.PointsExtra[req.PoolKey]
|
||||
|
||||
// Plancher à 0
|
||||
toSubtract := req.Points
|
||||
if toSubtract > current {
|
||||
toSubtract = current
|
||||
}
|
||||
|
||||
if toSubtract == 0 {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Aucun point à retirer (solde déjà à 0)",
|
||||
"username": username,
|
||||
"pool_key": req.PoolKey,
|
||||
"points": 0,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.AddClientPointsByCategory(username, -toSubtract, req.PoolKey); err != nil {
|
||||
utils.ServerErr(c, "Erreur retrait de points", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("✅ [SUBTRACT_POINTS] %d points (pool=%s) retirés à %s par %s",
|
||||
toSubtract, req.PoolKey, username, c.GetString("username"))
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": "Points retirés avec succès",
|
||||
"username": username,
|
||||
"pool_key": req.PoolKey,
|
||||
"points": toSubtract,
|
||||
})
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// STATISTIQUES TEMPS RÉEL
|
||||
// ============================================
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
@@ -33,7 +32,7 @@ func UpdateMyProfile(c *gin.Context) {
|
||||
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",
|
||||
"error": "Données invalides",
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -50,13 +49,21 @@ func UpdateMyProfile(c *gin.Context) {
|
||||
hasChanges := false
|
||||
|
||||
// Mise à jour du username
|
||||
if req.Username != "" {
|
||||
req.Username = utils.StripHTML(req.Username)
|
||||
}
|
||||
if req.Username != "" && req.Username != client.Username {
|
||||
// Vérifier que le nouveau username n'existe pas
|
||||
// Vérifier que le nouveau username n'existe pas (clients et users)
|
||||
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
|
||||
}
|
||||
if existingUser, _ := database.GetUserByUsername(req.Username); existingUser != nil {
|
||||
log.Printf("❌ [UPDATE_MY_PROFILE] Username réservé: %s", req.Username)
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "Nom d'utilisateur déjà utilisé"})
|
||||
return
|
||||
}
|
||||
client.Username = req.Username
|
||||
hasChanges = true
|
||||
}
|
||||
@@ -78,22 +85,28 @@ func UpdateMyProfile(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Mise à jour du nom
|
||||
if req.Nom != "" {
|
||||
req.Nom = utils.StripHTML(req.Nom)
|
||||
}
|
||||
if req.Nom != "" && req.Nom != client.Nom {
|
||||
if len(strings.TrimSpace(req.Nom)) < 2 {
|
||||
if len(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)
|
||||
client.Nom = req.Nom
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
// Mise à jour du prénom
|
||||
if req.Prenom != "" {
|
||||
req.Prenom = utils.StripHTML(req.Prenom)
|
||||
}
|
||||
if req.Prenom != "" && req.Prenom != client.Prenom {
|
||||
if len(strings.TrimSpace(req.Prenom)) < 2 {
|
||||
if len(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)
|
||||
client.Prenom = req.Prenom
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
@@ -184,7 +197,7 @@ func UpdateClientByAdmin(c *gin.Context) {
|
||||
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",
|
||||
"error": "Données invalides",
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -208,6 +221,9 @@ func UpdateClientByAdmin(c *gin.Context) {
|
||||
hasChanges := false
|
||||
|
||||
// Mise à jour du username
|
||||
if req.Username != "" {
|
||||
req.Username = utils.StripHTML(req.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 {
|
||||
@@ -238,15 +254,21 @@ func UpdateClientByAdmin(c *gin.Context) {
|
||||
}
|
||||
|
||||
// Mise à jour du nom
|
||||
if req.Nom != "" {
|
||||
req.Nom = utils.StripHTML(req.Nom)
|
||||
}
|
||||
if req.Nom != "" && req.Nom != client.Nom {
|
||||
client.Nom = strings.TrimSpace(req.Nom)
|
||||
client.Nom = req.Nom
|
||||
hasChanges = true
|
||||
log.Printf("✏️ [UPDATE_CLIENT_ADMIN] Nom modifié: %s", req.Nom)
|
||||
}
|
||||
|
||||
// Mise à jour du prénom
|
||||
if req.Prenom != "" {
|
||||
req.Prenom = utils.StripHTML(req.Prenom)
|
||||
}
|
||||
if req.Prenom != "" && req.Prenom != client.Prenom {
|
||||
client.Prenom = strings.TrimSpace(req.Prenom)
|
||||
client.Prenom = req.Prenom
|
||||
hasChanges = true
|
||||
log.Printf("✏️ [UPDATE_CLIENT_ADMIN] Prénom modifié: %s", req.Prenom)
|
||||
}
|
||||
@@ -277,10 +299,16 @@ func UpdateClientByAdmin(c *gin.Context) {
|
||||
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 req.Amende != nil {
|
||||
if *req.Amende < 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "L'amende ne peut pas être négative"})
|
||||
return
|
||||
}
|
||||
if *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 {
|
||||
@@ -353,13 +381,21 @@ func UpdateUserByAdmin(c *gin.Context) {
|
||||
hasChanges := false
|
||||
|
||||
// Mise à jour du username
|
||||
if req.Username != "" {
|
||||
req.Username = utils.StripHTML(req.Username)
|
||||
}
|
||||
if req.Username != "" && req.Username != user.Username {
|
||||
// Vérifier que le nouveau username n'existe pas
|
||||
// Vérifier que le nouveau username n'existe pas (users et clients)
|
||||
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
|
||||
}
|
||||
if existingClient, _ := database.GetClientByUsername(req.Username); existingClient != nil {
|
||||
log.Printf("❌ [UPDATE_USER_ADMIN] Username réservé: %s", req.Username)
|
||||
c.JSON(http.StatusConflict, gin.H{"error": "Nom d'utilisateur déjà utilisé"})
|
||||
return
|
||||
}
|
||||
user.Username = req.Username
|
||||
hasChanges = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user