From 7a2d283379387ba83aa220d781fc41f2d5239337 Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 1 Jun 2026 13:10:26 -0400 Subject: [PATCH] feat: Add Remember Me to admin login - Added Remember Me checkbox to login form with 30-day session expiry - Sessions default to 1 day; when remember_me=true, expire in 30 days - User info stored in localStorage (persistent) vs sessionStorage (per-tab) - initAuth() checks localStorage first, then sessionStorage for user info --- admin_server.py | 6 ++++-- static/index.html | 20 +++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/admin_server.py b/admin_server.py index 1d7c145..2ead5fe 100644 --- a/admin_server.py +++ b/admin_server.py @@ -834,9 +834,11 @@ def login(body: dict): raise HTTPException(status_code=401, detail="Invalid username or password") token = _generate_token() + remember_me = body.get("remember_me", False) + expiry = "+30 days" if remember_me else "+1 day" conn.execute( - "INSERT INTO sessions (user_id, token, expires_at) VALUES (?, ?, datetime('now', '+1 day'))", - (row["id"], token), + "INSERT INTO sessions (user_id, token, expires_at) VALUES (?, ?, datetime('now', ?))", + (row["id"], token, expiry), ) conn.commit() _log_activity(conn, "login", "user", row["id"], diff --git a/static/index.html b/static/index.html index e823898..cf55bab 100644 --- a/static/index.html +++ b/static/index.html @@ -937,6 +937,9 @@
+ @@ -1822,6 +1825,7 @@ async function doLogin() { const username = document.getElementById('loginUser').value.trim(); const password = document.getElementById('loginPass').value; const loginBtn = document.getElementById('loginBtn'); + const rememberMe = document.getElementById('loginRemember').checked; if (!username || !password) { showLoginError('Please enter username and password'); return; @@ -1832,12 +1836,19 @@ async function doLogin() { const data = await api('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ username, password }), + body: JSON.stringify({ username, password, remember_me: rememberMe }), }); AppState.currentUser = { id: data.id, username: data.username, role: data.role }; AppState.authToken = data.token; localStorage.setItem('adminToken', data.token); - sessionStorage.setItem('adminUser', JSON.stringify(AppState.currentUser)); + const userInfo = JSON.stringify(AppState.currentUser); + if (rememberMe) { + localStorage.setItem('adminUser', userInfo); + sessionStorage.removeItem('adminUser'); + } else { + sessionStorage.setItem('adminUser', userInfo); + localStorage.removeItem('adminUser'); + } document.getElementById('loginOverlay').classList.add('hidden'); updateUserUI(); switchPage('dashboard'); @@ -1875,9 +1886,8 @@ async function initAuth() { try { // Verify token by fetching a protected endpoint that returns user info const users = await api('/api/users?limit=1'); - // If we got here, token is valid — but we need user info. - // The login response stored user data, try sessionStorage fallback. - const savedUser = sessionStorage.getItem('adminUser'); + // If we got here, token is valid — try localStorage first, then sessionStorage + const savedUser = localStorage.getItem('adminUser') || sessionStorage.getItem('adminUser'); if (savedUser) { AppState.currentUser = JSON.parse(savedUser); document.getElementById('loginOverlay').classList.add('hidden');