From 0f42ef01b0143bbc7a8ed1af8cdb15eb3e442d40 Mon Sep 17 00:00:00 2001
From: Thomas Miceli <tho.miceli@gmail.com>
Date: Wed, 15 Mar 2023 00:52:24 +0100
Subject: [PATCH] Decrement fork counter on gist/user delete

---
 internal/models/gist.go | 14 +++++++----
 internal/models/user.go | 52 +++++++++++++++++++++++++++++------------
 internal/web/admin.go   |  2 +-
 3 files changed, 48 insertions(+), 20 deletions(-)

diff --git a/internal/models/gist.go b/internal/models/gist.go
index 0f3bca6..234d0ca 100644
--- a/internal/models/gist.go
+++ b/internal/models/gist.go
@@ -1,6 +1,7 @@
 package models
 
 import (
+	"gorm.io/gorm"
 	"time"
 )
 
@@ -41,6 +42,15 @@ type Commit struct {
 	Files     []File
 }
 
+func (g *Gist) BeforeDelete(tx *gorm.DB) error {
+	// Decrement fork counter if the gist was forked
+	err := tx.Model(&Gist{}).
+		Omit("updated_at").
+		Where("id = ?", g.ForkedID).
+		UpdateColumn("nb_forks", gorm.Expr("nb_forks - 1")).Error
+	return err
+}
+
 func GetGist(user string, gistUuid string) (*Gist, error) {
 	gist := new(Gist)
 	err := db.Preload("User").Preload("Forked.User").
@@ -141,10 +151,6 @@ func IncrementGistForkCount(gist *Gist) error {
 	return db.Model(&gist).Omit("updated_at").Update("nb_forks", gist.NbForks+1).Error
 }
 
-func DecrementGistForkCount(gist *Gist) error {
-	return db.Model(&gist).Omit("updated_at").Update("nb_forks", gist.NbForks-1).Error
-}
-
 func GetForkedGist(gist *Gist, user *User) (*Gist, error) {
 	fork := new(Gist)
 	err := db.Preload("User").
diff --git a/internal/models/user.go b/internal/models/user.go
index 042b29d..a14098b 100644
--- a/internal/models/user.go
+++ b/internal/models/user.go
@@ -1,6 +1,9 @@
 package models
 
-import "gorm.io/gorm"
+import (
+	"gorm.io/gorm"
+	"strconv"
+)
 
 type User struct {
 	ID        uint   `gorm:"primaryKey"`
@@ -14,6 +17,34 @@ type User struct {
 	Liked   []Gist   `gorm:"many2many:likes;constraint:OnUpdate:CASCADE,OnDelete:CASCADE"`
 }
 
+func (u *User) BeforeDelete(tx *gorm.DB) error {
+	// Decrement likes counter for all gists liked by this user
+	// The likes will be automatically deleted by the foreign key constraint
+	err := tx.Model(&Gist{}).
+		Omit("updated_at").
+		Where("id IN (?)", tx.
+			Select("gist_id").
+			Table("likes").
+			Where("user_id = ?", u.ID),
+		).
+		UpdateColumn("nb_likes", gorm.Expr("nb_likes - 1")).
+		Error
+	if err != nil {
+		return err
+	}
+
+	// Decrement forks counter for all gists forked by this user
+	return tx.Model(&Gist{}).
+		Omit("updated_at").
+		Where("id IN (?)", tx.
+			Select("forked_id").
+			Table("gists").
+			Where("user_id = ?", u.ID),
+		).
+		UpdateColumn("nb_forks", gorm.Expr("nb_forks - 1")).
+		Error
+}
+
 func DoesUserExists(userName string, count *int64) error {
 	return db.Table("users").
 		Where("username like ?", userName).
@@ -47,23 +78,14 @@ func CreateUser(user *User) error {
 	return db.Create(&user).Error
 }
 
-func DeleteUserByID(userid string) error {
-	// Decrement likes counter for all gists liked by this user
-	// The likes will be automatically deleted by the foreign key constraint
-	err := db.Model(&Gist{}).
-		Omit("updated_at").
-		Where("id IN (?)", db.
-			Select("gist_id").
-			Table("likes").
-			Where("user_id = ?", userid),
-		).
-		UpdateColumn("nb_likes", gorm.Expr("nb_likes - 1")).
-		Error
+func DeleteUser(userid string) error {
+	// trigger hook with a user ID
+	intId, err := strconv.ParseUint(userid, 10, 64)
 	if err != nil {
 		return err
 	}
-
-	return db.Delete(&User{}, "id = ?", userid).Error
+	var user = &User{ID: uint(intId)}
+	return db.Where("id = ?", userid).Delete(&user).Error
 }
 
 func SetAdminUser(user *User) error {
diff --git a/internal/web/admin.go b/internal/web/admin.go
index c17f399..e086a16 100644
--- a/internal/web/admin.go
+++ b/internal/web/admin.go
@@ -78,7 +78,7 @@ func adminGists(ctx echo.Context) error {
 }
 
 func adminUserDelete(ctx echo.Context) error {
-	if err := models.DeleteUserByID(ctx.Param("user")); err != nil {
+	if err := models.DeleteUser(ctx.Param("user")); err != nil {
 		return errorRes(500, "Cannot delete this user", err)
 	}