Add user management with admin and viewer roles
- data/users.json store; existing single account migrates to first admin - Roles: admin (settings, import, users) and viewer (read-only, own password) - Live role/disabled lookup; per-user session epoch so password changes, resets and disabling sign that user out everywhere - Login brute-force lockout (5 fails -> 5 min), constant-time unknown-user check - /api/users CRUD with last-admin and self-lockout safeguards - Account page (own password + admin user management); role-aware header - Min password length 8; recovery token resets the primary admin and clears lockouts Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b3f1061f5c
commit
1566ac2897
7 files changed
+442
-93
No files matched your search
@@ -549,6 +549,24 @@ function renderInsights() {
|
||||
});
|
||||
}
|
||||
|
||||
// ── Who's signed in: role-aware header links ─────────────────────────────────
|
||||
(async function roleAwareHeader() {
|
||||
try {
|
||||
const me = await getJSON('/api/dashboard/me');
|
||||
const right = document.querySelector('.header-right');
|
||||
if (!right) return;
|
||||
const settings = right.querySelector('a[href="/dashboard/settings"]');
|
||||
if (me.role !== 'admin' && settings) settings.remove();
|
||||
const acct = document.createElement('a');
|
||||
acct.href = '/dashboard/account'; acct.className = 'logout'; acct.textContent = me.role === 'admin' ? 'Account & Users' : 'Account';
|
||||
acct.title = `Signed in as ${me.username} (${me.role})`;
|
||||
(settings && me.role === 'admin' ? settings : right.querySelector('a[href="/dashboard/logout"]'))?.before(acct);
|
||||
const nm = $('stationName');
|
||||
if (nm && !nm.textContent) nm.textContent = me.username;
|
||||
window.DASH_USER = me;
|
||||
} catch {}
|
||||
})();
|
||||
|
||||
// ── Data refresh ─────────────────────────────────────────────────────────────
|
||||
async function refreshData() {
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user