Tighten license-poll cadence; add opportunistic online refresh
Three changes that together make license state changes feel
near-instant in the UI without burning real I/O / network budget:
1. File-poll interval: 30s → 5s
Action-set keys (via "Set Recap License") get picked up almost
immediately. Cost: a single stat per file every 5s, negligible.
2. Online validation interval: 6h → 30min
A license revoked on Keysat now flips to invalid within 30 min
worst-case, instead of sitting unnoticed for hours. Bounded
latency makes revocation usable in production.
3. Opportunistic online refresh on /api/license-status
If the cached LIC was last validated more than 10 min ago, fire
validateOnline() in the background (non-blocking) when the web
UI hits the status endpoint. Since the UI hits status on every
page load, revocations get caught the next time anyone opens
the app — usually well under the scheduled 30 min tick.
Three new env vars for tuning:
RECAP_LICENSE_FILE_POLL_MS (default 5000)
RECAP_VALIDATE_INTERVAL_MS (default 1800000)
RECAP_OPPORTUNISTIC_REFRESH_MS (default 600000)
This commit is contained in:
@@ -25,15 +25,28 @@ console.log(
|
|||||||
let freeJobInFlight = false;
|
let freeJobInFlight = false;
|
||||||
|
|
||||||
// ── Online validation tunables ──────────────────────────────────────────────
|
// ── Online validation tunables ──────────────────────────────────────────────
|
||||||
|
// 30 min default scheduled cycle catches revocations / suspensions /
|
||||||
|
// expirations within at most half an hour. Bounded so a key revoked on
|
||||||
|
// Keysat doesn't sit unnoticed for hours on the customer's machine.
|
||||||
const VALIDATE_INTERVAL_MS = parseInt(
|
const VALIDATE_INTERVAL_MS = parseInt(
|
||||||
process.env.RECAP_VALIDATE_INTERVAL_MS || String(6 * 60 * 60 * 1000),
|
process.env.RECAP_VALIDATE_INTERVAL_MS || String(30 * 60 * 1000),
|
||||||
10
|
10
|
||||||
);
|
);
|
||||||
const ACTIVATE_VALIDATE_TIMEOUT_MS = 8000;
|
const ACTIVATE_VALIDATE_TIMEOUT_MS = 8000;
|
||||||
// How often to re-read the license file (fast path for keys set via the
|
// 5 s file poll is the fast path for keys set via the StartOS "Set Recap
|
||||||
// StartOS action — the 6 h online cycle is too slow for that UX).
|
// License" action — the cost is one stat call per file every 5 s, which
|
||||||
|
// is negligible.
|
||||||
const LICENSE_FILE_POLL_MS = parseInt(
|
const LICENSE_FILE_POLL_MS = parseInt(
|
||||||
process.env.RECAP_LICENSE_FILE_POLL_MS || "30000",
|
process.env.RECAP_LICENSE_FILE_POLL_MS || "5000",
|
||||||
|
10
|
||||||
|
);
|
||||||
|
// Opportunistic refresh: when /api/license-status is hit and the cached
|
||||||
|
// LIC was last validated more than this long ago, fire validateOnline in
|
||||||
|
// the background. The web UI hits license-status on every page load, so
|
||||||
|
// revocations get caught the next time anyone opens the app — usually
|
||||||
|
// well under the scheduled 30 min tick.
|
||||||
|
const OPPORTUNISTIC_REFRESH_THRESHOLD_MS = parseInt(
|
||||||
|
process.env.RECAP_OPPORTUNISTIC_REFRESH_MS || String(10 * 60 * 1000),
|
||||||
10
|
10
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -199,6 +212,21 @@ export function setupLicenseMiddleware(app) {
|
|||||||
// through unauthenticated.
|
// through unauthenticated.
|
||||||
export function setupLicenseRoutes(app) {
|
export function setupLicenseRoutes(app) {
|
||||||
app.get("/api/license-status", (_req, res) => {
|
app.get("/api/license-status", (_req, res) => {
|
||||||
|
// Opportunistic refresh: if the cached state is more than
|
||||||
|
// OPPORTUNISTIC_REFRESH_THRESHOLD_MS old, fire a validateOnline in
|
||||||
|
// the background. Doesn't block the response — the next status hit
|
||||||
|
// (or the next browser refresh) sees the updated state. Caps the
|
||||||
|
// worst-case revocation-detection latency for active users at the
|
||||||
|
// threshold value (default 10 min).
|
||||||
|
if (LIC.state === "licensed") {
|
||||||
|
const lastValidated = LIC.lastValidatedAt
|
||||||
|
? new Date(LIC.lastValidatedAt).getTime()
|
||||||
|
: 0;
|
||||||
|
const ageMs = Date.now() - lastValidated;
|
||||||
|
if (ageMs > OPPORTUNISTIC_REFRESH_THRESHOLD_MS) {
|
||||||
|
refreshLicenseOnline("opportunistic").catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
res.json(license.publicView(LIC));
|
res.json(license.publicView(LIC));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user