/*
Backend Infastructure Kernel
The Kernel is responsible for the initialization and build process
of the backend server. It is the first file to be executed during production
build.
Version 1.0.0
Kernel.start()
Boot the backend server & start indexing and initialization of proccesses
Kernel.register(File)
register a file or directory and wrap it with the Kernel framework
*/
const Kernel = {};
const Path = require(`path`);
const FileSystem = require(`fs`);
const Enviroment = require(`dotenv`).config();
Kernel.order = [`Libraries`, `Utilities`,`Clients`, `Modules`, `Services`]; // Boot Priority Order
Kernel.continueThroughError = true; // Should the boot process break on initialization error?
Kernel.signals = {};
Kernel.catch = async function({File, Error}) {
// Improve this at a later date, for now handle basic error logging
return console.warn(`${Path} :`, Error)
}
Kernel.get = async function(File) {
Kernel.signals[File] = {}
Kernel.signals[File].Connection = new Promise(async (resolve, reject) => {
Kernel.signals[File].resolve = resolve;
Kernel.signals[File].reject = reject;
})
return Kernel.signals[File].Connection
}
Kernel.register = async function(File, Parent) {
return new Promise(async function(resolve, reject) {
let path = Path.parse(File);
let status = await FileSystem.promises.stat(`./${File}`).catch(Kernel.catch);
let children = status.isDirectory() && await FileSystem.promises.readdir(File).catch(Kernel.catch);
if ( status && status.isDirectory() ) {
(Parent || Kernel)[path.name] = (Parent || Kernel)[path.name] || {};
for (let childrenFile of children) { await Kernel.register(`${File}/${childrenFile}`, (Parent || Kernel)[path.name]).catch((Error) => {
return console.warn(`${File} :`, Error)
})
if (Kernel.signals[`${path.name}`]) { Kernel.signals[`${path.name}`].resolve((Parent || Kernel)[path.name]) };
}
} else if ( status && status.isFile() ) {
try {
let directory = path.dir;
let base = require(`./${File}`);
let object = base && base.construct && await base.construct(Kernel).catch((Error) => {
return reject({File: File, Error: Error})
});
(Parent || Kernel)[path.name] = object || base;
if (Kernel.signals[`${File}`]) { Kernel.signals[`${File}`].resolve(object || base) };
resolve(`${File} : ${object && object.constructionSuccessMesage || 'Constructed into Kernel'}`)
} catch (Error) { reject({File: File, Error: Error}) }
}
if (Kernel.continueThroughError) { resolve(`${File} : Constructed into Kernel`) } else {
Kernel.blockExecution = true
reject({File: 'None', Error: 'None'});
}
})
}
Kernel.start = async function() {
// Initialize boot required directories
for (let Directory of Kernel.order) {
await FileSystem.promises.access(Directory)
await Kernel.register(Directory).catch(Kernel.catch);
if (Kernel.blockExecution) { break } else { continue }
}
}
class APIEndpoint {
/**
* APIEndpoint Class used for creating new endpoint callbacks.
*
* This class is used to create & customize API endpoints for usage by external programs/servers.
*
* @access private
*
* @param {string} path The path of the endpoint, /users.
* @param {object} options The options associated with this endpoint, { ... }.
* @param {function} callback The callback function called when a request is recieved to this endpoint.
* @return {APIEndpoint} APIEndpoint class
*/
constructor() {
let props = arguments;
let path = props[0] == 'object' ? props[0].path : props[0];
let options = props[0] == 'object' ? props[0].options : typeof(props[1]) == 'object' && props[1];
let callback = props[0] == 'object' ? props[0].callback : (typeof(options) == 'object' ? props[2] : props[1]);
console.assert(path && typeof(path) == 'string', `Expected argument path to be type of string, got ${typeof(path)}`);
console.assert(callback&& typeof(callback) == 'function', `Expected argument callback to be a function, got ${typeof(callback)}`);
console.assert(!options || options && typeof(options) == 'object', `Expected argument options to be of type object, got ${typeof(options)}`);
this.enabled = true; // used for automatic disabling if analytic error count reaches certain level
this.options = {};
[this.path, this.options, this._callback] = [path, options, callback];
Http.endpoints[path] = this;
}
callback(parameters, request, response) {
return new Promise(async (resolve, reject) => {
if (!this.enabled) { reject(`This endpoint is not enabled.`) };
if (this.options && this.options.token) { reject(`403, You do not have access to this endpoint.`) };
response.statusCode = 202;
response.statusMessage = `The server is processing your API request`;
this.analytics.track(async () => {
try {
try {
request.body = request.method !== 'GET' && await Http._parser(request).catch((parseError) => {}) || {};
} catch(error) {}
[response.statusCode, response._body] = await this._callback(parameters, request.body, request, response);
} catch (error) { [response.statusCode, response.statusMessage] = [500, JSON.stringify(error)]; throw error }
})
resolve(response.statusCode)
})
}
}