session keep alive
This commit is contained in:
@@ -1804,7 +1804,79 @@ $gridMeta = [
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="sessionModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:99999;align-items:center;justify-content:center">
|
||||||
|
<div style="background:#fff;padding:28px 32px;border-radius:14px;max-width:420px;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,.3)">
|
||||||
|
<h5 style="margin:0 0 10px">La sessione sta per scadere</h5>
|
||||||
|
<p style="color:#64748b;margin:0 0 6px">Per la tua sicurezza verrai disconnesso tra</p>
|
||||||
|
<div id="sessCountdownBig" style="font:700 34px monospace;color:#dc2626;margin:8px 0 18px">00:30</div>
|
||||||
|
<button id="sessStayBtn" class="btn btn-primary btn-lg px-4">Resta connesso</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
// ── TEST: tempi compressi. In produzione: SESSION_MINUTES=120, WARN_SECONDS=120
|
||||||
|
const SESSION_MINUTES = 2; // durata simulata
|
||||||
|
const WARN_SECONDS = 30; // preavviso prima della scadenza
|
||||||
|
const LOGIN_URL = '../login'; // adatta al tuo path reale
|
||||||
|
|
||||||
|
const sessionMs = SESSION_MINUTES * 60 * 1000;
|
||||||
|
let lastActivity = Date.now();
|
||||||
|
let modalShown = false;
|
||||||
|
|
||||||
|
// ogni attività reale resetta il timer locale
|
||||||
|
['mousemove', 'keydown', 'click', 'scroll', 'touchstart'].forEach(ev =>
|
||||||
|
document.addEventListener(ev, () => {
|
||||||
|
lastActivity = Date.now();
|
||||||
|
if (!modalShown) return; // se il modal è aperto, l'attività NON conta: serve il click esplicito
|
||||||
|
}, {
|
||||||
|
passive: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const modal = document.getElementById('sessionModal');
|
||||||
|
const bigCd = document.getElementById('sessCountdownBig');
|
||||||
|
|
||||||
|
document.getElementById('sessStayBtn').addEventListener('click', async () => {
|
||||||
|
try {
|
||||||
|
const r = await fetch('session_keepalive.php', {
|
||||||
|
cache: 'no-store'
|
||||||
|
});
|
||||||
|
// se la sessione era già morta, headscript reindirizza: la risposta non sarà il JSON atteso
|
||||||
|
const ct = r.headers.get('content-type') || '';
|
||||||
|
if (!ct.includes('application/json')) {
|
||||||
|
window.location.href = LOGIN_URL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await r.json();
|
||||||
|
lastActivity = Date.now(); // timer riparte
|
||||||
|
modalShown = false;
|
||||||
|
modal.style.display = 'none';
|
||||||
|
} catch (e) {
|
||||||
|
window.location.href = LOGIN_URL;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
const elapsed = Date.now() - lastActivity;
|
||||||
|
const remaining = Math.ceil((sessionMs - elapsed) / 1000);
|
||||||
|
|
||||||
|
if (remaining <= 0) {
|
||||||
|
window.location.href = LOGIN_URL; // scaduta
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (remaining <= WARN_SECONDS && !modalShown) {
|
||||||
|
modalShown = true;
|
||||||
|
modal.style.display = 'flex';
|
||||||
|
}
|
||||||
|
if (modalShown) {
|
||||||
|
const m = Math.floor(remaining / 60),
|
||||||
|
s = remaining % 60;
|
||||||
|
bigCd.textContent = String(m).padStart(2, '0') + ':' + String(s).padStart(2, '0');
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -56,4 +56,12 @@ $photousername = basename($avatar);
|
|||||||
//include files
|
//include files
|
||||||
require_once(__DIR__ . '/../../languages/en/general.php');
|
require_once(__DIR__ . '/../../languages/en/general.php');
|
||||||
|
|
||||||
//include("generalsettings.php");
|
//include("generalsettings.php");
|
||||||
|
|
||||||
|
// Timbra la sessione dell'utente a ogni pagina (rolling manuale)
|
||||||
|
try {
|
||||||
|
$pdoSess = DBHandlerSelect::getInstance()->getConnection();
|
||||||
|
$pdoSess->prepare("UPDATE auth_sessions SET last_activity = ? WHERE user_id = ? ORDER BY last_activity DESC LIMIT 1")
|
||||||
|
->execute([time(), $iduserlogin]);
|
||||||
|
} catch (\Throwable $e) { /* non bloccare la pagina */
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/include/headscript.php'; // autentica e dà $iduserlogin
|
||||||
|
require_once __DIR__ . '/class/db-functions.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
try {
|
||||||
|
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||||
|
$stmt = $pdo->prepare(
|
||||||
|
"UPDATE auth_sessions SET last_activity = ? WHERE user_id = ? ORDER BY last_activity DESC LIMIT 1"
|
||||||
|
);
|
||||||
|
$stmt->execute([time(), $iduserlogin]);
|
||||||
|
echo json_encode(['ok' => true, 'rows' => $stmt->rowCount(), 'ts' => time()]);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
echo json_encode(['ok' => false, 'error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
require_once __DIR__ . '/class/db-functions.php';
|
||||||
|
$sid = session_id(); // oppure l'id Laravel, da verificare
|
||||||
|
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||||
|
$pdo->prepare("UPDATE auth_sessions SET last_activity = ? WHERE id = ?")
|
||||||
|
->execute([time(), $sid]);
|
||||||
|
echo json_encode(['ok' => true, 'sid' => $sid, 'ts' => time()]);
|
||||||
Reference in New Issue
Block a user