abstractmechanic
Version:
NodeJS Framework, small footprint, fast learning curve. This framework is perfect for produce apps fast
302 lines (243 loc) • 9.97 kB
JavaScript
/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<==
==> Program-Name: AbstractMechanics <==
==> Lang/Syntax: JavaScript (NodeJS Native Library) <==
==> Program-Type: Framework (for Node Env.) <==
==> Developer: Katana-Development/ A.J. Chamber <==
==> Created-On: [2020-JUN-15th] <==
==> Updated-On: [2020-JULY-1st] <==
==> ABOUT: <==
==> AbstractMechanics is a framework created for NodeJS, <==`
==> currently it is in development and small. I intend to use <==
==> it for my own custom project, however; if other developers <==
==> ever used it that would be really awesome. <==
==>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> * <<<<<<<<<<<<<<<<<<<<<<<<<<<<<==
*/
const formidable = require('formidable');
const http = require('http');
const url = require('url');
const path = require('path');
const fs = require('fs');
const events = require('events');
const {SSL_OP_SSLEAY_080_CLIENT_DH_BUG} = require('constants');
var ee = new events.EventEmitter();
//? ============( CLASS: 'Backend' )============
class Backend
{
/** @constructor
* @memberof Backend
* @param {string} __rootdir
**/
constructor(__rootdir)
{
this.port = 1000;
this.__rootdir = __rootdir;
}
/** @method Server()
* @memberof Backend
* @param {number} port
**/
server(port)
{
this.port = port;
http.createServer((req, res) =>
{
const reqData = parseHttpRequest(req, res);
var event = '';
var triggers = '';
//! =======================( GET REQUEST ROUTING )=======================
if(req.method.toUpperCase() == 'GET') {
printEzRead(`GET REQUEST RECIEVED`, reqData);
event = 'EventUrl_' + reqData.pathname;
triggers = ee.listenerCount(event);
if(triggers > 0) {
ee.emit(event, reqData.contenttype, res);
} else {
event = 'EventDir_' + reqData.dirname;
triggers = ee.listenerCount(event);
if(triggers > 0) {
ee.emit(event, reqData, res);
} else {
console.log('\nHTTP-404: Not Found\n');
serveStatus(404, res)
}
}
//! =======================( POST REQUEST ROUTING )=======================
} else if(req.method.toUpperCase() == 'POST') {
const form = formidable({multiples: true});
form.parse(req, (err, formData) =>
{
if(err) throw err;
event = `EventForm_${reqData.pathname}`;
ee.emit(event, reqData, res, formData);
});
} else {
console.log('\nHTTP-406: Not Acceptable, likely to do with content type\n');
serveStatus(406, res)
};
}).listen(this.port);
printEzRead('Server Created Successfully', `Port Listening on #${port}`);
};
/** @method Form()
* @memberof Backend
* @param {string} actionUrl "Forms action URL, proprty of HTML element FORM"
* @callback callback "Async callback that allows logic to be dynamicly added to the 'Form Method' anywhere it is called, i found this to be perfect for a method that is a sort of form abstraction." Callback has 3 arguments, (reqData - contains HTTP Request Data), (res - Contains the HTTP Response Object for making a response to the requesting computer), (formData - parsed data from the form that the user submited. Parsed using formidable.)"
* @example
* <!-- action property -->
* <form method='POST' action='/post/form/login.php'>
**/
form(actionUrl, callback)
{
ee.on(`EventForm_${actionUrl}`, (reqData, res, formData) =>
{
callback(reqData, res, formData);
});
}
/** @method URl()
* @memberof Backend "Abstraction of the applications Serverside/backend"
* @param {string} url "Select a valid URl"
* @param {string} filepath "Filepath of the file to be served from selected URl"
**/
url(url, filepath)
{
ee.on(`EventUrl_${url}`, (contenttype, res) =>
{
filepath = path.join(this.__rootdir, filepath);
if(fs.existsSync(filepath)) {
fs.readFile(filepath, (err, data) =>
{
res.writeHead(200, contenttype);
res.write(data);
res.end();
});
return 0;
} else {
console.log('\nHTTP-400: Bad Request\n');
serveStatus(400, res)
return -1;
}
});
}
/** @method Directory()
* @memberof Backend
* @param {string} url
* @param {string} dir "Select a directory to publisize with the seleted url as the URL prefix to the filename"
* @example
* dir('/css', '/public/assets/css);
*
* 'http://localhost/css/stylesheet.css' === 'http://localhost/public/assets/css/stylesheet.css'
**/
directory(url, dir)
{
ee.on(`EventDir_${url}`, (reqData, res) =>
{
var filepath = path.join(this.__rootdir, dir, reqData.filename);
if(fs.existsSync(filepath)) {
fs.readFile(filepath, (err, data) =>
{
res.writeHead(200, reqData.contenttype);
res.write(data);
res.end();
return 0;
});
} else {
console.log('\nHTTP-400: Bad Request\n');
serveStatus(400, res)
return -1;
}
});
}
}
//? ============( STAND ALONE FUNCTIONS )============
/** @function printEzRead
* @param {string} header Enter a string that will diplay as a title to the printed data
**/
function printEzRead(header)
{
var values = Object.values(arguments);
console.log(`\n\n\n\t\t===============|| ${header} ||===============`);
values.forEach((value, i) =>
{
if(i !== 0) {console.log(value);}
});
}
/** parseHttpRequest doesn't export. Should be no reason for
anyone to use it anywhere but here. **/
function parseHttpRequest(req, res)
{
const reqUrl = req.url;
const reqPathname = url.parse(reqUrl).pathname;
const parsedHttpRequest =
{
url: reqUrl,
method: req.method,
pathname: reqPathname,
filename: path.basename(reqPathname),
dirname: path.dirname(reqPathname),
ext: path.extname(reqPathname),
contenttype: getContentType(path.extname(reqPathname), res),
cookies: req.headers.cookie
};
return parsedHttpRequest;
}
/** @function getContentType
* @param {string} fileExt
* @return {Content-Type} @typedef JSON "{"Content-Type" : "?/?"} returns the complete json object needed for most common file extention/mime types."
**/
function getContentType(fileExt, res)
{
switch(fileExt) {
case '':
return new Object({"Content-Type": "text/html"});
case '.html':
return new Object({"Content-Type": "text/html"});
case '.css':
return new Object({"Content-Type": "text/css"});
case '.xml':
return new Object({"Content-Type": "text/xml"});
case '.txt':
return new Object({"Content-Type": "text/plain"});
case '.ico':
return new Object({"Content-Type": "image/x-icon"});
case '.jpg':
return new Object({"Content-Type": "image/jpeg"});
case '.png':
return new Object({"Content-Type": "image/png"});
case '.gif':
return new Object({"Content-Type": "image/gif"});
case '.js':
return new Object({"Content-Type": "application/javascript"});
case '.pdf':
return new Object({"Content-Type": "application/pdf"});
default:
console.log('\n\n\t ===| ERROR: setContentType(fileExt) Defaulted out! |===\n\n');
serveStatus(200, res);
return -1;
}
}
/**
* @function ServeStatus
* @param {number} status
* @param {Object} HTTP.response - HTTP Response Object
*/
function serveStatus(status, res)
{
let __filepath = path.join(__dirname, '/status', `/http${status}.html`);
fs.readFile(__filepath, (err, data) =>
{
if(err)
{
console.log('ERROR: Error serving HTTP Status');
res.writeHead(500, {"Content-Type": "text/html"});
res.write('<h1>HTTP-500: Error Serving Previous HttpStatus. ADMIN Attention Needed. Katana.Development@Gmail.com</h1>')
res.end();
}
res.writeHead(status, {"Content-Type": "text/html"});
res.write(data);
res.end();
});
}
//* ========| serv.js's: 'Modular Exports' |========
module.exports.Backend = Backend;
module.exports.getContentType = getContentType;
module.exports.printEzRead = printEzRead;
module.exports.serveStatus = serveStatus;