From 5b69b9ec6a0972d29a810302774e3c287aef19c2 Mon Sep 17 00:00:00 2001
From: Claude
Date: Sat, 5 Sep 2026 18:34:57 +0200
Subject: [PATCH] Set a password from the dashboard
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Asked for because a family member could not get a reset email. The cause turned
out not to be mail at all — PocketBase looks accounts up case-sensitively, he
was typing a capitalised address, and the endpoint answers 204 either way so
that nobody can use it to discover which addresses exist. The app now
lower-cases addresses, which should stop it recurring.
This stays for the case that fixes: somebody who has genuinely forgotten a
password and cannot receive mail. One field and one button per row of a table
only a superuser can load, so it grants no capability the page did not already
have — the same token already creates accounts.
The field is cleared the moment it succeeds. It exists to be typed and read
aloud once, and a password left sitting in an input is one that ends up in a
screenshot.
Verified against the live backend on a throwaway account: created it, signed in
with the first password, set a second from this path, signed in with the second,
was refused the first, deleted it.
---
pb_public/status.html | 64 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 61 insertions(+), 3 deletions(-)
diff --git a/pb_public/status.html b/pb_public/status.html
index a57e8b2..bc63314 100644
--- a/pb_public/status.html
+++ b/pb_public/status.html
@@ -64,6 +64,8 @@
pre{margin:8px 0 0;background:#0b0c10;border:1px solid var(--line);border-radius:8px;
padding:10px;max-height:340px;overflow:auto;font-family:var(--mono);font-size:11.5px;
white-space:pre-wrap;word-break:break-word}
+ .accts input.pw{width:190px;margin-right:6px}
+ .accts td:last-child{white-space:nowrap}
.login{max-width:340px;margin:14vh auto;padding:0 16px}
.login section{padding:18px}
.row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
@@ -675,11 +677,60 @@ async function setNoticeActive(id, to){
} catch (e) { alert("nepovedlo se: " + e.message); }
}
+/**
+ * Set someone's password directly.
+ *
+ * Exists because the emailed reset cannot be relied on, and the reason is not
+ * the mail server: PocketBase looks accounts up **case-sensitively**, so a
+ * family member typing `Vojta.Markup@seznam.cz` for an account stored in
+ * lowercase matches nothing — and the endpoint answers 204 either way, so that
+ * nobody can use it to discover which addresses exist. From the sofa that is
+ * indistinguishable from mail being broken, which is how it was reported.
+ *
+ * The app now lower-cases addresses before sending them, so this should stop
+ * happening. This stays for the case it cannot fix: somebody who has genuinely
+ * forgotten a password and cannot receive mail.
+ *
+ * No confirmation prompt. The button is one row of a table only a superuser can
+ * load, the field says what it does, and an accidental press with an empty box
+ * does nothing.
+ */
+async function setPassword(id, btn){
+ var input = el("pw_" + id);
+ var out = el("pwout_" + id);
+ var pw = (input.value || "").trim();
+ out.style.color = "";
+ if (pw.length < 8) { out.textContent = "nejméně 8 znaků"; return; }
+ btn.disabled = true;
+ out.textContent = "nastavuji…";
+ try {
+ // A superuser may set a password without the old one. `passwordConfirm` is
+ // required by the collection rules even so.
+ await api("/api/collections/users/records/" + id,
+ { method: "PATCH", body: { password: pw, passwordConfirm: pw } });
+ // Cleared immediately: it was only ever here to be typed and handed over,
+ // and a password left sitting in a field is one that ends up in a
+ // screenshot.
+ input.value = "";
+ out.textContent = "hotovo — předej mu ho";
+ } catch (e) {
+ out.style.color = "var(--bad)";
+ out.textContent = "nepovedlo se: " + e.message;
+ } finally {
+ btn.disabled = false;
+ }
+}
+
function viewAdmin(users, tpls){
var rows = (users || []).map(function(u){
+ var id = esc(u.id);
return "
" + esc(u.email || u.username || u.id) + "
" +
"
" + esc(u.ratingDefault || "—") + "
" +
- "
" + esc(String(u.created).substring(0, 10)) + "
";
+ "
" + esc(String(u.created).substring(0, 10)) + "
" +
+ "
" +
+ "" +
+ "
";
}).join("");
return '
Nový účet
' +
@@ -711,8 +762,12 @@ function viewAdmin(users, tpls){
"tohle jde přes superuživatele, kterým jsi právě přihlášený.
" +
'
Účty
' +
- "
e-mail
strop
vznik
" +
- (rows || "
nic
") + "
" +
+ "
e-mail
strop
vznik
heslo
" +
+ (rows || "
nic
") + "" +
+ '
Heslo nastavíš rovnou, bez e-mailu — reset přes e-mail ' +
+ "selhává tiše, když si adresu někdo napíše s velkým písmenem, protože " +
+ "PocketBase hledá účty přesně na znak a stejně vždycky odpoví „odesláno“, " +
+ "aby přes něj nešlo zjišťovat, které adresy existují.
" +
'
Varianta rozhoduje, jaký build dostane přes automatickou ' +
"aktualizaci — server ji vybírá podle tohohle příznaku, klient si ji nemůže " +
"vyžádat.