@paroicms/server
Version:
The ParoiCMS server
98 lines • 3.48 kB
JavaScript
import { ApiError } from "@paroicms/public-server-lib";
import { registeredSites } from "../context.js";
import { isShuttingDown } from "../maintenance/shutdown.js";
import { initializeRequestTracker, setRequestTrackerState, waitForPendingRequests, } from "../request-tracking/request-state.js";
import { loadSiteContext } from "./load-site-context.js";
const MAX_IDLE_TIME = 1000 * 60 * 20;
const promises = new Map();
const activeContextAndTimes = new Map();
export function getActiveSiteContexts() {
return Array.from(activeContextAndTimes.values()).map((item) => item.siteContext);
}
export function getActiveSiteContext(fqdn, options = {}) {
const item = activeContextAndTimes.get(fqdn);
if (item) {
updateLastUseTime(fqdn);
return item.siteContext;
}
if (options.returnsUndef)
return;
throw new Error(`should have the site-context for '${fqdn}'`);
}
export function getSiteContext(fqdn, options = {}) {
if (isShuttingDown())
throw new ApiError("Server shutting down", 503);
let promise = promises.get(fqdn);
if (promise) {
updateLastUseTime(fqdn);
}
else {
const regSite = registeredSites.get(fqdn);
if (!regSite) {
if (options.returnsUndef)
return Promise.resolve(undefined);
throw new ApiError(`Site not found: "${fqdn}"`, 404);
}
promise = wrapCreateSiteContext(regSite);
promises.set(fqdn, promise);
}
return promise;
}
export async function unloadSiteContext(siteContext, options) {
const { logger } = siteContext;
if (siteContext.status === "migration") {
await siteContext.cn.destroy();
}
else if (siteContext.status === "ready") {
setRequestTrackerState(siteContext, "gracefulShutdown");
await waitForPendingRequests(siteContext, 30_000, {
acceptOnePending: options?.inRequest,
});
await siteContext.cn.destroy();
await siteContext.textCache.close();
await siteContext.imageCache.close();
await siteContext.mediaStorage.close();
}
else {
throw new Error(`Invalid site context status "${siteContext.status}"`);
}
promises.delete(siteContext.fqdn);
activeContextAndTimes.delete(siteContext.fqdn);
logger.info("Site unloaded ↘");
}
async function wrapCreateSiteContext(regSite) {
const siteContext = await loadSiteContext(regSite);
if (siteContext.status === "ready") {
initializeRequestTracker(siteContext);
}
activeContextAndTimes.set(regSite.fqdn, {
siteContext,
lastUseTimeMs: Date.now(),
});
siteContext.logger.info("Site loaded ↗");
return siteContext;
}
function updateLastUseTime(fqdn) {
const item = activeContextAndTimes.get(fqdn);
if (item) {
item.lastUseTimeMs = Date.now();
}
}
export function watchForIdleSiteContexts() {
const intervalId = setInterval(closeIdlingSiteContexts, 1000 * 60 * 5);
intervalId.unref();
}
async function closeIdlingSiteContexts() {
const limit = Date.now() - MAX_IDLE_TIME;
for (const { lastUseTimeMs, siteContext } of activeContextAndTimes.values()) {
if (lastUseTimeMs >= limit)
continue;
try {
await unloadSiteContext(siteContext);
}
catch (error) {
siteContext.logger.error("Error unloading site", error);
}
}
}
//# sourceMappingURL=site-context.js.map