feat: account deletion in settings + privacy policy with deletion link

This commit is contained in:
2026-07-17 15:47:29 -04:00
parent e79505d8b4
commit aa0af5db4f
49 changed files with 1152 additions and 2 deletions
+30
View File
@@ -50,6 +50,7 @@ func (h *Handler) RegisterProtectedRoutes(r chi.Router) {
r.Get("/auth/me", h.Me)
r.Patch("/auth/me", h.UpdateProfile)
r.Put("/auth/me/password", h.ChangePassword)
r.Delete("/auth/me", h.DeleteAccount)
r.Get("/users/{userID}/profile", h.GetPublicProfile)
r.Get("/users/me/blocks", h.ListBlocks)
r.Post("/users/me/blocks", h.BlockUser)
@@ -662,3 +663,32 @@ func (h *Handler) ChangePassword(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"message": "password updated"})
}
// DeleteAccount deletes the authenticated user and all associated data.
// ponytail: cascading FK handles all DB cleanup — push subscriptions cleaned manually.
func (h *Handler) DeleteAccount(w http.ResponseWriter, r *http.Request) {
userID, ok := middleware.UserIDFromContext(r.Context())
if !ok {
http.Error(w, `{"error":"unauthorized"}`, http.StatusUnauthorized)
return
}
// Clean up push subscriptions
h.db.ExecContext(r.Context(), `DELETE FROM push_subscriptions WHERE user_id = $1`, userID)
// ON DELETE CASCADE handles everything else: sessions, tokens, messages,
// reactions, memberships, invites, bots, etc.
result, err := h.db.ExecContext(r.Context(), `DELETE FROM users WHERE id = $1`, userID)
if err != nil {
http.Error(w, `{"error":"failed to delete account"}`, http.StatusInternalServerError)
return
}
rows, _ := result.RowsAffected()
if rows == 0 {
http.Error(w, `{"error":"user not found"}`, http.StatusNotFound)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"message": "account deleted"})
}