@mdf.js/openc2-core
Version:
MMS - API Core - OpenC2
153 lines • 5.67 kB
JavaScript
"use strict";
/**
* Copyright 2024 Mytra Control S.L. All rights reserved.
*
* Use of this source code is governed by an MIT-style license that can be found in the LICENSE file
* or at https://opensource.org/licenses/MIT.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Registry = void 0;
const core_1 = require("@mdf.js/core");
const crash_1 = require("@mdf.js/crash");
const stream_1 = require("stream");
const uuid_1 = require("uuid");
const helpers_1 = require("../../helpers");
class Registry extends stream_1.EventEmitter {
/**
* Creates a new Register instance
* @param name - Component name
* @param maxInactivityTime - Max time in minutes that a job could be pending state
* @param registerLimit - Maximum number of entries in the message register
*/
constructor(name, maxInactivityTime = helpers_1.Constants.DEFAULT_MAX_INACTIVITY_TIME, registerLimit = helpers_1.Constants.DEFAULT_MESSAGES_REGISTERS_LIMIT) {
super();
this.name = name;
this.maxInactivityTime = maxInactivityTime;
this.registerLimit = registerLimit;
/** Component identification */
this.componentId = (0, uuid_1.v4)();
/** Array of messages used as fifo register */
this.messages = [];
/** Processed jobs */
this.executedJobs = [];
/** Pending commands */
this.pendingJobs = new Map();
/** Represent the actual status of the register of jobs */
this._status = 'pass';
/**
* Check if there are pending jobs that have been pending for too long
*/
this.checkOldPendingJobs = () => {
const now = Date.now();
let newStatus = 'pass';
for (const [, job] of this.pendingJobs[Symbol.iterator]()) {
if (now - job.createdAt.getTime() > 1000 * 60 * this.maxInactivityTime) {
job.done(new crash_1.Crash(`Job cancelled after ${this.maxInactivityTime} minutes of inactivity`, this.componentId));
}
else if (now - job.createdAt.getTime() > (1000 * 60 * this.maxInactivityTime) / 2) {
newStatus = 'warn';
}
}
if (newStatus !== this._status) {
this._status = newStatus;
this.emit('status', this._status);
}
};
this.timeInterval =
(Math.max(this.maxInactivityTime, helpers_1.Constants.DEFAULT_MIN_INACTIVITY_TIME) * 60 * 1000) / 2;
}
push(item) {
if (item instanceof core_1.Jobs.JobHandler) {
this.pendingJobs.set(item.uuid, item);
}
else {
this.messages.push(item);
if (this.messages.length > this.registerLimit) {
this.messages.shift();
}
}
if (!this.interval && this.pendingJobs.size > 0) {
this.interval = setInterval(this.checkOldPendingJobs, this.timeInterval);
}
}
/** Perform the cleaning of all the resources */
clear() {
this.messages.length = 0;
this.executedJobs.length = 0;
this.pendingJobs.clear();
if (this.interval) {
clearInterval(this.interval);
this.interval = undefined;
}
}
/**
* Return the job from the register and return it
* @param uuid - Job uuid
* @returns
*/
delete(uuid) {
const job = this.pendingJobs.get(uuid);
if (job) {
this.executedJobs.push({ ...job.result(), command: job.data });
if (this.executedJobs.length > this.registerLimit) {
this.executedJobs.shift();
}
this.pendingJobs.delete(uuid);
if (this.pendingJobs.size === 0) {
clearInterval(this.interval);
this.interval = undefined;
}
return job;
}
else {
return undefined;
}
}
/** Return a resume of the pending jobs in the register */
resume() {
const pendingJobsResume = [];
for (const job of this.pendingJobs.values()) {
for (const target of Object.keys(job.data.content.target)) {
pendingJobsResume.push(`${job.createdAt.toISOString()} - ${job.data.content.action}:${target} - ${job.status}`);
}
}
return pendingJobsResume;
}
/** Return the status of the register */
get status() {
return this._status;
}
/**
* Return the status of the stream in a standard format
* @returns _check object_ as defined in the draft standard
* https://datatracker.ietf.org/doc/html/draft-inadarei-api-health-check-05
*/
get checks() {
const check = {
status: this._status,
componentId: this.componentId,
componentType: 'source',
observedValue: this.pendingJobs.size,
observedUnit: 'pending commands',
time: new Date().toISOString(),
output: this._status !== 'pass' ? this.resume() : undefined,
};
return {
[`${this.name}:commands`]: [check],
};
}
/** Fake start method used to implement the Resource interface */
async start() {
return Promise.resolve();
}
/** Fake stop method used to implement the Resource interface */
async stop() {
return Promise.resolve();
}
/** Fake close method used to implement the Resource interface */
async close() {
return Promise.resolve();
}
}
exports.Registry = Registry;
//# sourceMappingURL=Registry.js.map