-
Notifications
You must be signed in to change notification settings - Fork 33
Keep-alive the session using a preloader #1686
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
fiammybe
wants to merge
18
commits into
ImpressCMS:main
Choose a base branch
from
fiammybe:keepalive-preload
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
46c4af7
Add .github/copilot-instructions.md for Copilot coding agent
Copilot 7b576ac
Revise development guidelines and PHP version limits
fiammybe 80cf3d1
Initial commit of keepalive functionality
fiammybe 5515150
Delete .github/copilot-instructions.md
fiammybe cda8977
implment no-cache headers
fiammybe 0249de9
fix intervals not happening at 5-minute intervals in some cases
fiammybe 4e72af9
Merge branch 'keepalive-preload' of https://github.com/fiammybe/impre…
fiammybe 6aca7a7
Add DOM listeners for keepalive activity
fiammybe 38602c0
remove emoji numerals
fiammybe 41105f6
remove double code
fiammybe f77c7ce
we really, really, really don't want to be cached
fiammybe 0840167
ensure the listener is only attached once per XHR instance
fiammybe d850d6b
make the keepalive work also in the ACP
fiammybe 215c7ec
change to eventAdminHeader for ACP inclusion
fiammybe b6e927a
handle errors with the XMLHTTPRequest gracefully
fiammybe 2dfc449
security hardening
fiammybe 13b66e8
No activity tracking, just session extension every X interval.
fiammybe 1fcc9c6
shortening the documentation
fiammybe File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| /* keepalive.js */ | ||
|
|
||
| document.addEventListener("DOMContentLoaded", function () { | ||
| /* 1 minute in milliseconds */ | ||
| const KEEPALIVE_INTERVAL = 1 * 60 * 1000; | ||
|
|
||
| /* Keepalive URL */ | ||
| const scriptTag = document.getElementById("keepalive-script"); | ||
| const KEEPALIVE_URL = scriptTag ? scriptTag.dataset.keepaliveUrl : ""; | ||
|
|
||
| if (!KEEPALIVE_URL) { | ||
| return; | ||
| } | ||
|
|
||
| /* Active request controller */ | ||
| let inflightController = null; | ||
|
|
||
| /* Periodic keepalive */ | ||
| setInterval(function () { | ||
| sendKeepAlive(); | ||
| }, KEEPALIVE_INTERVAL); | ||
|
|
||
| /* Send keepalive request */ | ||
| function sendKeepAlive() { | ||
| // Abort previous request | ||
| if (inflightController) { | ||
| inflightController.abort(); | ||
| } | ||
| const controller = new AbortController(); | ||
| inflightController = controller; | ||
|
|
||
| fetch(KEEPALIVE_URL, { | ||
| method: "GET", | ||
| credentials: "include", | ||
| cache: "no-store", | ||
| signal: controller.signal, | ||
| headers: { | ||
| "X-Requested-With": "XMLHttpRequest", | ||
| "Cache-Control": "no-store", | ||
| }, | ||
| }) | ||
| .then((response) => { | ||
| if (inflightController === controller) { | ||
| inflightController = null; | ||
| } | ||
| if (!response.ok) { | ||
| throw new Error("HTTP " + response.status); | ||
| } | ||
| return response.json(); | ||
| }) | ||
| .then(function () { | ||
| // Consume response | ||
| }) | ||
| .catch(() => { | ||
| if (inflightController === controller) { | ||
| inflightController = null; | ||
| } | ||
| // Ignore errors | ||
| }); | ||
| } | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| <?php | ||
| // htdocs/keepalive.php | ||
|
|
||
| // Bootstrap ImpressCMS | ||
| require_once __DIR__ . "/mainfile.php"; | ||
|
|
||
| /* GET requests only */ | ||
| if ($_SERVER['REQUEST_METHOD'] !== 'GET') { | ||
| http_response_code(405); | ||
| header("Allow: GET"); | ||
| header("Content-Type: application/json"); | ||
| echo json_encode(["error" => "Method not allowed"]); | ||
| exit(); | ||
| } | ||
|
|
||
| /* X-Requested-With header check */ | ||
| if (xoops_getenv('HTTP_X_REQUESTED_WITH') !== 'XMLHttpRequest') { | ||
| http_response_code(400); | ||
| header("Content-Type: application/json"); | ||
| echo json_encode(["error" => "Invalid request"]); | ||
| exit(); | ||
| } | ||
|
|
||
| /* Referer validation */ | ||
| $_keepaliveReferer = xoops_getenv('HTTP_REFERER'); | ||
| if ($_keepaliveReferer !== '' && strpos($_keepaliveReferer, ICMS_URL) !== 0) { | ||
| http_response_code(403); | ||
| header("Content-Type: application/json"); | ||
| echo json_encode(["error" => "Invalid request"]); | ||
| exit(); | ||
| } | ||
| unset($_keepaliveReferer); | ||
|
|
||
| /* Authenticated user only */ | ||
| if (!is_object(icms::$user) || icms::$user->isGuest()) { | ||
| http_response_code(403); | ||
| header("Content-Type: application/json"); | ||
| echo json_encode(["error" => "Not authenticated"]); | ||
| exit(); | ||
| } | ||
|
|
||
| /* Session rate limit */ | ||
| define('KEEPALIVE_MIN_INTERVAL', 60); | ||
| if ( | ||
| isset($_SESSION['keepalive_last']) && | ||
| (time() - (int) $_SESSION['keepalive_last']) < KEEPALIVE_MIN_INTERVAL | ||
| ) { | ||
| $_keepaliveRetryAfter = KEEPALIVE_MIN_INTERVAL - (time() - (int) $_SESSION['keepalive_last']); | ||
| http_response_code(429); | ||
| header("Content-Type: application/json"); | ||
| header("Retry-After: " . $_keepaliveRetryAfter); | ||
| echo json_encode(["error" => "Too many requests"]); | ||
| exit(); | ||
| } | ||
| $_SESSION['keepalive_last'] = time(); | ||
|
|
||
| /* Response headers */ | ||
| header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); | ||
| header("Pragma: no-cache"); | ||
| header("Expires: 0"); | ||
| header("X-Content-Type-Options: nosniff"); | ||
|
|
||
| /* JSON response */ | ||
| header("Content-Type: application/json"); | ||
| echo json_encode(["status" => "ok"]); | ||
| exit(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| <?php | ||
| // preload/keepalive.php | ||
|
|
||
| defined("ICMS_ROOT_PATH") || die("ImpressCMS root path not defined"); | ||
|
|
||
| class IcmsPreloadKeepalive extends icms_preload_Item | ||
| { | ||
| public function eventBeforeFooter() | ||
| { | ||
| global $xoTheme; | ||
| /* ------------------------------------------------------------------ | ||
| * Make sure a user object exists and is not a guest. | ||
| * ------------------------------------------------------------------*/ | ||
| // The core may still be booting, so icms::$user can be null. | ||
| // We guard against that before calling isGuest(). | ||
| if ( | ||
| !isset(icms::$user) || // no user object yet | ||
| !is_object(icms::$user) || // defensive – just in case | ||
| icms::$user->isGuest() // guest users are ignored | ||
| ) { | ||
| return; | ||
| } | ||
|
|
||
| /* ------------------------------------------------------------------ | ||
| * Register the external script, attaching a unique id | ||
| * and the keep‑alive endpoint as a data attribute. | ||
| */ | ||
| $keepaliveUrl = ICMS_URL . "/keepalive.php"; | ||
|
|
||
| $xoTheme->addScript( | ||
| "assets/js/keepalive.js", | ||
| [ | ||
| "id" => "keepalive-script", | ||
| "data-keepalive-url" => $keepaliveUrl, | ||
| ], | ||
| "", // no inline content | ||
| "module", | ||
| 0, // default weight | ||
| ); | ||
| } | ||
|
|
||
| public function eventAdminHeader() | ||
| { | ||
| $this->eventBeforeFooter(); | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.