chore: fix vulenrability
This commit is contained in:
@@ -270,35 +270,68 @@ func (d *Database) DecrementProductStockByID(productID int, quantity float64) er
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteProductFromBasket supprime un produit spécifique du panier
|
||||
// DeleteProductFromBasket supprime un produit spécifique du panier et restitue le stock.
|
||||
func (d *Database) DeleteProductFromBasket(basketID int) error {
|
||||
query := `DELETE FROM baskets WHERE id = $1`
|
||||
result, err := d.Exec(query, basketID)
|
||||
tx, err := d.Begin()
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur lors de la suppression du produit: %w", err)
|
||||
return fmt.Errorf("erreur transaction: %w", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
var productID int
|
||||
var quantity float64
|
||||
err = tx.QueryRow(
|
||||
`SELECT product_id, quantity FROM baskets WHERE id = $1`,
|
||||
basketID,
|
||||
).Scan(&productID, &quantity)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur lors de la vérification des lignes affectées: %w", err)
|
||||
}
|
||||
|
||||
if rowsAffected == 0 {
|
||||
return fmt.Errorf("produit non trouvé dans le panier")
|
||||
}
|
||||
|
||||
return nil
|
||||
_, err = tx.Exec(
|
||||
`UPDATE products SET stock = stock + $1 WHERE id = $2`,
|
||||
quantity, productID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur restitution stock: %w", err)
|
||||
}
|
||||
|
||||
result, err := tx.Exec(`DELETE FROM baskets WHERE id = $1`, basketID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur lors de la suppression du produit: %w", err)
|
||||
}
|
||||
rows, _ := result.RowsAffected()
|
||||
if rows == 0 {
|
||||
return fmt.Errorf("produit non trouvé dans le panier")
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// ClearBasket vide complètement le panier d'un utilisateur
|
||||
// ClearBasket vide complètement le panier d'un utilisateur et restitue les stocks.
|
||||
func (d *Database) ClearBasket(username string) error {
|
||||
query := `DELETE FROM baskets WHERE username = $1`
|
||||
_, err := d.Exec(query, username)
|
||||
tx, err := d.Begin()
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur transaction: %w", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
_, err = tx.Exec(`
|
||||
UPDATE products p
|
||||
SET stock = stock + b.quantity
|
||||
FROM baskets b
|
||||
WHERE b.username = $1 AND b.product_id = p.id
|
||||
`, username)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur restitution stock: %w", err)
|
||||
}
|
||||
|
||||
_, err = tx.Exec(`DELETE FROM baskets WHERE username = $1`, username)
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur lors du vidage du panier: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// GetBasketTotal calcule le montant total du panier d'un utilisateur
|
||||
|
||||
@@ -89,7 +89,6 @@ func ValidateStatuses(statuses string) ([]string, error) {
|
||||
"arrived": true,
|
||||
"livre": true,
|
||||
"approved": true,
|
||||
"failed": true,
|
||||
"cancelled": true,
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ func (d *Database) ProcessNextCommandForDeliveryman(deliveryman string) error {
|
||||
log.Printf("📊 [NEXT_COMMAND] Commande %d: statut = '%s'", commandID, currentStatus)
|
||||
|
||||
// ✅ Si la commande n'est plus assignable, la retirer et passer à la suivante
|
||||
nonAssignableStatuses := []string{"livre", "approved", "cancelled", "disabled", "failed"}
|
||||
nonAssignableStatuses := []string{"livre", "approved", "cancelled", "disabled"}
|
||||
isNonAssignable := false
|
||||
for _, s := range nonAssignableStatuses {
|
||||
if currentStatus == s {
|
||||
|
||||
@@ -37,6 +37,44 @@ func (d *Database) CreditClientReferral(username string, amount float64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DebitReferralBalance déduit atomiquement le solde parrainage avant la création de commande.
|
||||
// Gère sa propre transaction avec FOR UPDATE pour éviter le double-spend concurrent.
|
||||
// Retourne une erreur si le solde est insuffisant.
|
||||
func (d *Database) DebitReferralBalance(username string, amount float64) error {
|
||||
if amount <= 0 {
|
||||
return nil
|
||||
}
|
||||
tx, err := d.Begin()
|
||||
if err != nil {
|
||||
return fmt.Errorf("erreur transaction: %w", err)
|
||||
}
|
||||
var balance float64
|
||||
if err := tx.QueryRow(
|
||||
`SELECT referral_balance FROM clients WHERE username = $1 FOR UPDATE`,
|
||||
username,
|
||||
).Scan(&balance); err != nil {
|
||||
tx.Rollback()
|
||||
return fmt.Errorf("client non trouvé")
|
||||
}
|
||||
if balance < amount {
|
||||
tx.Rollback()
|
||||
return fmt.Errorf("solde parrainage insuffisant (disponible: %.2f€)", balance)
|
||||
}
|
||||
if _, err := tx.Exec(
|
||||
`UPDATE clients SET referral_balance = referral_balance - $1 WHERE username = $2`,
|
||||
amount, username,
|
||||
); err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
// RestoreReferralBalance restaure le solde parrainage si la commande échoue après le débit.
|
||||
func (d *Database) RestoreReferralBalance(username string, amount float64) error {
|
||||
return d.CreditClientReferral(username, amount)
|
||||
}
|
||||
|
||||
// UseClientReferralBalance déduit un montant du solde parrainage dans une transaction.
|
||||
// Retourne une erreur si le solde est insuffisant.
|
||||
func (d *Database) UseClientReferralBalance(tx *sql.Tx, username string, amount float64) error {
|
||||
|
||||
Reference in New Issue
Block a user