@qrvey/health-checker
Version:
 
151 lines • 6.55 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.handleRuntimeHealth = void 0;
exports.createRuntimeHealthMiddlewareCacheState = createRuntimeHealthMiddlewareCacheState;
const runtimeHealth_service_1 = require("../../services/runtimeHealth.service");
const constants_1 = require("../../utils/constants");
const runtimeHealth_constants_1 = require("../../utils/runtimeHealth.constants");
const runtimeHealth_constants_2 = require("../../utils/runtimeHealth.constants");
const logger_1 = __importDefault(require("../../utils/logger"));
const handleRuntimeHealth = async (req, res, options, cache) => {
if (req.method === constants_1.HTTP_METHOD_OPTIONS) {
return true;
}
const resolvedOptions = resolveStartOptions(options);
if (!isRequestTargeted(req, resolvedOptions)) {
return true;
}
try {
const result = await getRuntimeHealthStatusWithCache(resolvedOptions.runtimeOptions, resolvedOptions.cacheTtlMs, cache);
if (result.status === constants_1.FAILED) {
const cacheLog = runtimeHealth_constants_1.DEFAULT_RUNTIME_HEALTH_LOG_GET && result.details.cache
? { cache: result.details.cache }
: {};
logger_1.default.warn('[RuntimeHealthMiddleware] RuntimeHealth is unhealthy', Object.assign({ path: req.path, method: req.method, failedMetrics: result.details.failedMetrics }, cacheLog));
res.sendJson(constants_1.HTTP_STATUS_TOO_MANY_REQUESTS, result);
return false;
}
if (runtimeHealth_constants_1.DEFAULT_RUNTIME_HEALTH_LOG_GET &&
result.details.cache &&
result.details.cache.source !== runtimeHealth_constants_2.RUNTIME_HEALTH_CACHE_SOURCE.computed) {
logger_1.default.info('[RuntimeHealthMiddleware] RuntimeHealth from cache', {
path: req.path,
method: req.method,
cache: result.details.cache,
});
}
return true;
}
catch (err) {
logger_1.default.error('[RuntimeHealthMiddleware] RuntimeHealth check failed', {
error: err instanceof Error ? err.message : String(err),
path: req.path,
method: req.method,
});
res.sendJson(constants_1.HTTP_STATUS_TOO_MANY_REQUESTS, {
status: constants_1.FAILED,
details: { reason: constants_1.RUNTIME_HEALTH_ERROR_REASON },
});
return false;
}
};
exports.handleRuntimeHealth = handleRuntimeHealth;
function createRuntimeHealthMiddlewareCacheState() {
return {};
}
function isRequestTargeted(req, resolvedOptions) {
if (resolvedOptions.serviceNames.length === 0) {
return true;
}
const serviceName = getHeaderValue(req.headers, resolvedOptions.serviceNameHeader);
return !!serviceName && resolvedOptions.serviceNames.includes(serviceName);
}
function resolveStartOptions(options) {
var _a, _b, _c, _d;
if (!options) {
return {
runtimeOptions: undefined,
serviceNames: [],
serviceNameHeader: constants_1.DEFAULT_SERVICE_NAME_HEADER,
cacheTtlMs: runtimeHealth_constants_1.DEFAULT_RUNTIME_HEALTH_CACHE_TTL_MS,
};
}
if (isRuntimeHealthMiddlewareStartOptions(options)) {
return {
runtimeOptions: options.runtimeOptions,
serviceNames: (_a = options.serviceNames) !== null && _a !== void 0 ? _a : [],
serviceNameHeader: (_c = (_b = options.serviceNameHeader) === null || _b === void 0 ? void 0 : _b.toLowerCase()) !== null && _c !== void 0 ? _c : constants_1.DEFAULT_SERVICE_NAME_HEADER,
cacheTtlMs: (_d = options.cacheTtlMs) !== null && _d !== void 0 ? _d : runtimeHealth_constants_1.DEFAULT_RUNTIME_HEALTH_CACHE_TTL_MS,
};
}
return {
runtimeOptions: options,
serviceNames: [],
serviceNameHeader: constants_1.DEFAULT_SERVICE_NAME_HEADER,
cacheTtlMs: runtimeHealth_constants_1.DEFAULT_RUNTIME_HEALTH_CACHE_TTL_MS,
};
}
function isRuntimeHealthMiddlewareStartOptions(options) {
return ('runtimeOptions' in options ||
'serviceNames' in options ||
'serviceNameHeader' in options ||
'cacheTtlMs' in options);
}
function getHeaderValue(headers, headerName) {
const value = headers[headerName];
if (Array.isArray(value)) {
return value[0];
}
return typeof value === 'string' ? value : undefined;
}
async function getRuntimeHealthStatusWithCache(runtimeOptions, cacheTtlMs, cache = {}) {
const now = Date.now();
if (isCacheValid(cache, now, cacheTtlMs)) {
return withCacheInfo(cache.cachedStatus, {
source: runtimeHealth_constants_2.RUNTIME_HEALTH_CACHE_SOURCE.cached,
ttlMs: cacheTtlMs,
ageMs: now - cache.cachedAtMs,
cachedAt: toISOStringOrUndefined(cache.cachedAtMs),
});
}
if (cache.inFlight) {
const status = await cache.inFlight;
return withCacheInfo(status, {
source: runtimeHealth_constants_2.RUNTIME_HEALTH_CACHE_SOURCE.in_flight,
ttlMs: cacheTtlMs,
ageMs: cache.cachedAtMs != null ? Date.now() - cache.cachedAtMs : 0,
cachedAt: toISOStringOrUndefined(cache.cachedAtMs),
});
}
cache.inFlight = runtimeHealth_service_1.RuntimeHealthService.get(runtimeOptions)
.then((status) => {
cache.cachedStatus = status;
cache.cachedAtMs = Date.now();
return status;
})
.finally(() => {
cache.inFlight = undefined;
});
const status = await cache.inFlight;
return withCacheInfo(status, {
source: runtimeHealth_constants_2.RUNTIME_HEALTH_CACHE_SOURCE.computed,
ttlMs: cacheTtlMs,
ageMs: 0,
cachedAt: toISOStringOrUndefined(cache.cachedAtMs),
});
}
function isCacheValid(cache, now, cacheTtlMs) {
return (cache.cachedStatus != null &&
cache.cachedAtMs != null &&
now - cache.cachedAtMs < cacheTtlMs);
}
function toISOStringOrUndefined(ms) {
return ms != null ? new Date(ms).toISOString() : undefined;
}
function withCacheInfo(status, cacheInfo) {
return Object.assign(Object.assign({}, status), { details: Object.assign(Object.assign({}, status.details), { cache: cacheInfo }) });
}
//# sourceMappingURL=runtimeHealth.middleware.js.map