feat: account deletion
This commit is contained in:
parent
4769760b93
commit
2db260f849
20 changed files with 785 additions and 29 deletions
|
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"git.juancwu.dev/juancwu/budgit/internal/ctxkeys"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/middleware"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/service"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/ui"
|
||||
"git.juancwu.dev/juancwu/budgit/internal/ui/components/toast"
|
||||
|
|
@ -35,7 +36,54 @@ func (h *settingsHandler) SettingsPage(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
ui.Render(w, r, pages.AppSettings(fullUser.HasPassword(), ""))
|
||||
ui.Render(w, r, pages.AppSettings(fullUser.HasPassword(), fullUser.Email, "", ""))
|
||||
}
|
||||
|
||||
func (h *settingsHandler) DeleteAccount(w http.ResponseWriter, r *http.Request) {
|
||||
user := ctxkeys.User(r.Context())
|
||||
|
||||
fullUser, err := h.userService.ByID(user.ID)
|
||||
if err != nil {
|
||||
slog.Error("failed to fetch user for account deletion", "error", err, "user_id", user.ID)
|
||||
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
confirmation := r.FormValue("confirm_email")
|
||||
reason := r.FormValue("reason")
|
||||
|
||||
err = h.userService.RequestAccountDeletion(service.RequestAccountDeletionInput{
|
||||
UserID: user.ID,
|
||||
ConfirmationEmail: confirmation,
|
||||
Reason: reason,
|
||||
IPAddress: middleware.GetClientIP(r),
|
||||
})
|
||||
if err != nil {
|
||||
slog.Warn("account deletion request failed", "error", err, "user_id", user.ID)
|
||||
|
||||
msg := "We couldn't queue your account for deletion. Please try again."
|
||||
if errors.Is(err, service.ErrEmailConfirmationMismatch) {
|
||||
msg = "The email you entered does not match your account email."
|
||||
} else if errors.Is(err, service.ErrAccountAlreadyPending) {
|
||||
// Race with another tab — just send them to the pending page.
|
||||
http.Redirect(w, r, "/account-pending-deletion", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
ui.Render(w, r, pages.AppSettings(fullUser.HasPassword(), fullUser.Email, "", msg))
|
||||
return
|
||||
}
|
||||
|
||||
slog.Info("account deletion queued", "user_id", user.ID, "email", fullUser.Email)
|
||||
http.Redirect(w, r, "/account-pending-deletion", http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (h *settingsHandler) AccountPendingDeletionPage(w http.ResponseWriter, r *http.Request) {
|
||||
user := ctxkeys.User(r.Context())
|
||||
if user == nil || !user.IsPendingDeletion() {
|
||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
ui.Render(w, r, pages.AccountPendingDeletion(*user.PendingDeletionAt))
|
||||
}
|
||||
|
||||
func (h *settingsHandler) SetPassword(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
@ -66,12 +114,12 @@ func (h *settingsHandler) SetPassword(w http.ResponseWriter, r *http.Request) {
|
|||
msg = "Password must be at least 12 characters"
|
||||
}
|
||||
|
||||
ui.Render(w, r, pages.AppSettings(fullUser.HasPassword(), msg))
|
||||
ui.Render(w, r, pages.AppSettings(fullUser.HasPassword(), fullUser.Email, msg, ""))
|
||||
return
|
||||
}
|
||||
|
||||
// Password set successfully — render page with success toast
|
||||
ui.Render(w, r, pages.AppSettings(true, ""))
|
||||
ui.Render(w, r, pages.AppSettings(true, fullUser.Email, "", ""))
|
||||
ui.RenderToast(w, r, toast.Toast(toast.Props{
|
||||
Title: "Password updated",
|
||||
Variant: toast.VariantSuccess,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ func newTestSettingsHandler(dbi testutil.DBInfo) (*settingsHandler, *service.Aut
|
|||
accountSvc := service.NewAccountService(accountRepo)
|
||||
emailSvc := service.NewEmailService(nil, "test@example.com", "http://localhost:9999", "Budgit Test", false)
|
||||
authSvc := service.NewAuthService(emailSvc, userRepo, tokenRepo, spaceSvc, accountSvc, cfg.JWTSecret, cfg.JWTExpiry, cfg.TokenMagicLinkExpiry, false, false)
|
||||
userSvc := service.NewUserService(userRepo)
|
||||
userSvc := service.NewUserService(dbi.DB, userRepo, repository.NewAccountDeletionRequestRepository(dbi.DB))
|
||||
return NewSettingsHandler(authSvc, userSvc), authSvc
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue