Add self-service account deletion
Settings → Delete account removes the signed-in account and everything it owns. DELETE /api/me re-checks the password (guarding an unattended session), then wipes the user's events, config, sessions and user row in one transaction and removes their photos/<user_id>/ directory. The client clears the account's local cache and returns to the login screen. Bumps the service-worker cache so clients pick up the new UI. Verified: wrong password is rejected (401, data intact); correct password returns 204, invalidates the session, drops all rows to zero and removes the photo dir; the email can be re-registered afterwards. Confirmed end to end in a headless-browser run of the Settings → delete flow.
This commit is contained in:
+44
@@ -1522,6 +1522,50 @@
|
||||
settingsDialog.close();
|
||||
});
|
||||
|
||||
// ---------- delete account ----------
|
||||
const deleteAccountDialog = document.getElementById("delete-account-dialog");
|
||||
const deleteAccountPassword = document.getElementById("delete-account-password");
|
||||
const deleteAccountError = document.getElementById("delete-account-error");
|
||||
const deleteAccountConfirm = document.getElementById("delete-account-confirm");
|
||||
|
||||
document.getElementById("delete-account-btn").addEventListener("click", () => {
|
||||
settingsDialog.close();
|
||||
deleteAccountPassword.value = "";
|
||||
deleteAccountError.hidden = true;
|
||||
deleteAccountDialog.showModal();
|
||||
setTimeout(() => deleteAccountPassword.focus(), 50);
|
||||
});
|
||||
|
||||
deleteAccountConfirm.addEventListener("click", async () => {
|
||||
deleteAccountError.hidden = true;
|
||||
deleteAccountConfirm.disabled = true;
|
||||
try {
|
||||
const res = await fetch("api/me", {
|
||||
method: "DELETE",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ password: deleteAccountPassword.value }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const msg = (await res.text()).trim();
|
||||
throw new Error(res.status === 401 ? "Incorrect password" : (msg || `HTTP ${res.status}`));
|
||||
}
|
||||
// Account is gone server-side. Wipe this user's local cache before the
|
||||
// reload drops us back on the login screen.
|
||||
try {
|
||||
localStorage.removeItem(eventsKey());
|
||||
localStorage.removeItem(configKey());
|
||||
} catch { /* ignore */ }
|
||||
clearUser();
|
||||
deleteAccountDialog.close();
|
||||
location.reload();
|
||||
} catch (err) {
|
||||
deleteAccountError.textContent = err.message || "Something went wrong";
|
||||
deleteAccountError.hidden = false;
|
||||
} finally {
|
||||
deleteAccountConfirm.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
// ---------- wiring ----------
|
||||
document.querySelectorAll("button.action").forEach(btn => {
|
||||
btn.addEventListener("click", () => openNoteDialog(btn.dataset.type));
|
||||
|
||||
Reference in New Issue
Block a user