Skip to content

Commit

Permalink
Disable deletion for active subscription users (#3010)
Browse files Browse the repository at this point in the history
* fix: don't allow deletion of user with active subscription

* feat: disable delete button for active subscription users

* fix: show correct error message when deleting user
  • Loading branch information
LuccaBitfly authored Jan 22, 2025
1 parent a073010 commit 6ea8cab
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 32 deletions.
84 changes: 63 additions & 21 deletions handlers/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,16 @@ func UserSettings(w http.ResponseWriter, r *http.Request) {
}
}

// disable delete button if user has active subscription
hasUserActiveSubscription, err := getHasUserActiveSubscription(user.UserID)
if err != nil {
logger.Errorf("Error retrieving the active subscription for user: %v %v", user.UserID, err)
utils.SetFlash(w, r, "", "Error: Something went wrong.")
http.Redirect(w, r, "/user/settings", http.StatusSeeOther)
return
}
userSettingsData.IsUserDeleteDisabled = hasUserActiveSubscription

userSettingsData.ApiStatistics.MaxDaily = &maxDaily
userSettingsData.ApiStatistics.MaxMonthly = &maxMonthly

Expand Down Expand Up @@ -975,38 +985,70 @@ func UserAuthorizeConfirmPost(w http.ResponseWriter, r *http.Request) {
}
}

func getHasUserActiveSubscription(userId uint64) (bool, error) {
var hasUserActiveSubscription bool
err := db.FrontendReaderDB.Get(&hasUserActiveSubscription, `
SELECT EXISTS (
SELECT uss.price_id
FROM users_stripe_subscriptions uss
LEFT JOIN users u ON u.stripe_customer_id = uss.customer_id
WHERE uss.active = true AND u.id = $1
UNION
SELECT product_id
FROM users_app_subscriptions uas
LEFT JOIN users u ON u.id = uas.user_id
WHERE uas.active = true AND u.id = $1
)`, userId)
if err != nil {
return false, err
}
return hasUserActiveSubscription, nil
}

func UserDeletePost(w http.ResponseWriter, r *http.Request) {
logger := logger.WithField("route", r.URL.String())
user, session, err := getUserSession(r)
user, _, err := getUserSession(r)
if err != nil {
logger.Errorf("error retrieving session: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
if user.Authenticated {
err := db.DeleteUserById(user.UserID)
if err != nil {
logger.Errorf("error deleting user by email for user: %v %v", user.UserID, err)
http.Redirect(w, r, "/user/settings", http.StatusSeeOther)
utils.SetFlash(w, r, "", "Error: Could not delete user.")
session.Save(r, w)
http.Redirect(w, r, "/user/settings", http.StatusSeeOther)
return
}

Logout(w, r)
err = purgeAllSessionsForUser(r.Context(), user.UserID)
if err != nil {
utils.LogError(err, "error purging sessions for user", 0, map[string]interface{}{"userID": user.UserID})
utils.SetFlash(w, r, authSessionName, authInternalServerErrorFlashMsg)
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
} else {
if !user.Authenticated {
utils.LogError(nil, "Trying to delete an unauthenticated user", 0)
http.Redirect(w, r, "/user/settings", http.StatusSeeOther)
return
}
// don't allow user to delete account if they have an active subscription
hasUserActiveSubscription, err := getHasUserActiveSubscription(user.UserID)
if err != nil {
logger.Errorf("error checking if user has active subscription: %v", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
if hasUserActiveSubscription {
utils.SetFlash(w, r, authSessionName, "Error: You cannot delete your account while you have an active subscription.")
http.Redirect(w, r, "/user/settings", http.StatusSeeOther)
return
}

err = db.DeleteUserById(user.UserID)
if err != nil {
logger.Errorf("error deleting user by id for user: %v %v", user.UserID, err)
utils.SetFlash(w, r, authSessionName, "Error: Could not delete user.")
http.Redirect(w, r, "/user/settings", http.StatusSeeOther)
return
}

Logout(w, r)
err = purgeAllSessionsForUser(r.Context(), user.UserID)
if err != nil {
utils.LogError(err, "error purging sessions for user", 0, map[string]interface{}{"userID": user.UserID})
utils.SetFlash(w, r, authSessionName, authInternalServerErrorFlashMsg)
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
}

func UserUpdateFlagsPost(w http.ResponseWriter, r *http.Request) {
Expand Down
10 changes: 7 additions & 3 deletions templates/user/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -460,9 +460,13 @@ <h3 class="h5">Delete Account <i class="fas fa-exclamation-triangle text-warning
</div>
<div class="card-body">
<div class="d-flex justify-content-between">
<span> Warning, you will not be able to recover your account! </span>
<!-- Button trigger modal -->
<button type="button" class="btn btn-sm btn-outline-danger" data-toggle="modal" data-target="#deleteAccountModal">Delete</button>
{{ if .IsUserDeleteDisabled }}
<span> Warning, you will not be able to recover your account! </span>
<!-- Button trigger modal -->
<button type="button" class="btn btn-sm btn-outline-danger" data-toggle="modal" data-target="#deleteAccountModal">Delete</button>
{{ else }}
<span> You currently have an active premium subscription or premium API plan. To delete your account please cancel your subscription or contact our <a href="https://dsc.gg/beaconchain">support</a> </span>
{{ end }}
</div>
</div>
</div>
Expand Down
17 changes: 9 additions & 8 deletions types/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -1220,14 +1220,15 @@ type CsrfData struct {
type UserSettingsPageData struct {
CsrfField template.HTML
AuthData
Subscription UserSubscription
Premium UserPremiumSubscription
PairedDevices []PairedDevice
Sapphire *string
Emerald *string
Diamond *string
ShareMonitoringData bool
ApiStatistics *ApiStatistics
Subscription UserSubscription
Premium UserPremiumSubscription
PairedDevices []PairedDevice
Sapphire *string
Emerald *string
Diamond *string
ShareMonitoringData bool
ApiStatistics *ApiStatistics
IsUserDeleteDisabled bool
}

type PairedDevice struct {
Expand Down

0 comments on commit 6ea8cab

Please sign in to comment.