Implement user account deletion flow
POST /user/delete (auth required) verifies the password via pass1 and transactionally removes the user's sessions, votes, entries and the user row, then clears the session cookie. Associated media files are removed best effort after commit. This was only a stub in the Flask original. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"database/sql"
|
||||
"math/rand/v2"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
@@ -71,6 +72,68 @@ func handleUserInfo(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// handleUserDelete löscht den eingeloggten Account dauerhaft, nach Bestätigung
|
||||
// durch das Passwort (Formularfeld pass1). Beiträge, Votes und Sessions werden
|
||||
// in einer Transaktion mitgelöscht; zugehörige Mediendateien best effort entfernt.
|
||||
func handleUserDelete(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusNotImplemented, "Account-Löschung ist noch nicht implementiert")
|
||||
uid := uidFromContext(r.Context())
|
||||
|
||||
var hash []byte
|
||||
if err := db.QueryRow(`SELECT password FROM user WHERE uid = ?`, uid).Scan(&hash); err != nil {
|
||||
writeError(w, http.StatusNotFound, "Nutzer nicht gefunden")
|
||||
return
|
||||
}
|
||||
|
||||
if bcrypt.CompareHashAndPassword(hash, []byte(r.FormValue("pass1"))) != nil {
|
||||
writeError(w, http.StatusForbidden, "Passwort ist falsch.")
|
||||
return
|
||||
}
|
||||
|
||||
// Mediendateien der Beiträge einsammeln, bevor die Zeilen verschwinden.
|
||||
var media []string
|
||||
if rows, err := db.Query(`SELECT filepath FROM entry WHERE uid = ?`, uid); err == nil {
|
||||
for rows.Next() {
|
||||
var fp string
|
||||
if rows.Scan(&fp) == nil && fp != "" && fp != "none" {
|
||||
media = append(media, fp)
|
||||
}
|
||||
}
|
||||
rows.Close()
|
||||
}
|
||||
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "Löschen fehlgeschlagen")
|
||||
return
|
||||
}
|
||||
for _, q := range []string{
|
||||
`DELETE FROM session WHERE uid = ?`,
|
||||
`DELETE FROM vote WHERE uid = ?`,
|
||||
`DELETE FROM entry WHERE uid = ?`,
|
||||
`DELETE FROM user WHERE uid = ?`,
|
||||
} {
|
||||
if _, err := tx.Exec(q, uid); err != nil {
|
||||
tx.Rollback()
|
||||
writeError(w, http.StatusInternalServerError, "Löschen fehlgeschlagen")
|
||||
return
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
writeError(w, http.StatusInternalServerError, "Löschen fehlgeschlagen")
|
||||
return
|
||||
}
|
||||
|
||||
// Erst nach erfolgreichem Commit die Dateien entfernen.
|
||||
for _, fp := range media {
|
||||
os.Remove(fp)
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "session",
|
||||
Value: "",
|
||||
Path: "/",
|
||||
Expires: time.Unix(0, 0),
|
||||
MaxAge: -1,
|
||||
})
|
||||
writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user