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
This commit is contained in:
+4
-2
@@ -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"],
|
||||
|
||||
+15
-5
@@ -937,6 +937,9 @@
|
||||
<div id="loginError" class="login-error"></div>
|
||||
<input id="loginUser" class="input-field" type="text" placeholder="Username" autocomplete="username">
|
||||
<input id="loginPass" class="input-field" type="password" placeholder="Password" autocomplete="current-password">
|
||||
<label style="display:flex;align-items:center;gap:6px;font-size:13px;color:var(--text2,#999);margin-top:4px;cursor:pointer;">
|
||||
<input type="checkbox" id="loginRemember" checked> Remember me (30 days)
|
||||
</label>
|
||||
<button id="loginBtn" class="btn btn-primary btn-block" onclick="doLogin()">Sign In</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user