feat: optional 4-digit PIN asked in every new tab (not on reload), on-screen keypad for phones; set, change or remove it in the account; five misses sign out

This commit is contained in:
redtea committed 2026-10-14 21:12:09 +03:00
1 parent 29353e5e6a
commit 119b524825
14 files changed
+316 -1

No files matched your search

+42
View File
@@ -48,6 +48,13 @@
' <input type="password" name="new" placeholder="новый пароль, от 8 символов" autocomplete="new-password" minlength="8" required>' +
' <input type="password" name="again" placeholder="новый ещё раз" autocomplete="new-password" required>' +
' <button type="submit">Сменить</button></form></details>' +
' <details class="acct-pin"><summary>PIN-код <small class="acct-pinstate"></small></summary>' +
' <form class="acct-pinform">' +
' <input type="password" name="current" inputmode="numeric" maxlength="4" pattern="[0-9]{4}" placeholder="текущий PIN" autocomplete="off">' +
' <input type="password" name="pin" inputmode="numeric" maxlength="4" pattern="[0-9]{4}" placeholder="новый PIN, 4 цифры" autocomplete="off">' +
' <input type="password" name="again" inputmode="numeric" maxlength="4" pattern="[0-9]{4}" placeholder="новый ещё раз" autocomplete="off">' +
' <div class="row"><button type="button" class="btn danger small acct-pindel">Убрать PIN</button><button type="submit" class="acct-pinsave">Сохранить</button></div>' +
' </form><p class="acct-pinmsg" aria-live="polite"></p><p class="muted" style="font-size:12px;margin:8px 0 0">Спрашивается в каждой новой вкладке; при перезагрузке той же вкладки - нет.</p></details>' +
' <form method="post" action="/logout" class="acct-out"><input type="hidden" name="csrf"><button type="submit" class="btn ghost">Выйти</button></form>' +
'</div>';
document.body.appendChild(dlg);
@@ -64,6 +71,11 @@
return fetch('/api/panel/account', {cache: 'no-store'}).then(function (r) { return r.json(); }).then(function (d) {
$('.acct-name').textContent = d.username;
dlg.querySelector('.acct-pwform [name=username]').value = d.username;
const pf = dlg.querySelector('.acct-pinform');
dlg.querySelector('.acct-pinstate').textContent = d.pin ? '· установлен' : '· не задан';
pf.current.hidden = !d.pin;
pf.querySelector('.acct-pindel').hidden = !d.pin;
pf.querySelector('.acct-pinsave').textContent = d.pin ? 'Сменить' : 'Установить';
$('.acct-ava').textContent = (d.username || '?').slice(0, 1).toUpperCase();
$('.acct-mode').textContent = d.password_off ? 'вход только по ключу доступа' : d.keys.length ? 'вход по паролю или ключу' : 'вход по паролю';
const sw = $('.acct-pw .sw');
@@ -134,6 +146,35 @@
.catch(function () { msg('ошибка сети', true); })
.then(function () { f.querySelector('button').disabled = false; });
};
function pinMsg(text, bad) {
$('.acct-pinmsg').textContent = text || '';
$('.acct-pinmsg').className = 'acct-pinmsg' + (bad ? ' bad' : '');
}
function savePin(pin) {
const f = $('.acct-pinform');
return fetch('/api/panel/account/pin', {method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({pin: pin, current: f.current.value})})
.then(function (r) { return r.json(); })
.then(function (r) {
if (!r.ok) { pinMsg(r.error, true); return; }
f.reset();
// This tab stays unlocked: the PIN is for tabs opened from now on.
try { sessionStorage.setItem('lpra.pin.ok', '1'); } catch (e) { /* asked next load */ }
pinMsg(pin ? 'PIN сохранён' : 'PIN убран');
load();
})
.catch(function () { pinMsg('ошибка сети', true); });
}
$('.acct-pinform').onsubmit = function (ev) {
ev.preventDefault();
const f = ev.target;
if (!/^[0-9]{4}$/.test(f.pin.value)) { pinMsg('PIN - ровно 4 цифры', true); return; }
if (f.pin.value !== f.again.value) { pinMsg('новые PIN не совпадают', true); return; }
savePin(f.pin.value);
};
$('.acct-pindel').onclick = function () {
if (confirm('Убрать PIN-код?')) savePin('');
};
$('.acct-pw .sw').onchange = function (ev) {
const off = !ev.target.checked;
if (off && !confirm('Выключить вход по паролю? Войти можно будет только ключом доступа. Если ключи потеряются, их сможет сбросить другой администратор в «Настройках».')) {
@@ -146,6 +187,7 @@
};
btn.onclick = function () {
msg('');
pinMsg('');
load().then(function () { dlg.showModal(); dlg.focus(); });
};
})();