diff --git a/pb_migrations/1794500000_verify_link_to_web.js b/pb_migrations/1794500000_verify_link_to_web.js new file mode 100644 index 0000000..4f1c4d5 --- /dev/null +++ b/pb_migrations/1794500000_verify_link_to_web.js @@ -0,0 +1,55 @@ +/// + +// Point the verification email at the site instead of the admin console. +// +// 1794000000 translated all three account emails but deliberately left +// verification and email-change aimed at `{APP_URL}/_/#/auth/…`, on the grounds +// that "nobody has hit those yet". That was wrong on the facts: the app fires a +// verification email on every registration (auth_service.dart, fire and forget), +// and four of the nine accounts carry `verified: 1` — four family members opened +// PocketBase's admin console and clicked a button in it. +// +// Nothing is gated on `verified`: the users collection has an empty authRule, +// and five accounts including the owner's work fine unverified. The flag is kept +// because it is the only evidence an address was typed correctly, which is +// exactly the question that comes up when somebody reports a missing email. +// +// amber.petruzalekr.cz/?verify= now hosts the confirm step in Czech, the +// same way `?reset=` does. +// +// Email-change is left alone on purpose: no surface in the app or the site can +// trigger it today, so pointing it at a page nobody reaches would be motion +// without a reader. +const VERIFY_SUBJECT = "Ověření e-mailu pro Amber"; +const VERIFY_BODY = `

Ahoj,

+

Potvrď prosím tímhle odkazem, že tenhle e-mail patří tobě:

+

+ Potvrdit e-mail +

+

Není to povinné. Bez potvrzení se přihlásíš úplně stejně, jen Richard +uvidí, že adresa opravdu funguje.

+

Amber

`; + +// What 1794000000 set, so the down migration lands where it started rather than +// on PocketBase's English default. +const PREVIOUS_BODY = `

Ahoj,

+

Potvrď prosím tímhle odkazem, že tenhle e-mail patří tobě:

+

+ Potvrdit e-mail +

+

Amber

`; + +migrate( + (app) => { + const users = app.findCollectionByNameOrId("users"); + users.verificationTemplate.subject = VERIFY_SUBJECT; + users.verificationTemplate.body = VERIFY_BODY; + app.save(users); + }, + (app) => { + const users = app.findCollectionByNameOrId("users"); + users.verificationTemplate.subject = VERIFY_SUBJECT; + users.verificationTemplate.body = PREVIOUS_BODY; + app.save(users); + }, +); diff --git a/pb_public/index.html b/pb_public/index.html index f41c9af..4964616 100644 --- a/pb_public/index.html +++ b/pb_public/index.html @@ -131,6 +131,26 @@ + + + +
@@ -1417,6 +1437,41 @@ $("rSave").onclick=async function(){ setTimeout(function(){ $("reset").hidden=true; $("anon").hidden=false; }, 2500); }; +// ── confirming an email address from an emailed link ───────────────────────── + +/** The token the verification email put in the address bar, or null. */ +function verifyToken(){ + try { return new URLSearchParams(location.search).get("verify"); } + catch(_){ return null; } +} + +async function showVerify(){ + $("anon").hidden = true; + $("verify").hidden = false; + var r = await api("POST","/api/collections/users/confirm-verification", + { token: verifyToken() }, {anon:true}); + // Spend the token out of the address bar either way: it is single use, and a + // URL carrying one is a URL that ends up in a bookmark or a screenshot. + try { history.replaceState(null,"",location.pathname); } catch(_){} + if(!r.ok){ + // Worth saying plainly that nothing is broken: verification gates nothing, + // so a burnt or expired link costs the person exactly nothing. + $("vSub").textContent="Odkaz nezabral."; + setMsg($("vMsg"),"Nejspíš už vypršel. Nevadí, k přihlášení ověření "+ + "potřeba není.","err"); + } else { + $("vSub").textContent="Hotovo, e-mail je ověřený."; + setMsg($("vMsg"),"Můžeš se přihlásit.","ok"); + } + $("vDone").hidden=false; +} + +$("vDone").onclick=async function(){ + $("verify").hidden=true; + if (await restore()) { await enter(); return; } + $("anon").hidden=false; +}; + // ── boot ───────────────────────────────────────────────────────────────────── (async function(){ fillLangs(); @@ -1425,6 +1480,7 @@ $("rSave").onclick=async function(){ // trying to fix their account, and dropping them into a signed-in page they // did not ask for hides the very thing they came to do. if (resetToken()) { showReset(); return; } + if (verifyToken()) { await showVerify(); return; } if (await restore()) await enter(); })();