Warn before the idle-timeout logs a user out mid-form
Session timeout (30 min) only resets on a real PHP page load, but the Location QR type's address search runs entirely client-side against Nominatim, so a slow fill-in can silently expire the session and bounce the user back to login, losing the form. Adds a toast that appears a couple of minutes before expiry with a "stay logged in" button that pings session_ping.php to refresh last_activity without navigating away.
This commit is contained in:
@@ -36,3 +36,63 @@
|
|||||||
navigator.serviceWorker.register('service-worker.js');
|
navigator.serviceWorker.register('service-worker.js');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Idle-timeout warning: the session dies silently after
|
||||||
|
<?php echo SESSION_IDLE_TIMEOUT; ?> seconds of inactivity (no PHP page
|
||||||
|
load), which loses whatever form the user is filling in. This warns a
|
||||||
|
couple of minutes before that happens and offers a "stay logged in"
|
||||||
|
button that pings the server without navigating away.
|
||||||
|
-->
|
||||||
|
<div id="session-timeout-toast" class="toast" role="alert" aria-live="assertive" aria-atomic="true"
|
||||||
|
style="position:fixed;bottom:20px;right:20px;z-index:2000;min-width:320px;display:none;">
|
||||||
|
<div class="toast-header bg-warning">
|
||||||
|
<i class="fa fa-clock mr-2"></i>
|
||||||
|
<strong class="mr-auto">Session expiring soon</strong>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body bg-white">
|
||||||
|
<span id="session-timeout-message">You'll be logged out in a couple of minutes due to inactivity.</span>
|
||||||
|
<div class="mt-2">
|
||||||
|
<button type="button" id="session-timeout-extend" class="btn btn-sm btn-primary">Stay logged in</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
(function () {
|
||||||
|
var SESSION_IDLE_TIMEOUT = <?php echo (int) SESSION_IDLE_TIMEOUT; ?>;
|
||||||
|
var WARNING_LEAD_TIME = 120; // show the warning this many seconds before expiry
|
||||||
|
var toast = document.getElementById('session-timeout-toast');
|
||||||
|
var message = document.getElementById('session-timeout-message');
|
||||||
|
var extendBtn = document.getElementById('session-timeout-extend');
|
||||||
|
var warnTimer = null;
|
||||||
|
|
||||||
|
function showWarning() {
|
||||||
|
toast.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
function scheduleWarning() {
|
||||||
|
clearTimeout(warnTimer);
|
||||||
|
var delayMs = Math.max(0, (SESSION_IDLE_TIMEOUT - WARNING_LEAD_TIME) * 1000);
|
||||||
|
warnTimer = setTimeout(showWarning, delayMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
extendBtn.addEventListener('click', function () {
|
||||||
|
fetch('session_ping.php', { method: 'GET', redirect: 'manual', credentials: 'same-origin' })
|
||||||
|
.then(function (response) {
|
||||||
|
// redirect: 'manual' turns a server-side redirect (session already
|
||||||
|
// dead) into an opaque response instead of silently following it.
|
||||||
|
if (response.type === 'opaqueredirect' || !response.ok) {
|
||||||
|
throw new Error('expired');
|
||||||
|
}
|
||||||
|
toast.style.display = 'none';
|
||||||
|
scheduleWarning();
|
||||||
|
})
|
||||||
|
.catch(function () {
|
||||||
|
message.textContent = 'Your session already expired - please copy any unsaved work before reloading.';
|
||||||
|
extendBtn.style.display = 'none';
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
scheduleWarning();
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
// Lightweight keep-alive endpoint: including bootstrap.php refreshes
|
||||||
|
// $_SESSION['last_activity'], extending the idle timeout without navigating
|
||||||
|
// away from (and losing) whatever form the user is currently filling in.
|
||||||
|
// Deliberately doesn't use auth_validate.php's redirect-to-login-on-failure
|
||||||
|
// behavior: this is called from JS, and a 401 lets the caller show "your
|
||||||
|
// session already expired" instead of silently following a redirect.
|
||||||
|
require_once 'includes/bootstrap.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
if (empty($_SESSION['user_logged_in'])) {
|
||||||
|
http_response_code(401);
|
||||||
|
echo json_encode(['ok' => false]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo json_encode(['ok' => true]);
|
||||||
Reference in New Issue
Block a user