Skip to content

Commit

Permalink
v0.2.5 Update:
Browse files Browse the repository at this point in the history
1. Added support for i18next for internationalisation and localisation. Haven't fully integrated it in yet to the event stream

2. Added Cheerio as an include in to the HTTP Interface module so that we can perform server-side jquery style parsing and modification of pages prior to sending them down to the client

3. Enabled Silent Mode on Core (no console output). Allows you to build Blackrock as the engine in to any application that you choose

4. Added Log Event Stream in to the Core Object (from Logger module). If Blackrock is used as the engine in another application then that application can listen to the full event stream in JSON from the entire application server and react accordingly

5. The Blackrock (Core) Object is now exported from the library include via return (immediate), promise and callback (later two are asynchronous and is have pointers to the same Blackrock object, but arrive when the underlying object (app server) switches in to Active mode

6. Added support for hooks in to the HTTP interface module. The following events are supported - onIncomingRequest, onOutgoingResponse, onOutgoingRequest and onIncomingResponse
  • Loading branch information
darrensmith committed Nov 9, 2020
1 parent 2c742e6 commit 5be3d4d
Show file tree
Hide file tree
Showing 1,304 changed files with 85,737 additions and 102 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

## Introduction

Welcome to the second-generation ISNode Node.JS Web Framework - ISNode Blackrock (Aka: "Blackrock"). This version of the ISNode Web Framework takes what was built for ISNode (v1.0) and re-architects it from the ground up to make use of a Reactive (Event-Driven) Architecture AND to combine all major ISNode modules in to a single dependency for your next project.
Welcome to the second-generation ISNode Node.JS Web Framework - "Blackrock". Blackrock has a Reactive / Event-Driven Architecture, and contains all the functionality you need to build your next Node.JS application.



Expand All @@ -20,7 +20,7 @@ Welcome to the second-generation ISNode Node.JS Web Framework - ISNode Blackrock
6. Nested Controller Schema For Each Web Service Reflective of URL Path
7. Built-In Support for Mustache Views (HTTP Controllers)
8. HTTP Module Can Pipe Filesystem Files Transparently

9. i18n Support For Internationalisation


<br/><br/>
Expand Down
201 changes: 163 additions & 38 deletions interfaces/http/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

/** Initialise Variables & Create String Prototype Method */
String.prototype.endsWith = function(suffix) {return this.indexOf(suffix, this.length - suffix.length) !== -1;};
var mustache = require('./support/mustache.js'), formidable = require('./support/formidable');
var mustache = require('./support/mustache.js'), formidable = require('./support/formidable'), cheerio = require('./support/cheerio/cheerio');
var core, interface, log, config, instances = [], client = {}, utils = {}, streamFns = {}, pipelines = {}, viewCache = {};


Expand All @@ -37,6 +37,8 @@
interface.client = client;
interface.startInterface = startInterface;
interface.get = get;
interface.addHook = addHook;
interface.removeHook = removeHook;
interface.startInterfaces();
return interface;
}
Expand Down Expand Up @@ -66,6 +68,7 @@
if(cfg.ssl) { var httpLib = "https" } else { var httpLib = "http" };
instances[name] = inst = new core.Base().extend({});
inst.listening = false;
inst.hooks = { onIncomingRequest: {}, onOutgoingResponse: {}, onOutgoingRequest: {}, onIncomingResponse: {} }, inst.hookIdDirectory = {};
var serverLib = require('./support/' + httpLib);
if(cfg.ssl) { inst.server = serverLib(cfg.key, cfg.cert) } else { inst.server = serverLib() };

Expand All @@ -81,14 +84,17 @@
verb: request.method,
url: request.url,
headers: request.headers,

}
log("debug","Blackrock HTTP Interface > Received Incoming Request", myMsg);
request.interface = name;
for (var i = 0; i < routers.length; i++) {
request.router = routers[i];
if(protocol == "HTTPS") { request.secure = true; } else { request.secure = false; }
new ISPipeline({ "req": request, "res": response }).pipe();
executeHookFns(name, "onIncomingRequest", request).then(function(output) {
new ISPipeline({ "req": output, "res": response }).pipe();
}).catch(function(err) {
new ISPipeline({ "req": request, "res": response }).pipe();
});
}
});
inst.server.listen(cfg.port, function HTTPListenHandler(){
Expand All @@ -103,11 +109,105 @@


/**
* (Internal > Init) Exports the HTTP/S Server Interface
* (Internal > Get Instances) Exports the HTTP/S Server Interface
* @param {string} name - The name of the interface
*/
var get = function HTTPGetInstances(name){
if(name) { return instances[name]; }
else { return instances; }
}

/**
* (Internal > Add Hook) Adds a Hook Function to the Defined HTTP Interface Instances
* @param {string} names - Array or String Containing Interface Name(s)
* @param {string} hookType - Type of Hook
* @param {string} hookFn - Hook Function
* @param {string} cb - Callback Function
*/
var addHook = function HTTPAddHook(names, hookType, hookFn){
return new Promise((resolve, reject) => {
var hookCount = 0, hooksSet = [];
var addNow = function(inst, hook, fn) {
var uniqueId = core.module("utilities").uuid4();
inst.hooks[hook][uniqueId] = fn;
inst.hookIdDirectory[uniqueId] = hook;
hooksSet.push(uniqueId);
}
if(Array.isArray(names)) {
hookCount = names.length;
for (var name in names) {
if(instances && instances[name])
addNow(instances[name], hookType, hookFn);
}
} else if (names == "*") {
hookCount = instances.length;
for (var name in instances) {
addNow(instances[name], hookType, hookFn);
}
} else if (instances && instances[names]) {
hookCount = 1;
addNow(instances[names], hookType, hookFn);
} else {
reject({"message": "No Valid Hook Targets Defined", "code": "NO_TARGETS"});
return;
}
var interval = setInterval(function(){
if(hooksSet.length >= hookCount) {
clearInterval(interval);
resolve({"message": "Hooks Set", "code": "HOOKS_SET", "hooks": hooksSet});
return;
}
}, 10);
});
}

/**
* (Internal > Remove Hook) Removes Defined Hook Functions
* @param {string} hookId - The Hook UUID to Remove
*/
var removeHook = function HTTPAddHook(hookId){
for (var name in instances) {
if(instances[name].hookIdDirectory[hookId]) {
if(instances[name].hooks[hookIdDirectory[hookId]] && instances[name].hooks[hookIdDirectory[hookId]][hookId]) {
delete instances[name].hookIdDirectory[hookId];
delete instances[name].hooks[hookIdDirectory[hookId]][hookId];
}
}
}
return true;
}

/**
* (Internal > Execute Hook) Executes Hooks for a Defined Type
* @param {string} name - The name of the interface
*/
var get = function HTTPGetInstance(name){
return instances[name];
var executeHookFns = function HTTPExecuteHook(name, type, input){
return new Promise((resolve, reject) => {
console.log('executing hook fns - outer');
var hookCount = Object.keys(instances[name].hooks[type]).length;
var hooksExecuted = [], hookStack = [];
for(var hookId in instances[name].hooks[type]) {
hookStack.push(instances[name].hooks[type][hookId]);
}
var executeNow = function HTTPExecuteHookInner(newInput, cb) {
console.log('executing hook fns - inner', hookStack, hookStack.length);
if(!instances[name]) { cb({"message": "Invalid Instance", "code": "INVALID_INSTANCE"}, null); }
const types = ["onIncomingRequest", "onOutgoingResponse", "onOutgoingRequest", "onIncomingResponse"];
if(!types.includes(type)) { cb({"message": "Invalid Type", "code": "INVALID_TYPE"}, null); }
if(hookCount <= 0) { resolve(newInput); }
else if (hookStack.length > 0) {
var hookFn = hookStack.pop();
hookFn(newInput, function(output) {
console.log("hook fn completed ", output);
executeNow(output, cb);
});
} else {
resolve(newInput);
}
}
executeNow(input);
});

}


Expand Down Expand Up @@ -667,8 +767,8 @@
evt.res.end(JSON.stringify(evt.msg.response.body));
return;
}
renderView(evt.msg, evt.res, mustache, htmlData);
log("debug","Blackrock HTTP Interface > [8] Object-Type View Rendered Successfully");
log("debug","Blackrock HTTP Interface > [8] Rendering Object-Type HTML View...");
utils.renderView(evt.msg, evt.res, htmlData);
return;
});
} catch(err){
Expand All @@ -679,8 +779,8 @@
}
} else if (evt.msg.response.view.html) {
var htmlData = evt.msg.response.view.html;
utils.renderView(evt.msg, evt.res, mustache, htmlData);
log("debug","Blackrock HTTP Interface > [8] Successfully Rendered HTML View");
log("debug","Blackrock HTTP Interface > [8] Rendering Object-Type HTML View...");
utils.renderView(evt.msg, evt.res, htmlData);
return;
} else {
log("error","Blackrock HTTP Interface > [8] Error Loading View - Unknown Type.");
Expand All @@ -704,7 +804,8 @@
var basePath = __dirname + "/../../../../", fs = require('fs');
var viewPath = basePath + "services/" + evt.msg.service + "/views/" + evt.msg.response.view;
if(viewCache[viewPath] && viewCache[viewPath].content) {
utils.renderView(evt.msg, evt.res, mustache, viewCache[viewPath].content);
log("debug","Blackrock HTTP Interface > [9] Rendering File-Type View...");
utils.renderView(evt.msg, evt.res, viewCache[viewPath].content);
resolve(evt);
return;
} else {
Expand All @@ -720,8 +821,8 @@
content: htmlData,
expiry: "TBC"
}
utils.renderView(evt.msg, evt.res, mustache, htmlData);
log("debug","Blackrock HTTP Interface > [9] File-Type View Rendered Successfully");
log("debug","Blackrock HTTP Interface > [9] Rendering File-Type View...");
utils.renderView(evt.msg, evt.res, htmlData);
return;
});
} catch(err){
Expand Down Expand Up @@ -762,10 +863,11 @@
* (Internal > Utilities) Render View
* @param {object} msg - Message Object
* @param {object} response - Response Object
* @param {object} mustache - Mustache Library
* @param {string} htmlData - HTML Data Context
*/
utils.renderView = function HTTPRenderView(msg, response, mustache, htmlData) {
utils.renderView = function HTTPRenderView(msg, response, htmlData) {

// Load Partial Includes:
var rootBasePath = __dirname + "/../../../../", fs = require("fs"), partials = {}, regex = /{{>(.+)}}+/g, found = htmlData.match(regex);
if(found){
for (var i = 0; i < found.length; i++) {
Expand All @@ -774,8 +876,19 @@
catch(err){ null; }
}
}
var output = mustache.render(htmlData, msg.response.body, partials);
response.end(output);

// Inject Context Into View With Mustache:
var result = mustache.render(htmlData, msg.response.body, partials);

// Execute Hooks for onOutgoingResponse
executeHookFns(msg.interface, "onOutgoingResponse", result).then(function(output) {
response.end(output);
log("debug","Blackrock HTTP Interface > [10] View Rendered Successfully.");
}).catch(function(err) {
response.end(result);
log("debug","Blackrock HTTP Interface > [10] View Rendered Successfully.");
});

}

/**
Expand Down Expand Up @@ -865,28 +978,40 @@

if(req.data && !options.headers["Content-Length"]) { options.headers["Content-Length"] = Buffer.byteLength(req.data); }

var reqObj = httpLib.request(options, function HTTPClientRequestCallback(res) {
let responseData = "";
if(req.encoding) { res.setEncoding(req.encoding) }
else { res.setEncoding("utf8"); }
res.on('data', (chunk) => { responseData += chunk; });
res.on('end', () => {
if (responseData && core.module("utilities").isJSON(responseData) == "json_string") { responseData = JSON.parse(responseData); }
else if (responseData && responseData.indexOf('<') == -1 && responseData.indexOf('>') == -1 && responseData.indexOf('=') !== -1) {
var responseDataSplit = responseData.split("&"), responseDataNew = {};
for (var i = 0; i < responseDataSplit.length; i++) {
var valueSplit = responseDataSplit[i].split("=");
responseDataNew[decodeURIComponent(valueSplit[0])] = decodeURIComponent(valueSplit[1]);
}
responseData = responseDataNew;
} else { responseData = decodeURIComponent(responseData); }
cb(null, { success: true, code: 4, message: "Response Received Successfully", statusCode: res.statusCode, data: responseData });
return;
});
var makeRequest = function(theOptions) {
var reqObj = httpLib.request(theOptions, function HTTPClientRequestCallback(res) {
let responseData = "";
if(req.encoding) { res.setEncoding(req.encoding) }
else { res.setEncoding("utf8"); }
res.on('data', (chunk) => { responseData += chunk; });
res.on('end', () => {
if (responseData && core.module("utilities").isJSON(responseData) == "json_string") { responseData = JSON.parse(responseData); }
else if (responseData && responseData.indexOf('<') == -1 && responseData.indexOf('>') == -1 && responseData.indexOf('=') !== -1) {
var responseDataSplit = responseData.split("&"), responseDataNew = {};
for (var i = 0; i < responseDataSplit.length; i++) {
var valueSplit = responseDataSplit[i].split("=");
responseDataNew[decodeURIComponent(valueSplit[0])] = decodeURIComponent(valueSplit[1]);
}
responseData = responseDataNew;
} else { responseData = decodeURIComponent(responseData); }
executeHookFns(name, "onIncomingResponse", {"statusCode": res.statusCode, "data": responseData}).then(function(myResOutput) {
cb(null, { success: true, code: 4, message: "Response Received Successfully", statusCode: myResOutput.statusCode, data: myResOutput.data });
}).catch(function(err) {
cb(null, { success: true, code: 4, message: "Response Received Successfully", statusCode: res.statusCode, data: responseData });
});
});
});
reqObj.on('error', (error) => { cb({ success: false, code: 5, message: "Request Error", error: error}, null); });
if(req.data) { reqObj.write(req.data); }
reqObj.end();
}

executeHookFns(name, "onOutgoingRequest", options).then(function(theOptions) {
makeRequest(theOptions);
}).catch(function(err) {
makeRequest(options);
});
reqObj.on('error', (error) => { cb({ success: false, code: 5, message: "Request Error", error: error}, null); });
if(req.data) { reqObj.write(req.data); }
reqObj.end();

}

/**
Expand Down
21 changes: 21 additions & 0 deletions interfaces/http/support/cheerio/@types/node/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) Microsoft Corporation.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
16 changes: 16 additions & 0 deletions interfaces/http/support/cheerio/@types/node/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Installation
> `npm install --save @types/node`
# Summary
This package contains type definitions for Node.js (http://nodejs.org/).

# Details
Files were exported from https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node.

### Additional Details
* Last updated: Wed, 28 Oct 2020 18:55:35 GMT
* Dependencies: none
* Global values: `Buffer`, `__dirname`, `__filename`, `clearImmediate`, `clearInterval`, `clearTimeout`, `console`, `exports`, `global`, `module`, `process`, `queueMicrotask`, `require`, `setImmediate`, `setInterval`, `setTimeout`

# Credits
These definitions were written by [Microsoft TypeScript](https://github.com/Microsoft), [DefinitelyTyped](https://github.com/DefinitelyTyped), [Alberto Schiabel](https://github.com/jkomyno), [Alexander T.](https://github.com/a-tarasyuk), [Alvis HT Tang](https://github.com/alvis), [Andrew Makarov](https://github.com/r3nya), [Benjamin Toueg](https://github.com/btoueg), [Bruno Scheufler](https://github.com/brunoscheufler), [Chigozirim C.](https://github.com/smac89), [David Junger](https://github.com/touffy), [Deividas Bakanas](https://github.com/DeividasBakanas), [Eugene Y. Q. Shen](https://github.com/eyqs), [Flarna](https://github.com/Flarna), [Hannes Magnusson](https://github.com/Hannes-Magnusson-CK), [Hoàng Văn Khải](https://github.com/KSXGitHub), [Huw](https://github.com/hoo29), [Kelvin Jin](https://github.com/kjin), [Klaus Meinhardt](https://github.com/ajafff), [Lishude](https://github.com/islishude), [Mariusz Wiktorczyk](https://github.com/mwiktorczyk), [Mohsen Azimi](https://github.com/mohsen1), [Nicolas Even](https://github.com/n-e), [Nikita Galkin](https://github.com/galkin), [Parambir Singh](https://github.com/parambirs), [Sebastian Silbermann](https://github.com/eps1lon), [Simon Schick](https://github.com/SimonSchick), [Thomas den Hollander](https://github.com/ThomasdenH), [Wilco Bakker](https://github.com/WilcoBakker), [wwwy3y3](https://github.com/wwwy3y3), [Samuel Ainsworth](https://github.com/samuela), [Kyle Uehlein](https://github.com/kuehlein), [Jordi Oliveras Rovira](https://github.com/j-oliveras), [Thanik Bhongbhibhat](https://github.com/bhongy), [Marcin Kopacz](https://github.com/chyzwar), [Trivikram Kamat](https://github.com/trivikr), [Minh Son Nguyen](https://github.com/nguymin4), [Junxiao Shi](https://github.com/yoursunny), [Ilia Baryshnikov](https://github.com/qwelias), [ExE Boss](https://github.com/ExE-Boss), [Surasak Chaisurin](https://github.com/Ryan-Willpower), [Piotr Błażejewicz](https://github.com/peterblazejewicz), [Anna Henningsen](https://github.com/addaleax), [Jason Kwok](https://github.com/JasonHK), and [Victor Perin](https://github.com/victorperin).
Loading

0 comments on commit 5be3d4d

Please sign in to comment.