You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rcn-frontserver/server.js

203 lines
7.5 KiB
JavaScript

// requirement
const https = require('https'),
fs = require('fs'),
urlparse = require('url'),
{ JSDOM } = require('jsdom'),
perfy = require('perfy'),
winston = require('winston'),
DailyRotateFile = require('winston-daily-rotate-file'),
// args
args = require('args-parser')(process.argv),
// ressources
defaultCss = "/glob/style.css",
defaultJs = "/glob/script.js",
// logging config using winston
fileLoggingFormat = winston.format.printf(info => {
return `${info.timestamp} ${info.level.toUpperCase()}: ${JSON.stringify(info.message)}`;
});
consoleLoggingFormat = winston.format.printf(info => {
return `${info.timestamp} [${info.level}] ${JSON.stringify(info.message)}`;
});
loggingFullFormat = winston.format.combine(
winston.format.splat(),
winston.format.timestamp({
format: 'MM-DD HH:mm:ss.SSS'
}),
fileLoggingFormat
),
logger = winston.createLogger({
level: winston.config.npm.levels,
format: loggingFullFormat,
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.splat(),
winston.format.timestamp({
format: 'YY-MM-DD HH:mm:ss.SSS'
}),
consoleLoggingFormat
),
level: args.loglevel || 'info'
}),
new winston.transports.File({
level: 'debug',
filename: './.log/rcn-frontserver.log'
}),
new DailyRotateFile({
level: 'verbose',
filename: './.log/frontserver-%DATE%.log',
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '32m',
maxFiles: '30d'
})
]
}),
// serveroptions
options = {
key: fs.readFileSync('.ssh/key.pem'), // the key-file
cert: fs.readFileSync('.ssh/cert.pem') // the certificate-file
},
port = args.port || 80; // The port of the web server.
// --- functional declaration part ---
/**
* Creates a server running on port 80 with tls enabled.
* @return {boolean} Returns false if the server stopped by error.
*/
function main() {
try {
https.createServer(options, function(req, res) {
logger.verbose({'msg': 'Received request', 'method': req.method, 'url': req.url});
perfy.start('response-calculation'); // caluclate the response time
let url = urlparse.parse(req.url); // parse the url
let path = url.pathname; // set path to the urls pathname
logger.debug({msg: 'Got URL by using url package','url': url, 'path': path});
let [response, mime] = getResponse(path); // get a response for the url path
logger.debug({'response-length': response.length, 'mime-type': mime});
res.writeHead(200, {"Content-Type": mime}); // write the mime as head
res.end(response); // write the response
let execTime = perfy.end('response-calculation').fullMilliseconds; // get the execution time
logger.debug("Response-Time: " + execTime + " ms for " + req.url, "debug"); // log the execution time
}).listen(port); // server listens on port specified in the parameter
} catch (error) {
logger.error(error);
logger.info("Shutting Down...");
return false;
}
}
/**
* returns the extension of a file for the given filename.
* @param {String} filename The name of the file.
* @return {String} A string that represents the file-extension.
*/
function getExtension(filename) {
try {
let exts = filename.match(/\.[a-z]+/g); // get the extension by using regex
if (exts) return exts[exts.length - 1]; // return the found extension
else return null; // return null if no extension could be found
} catch (error) {
logger.warn(error);
return null;
}
}
/**
* Returns a string that depends on the path.
* @param {String} path Normally a file-name. Depending on the extension, an other root-path is choosen.
* @return {String} An Array containing (Either the files content or an error message) and the mime-type.
*/
function getResponse(path) {
logger.verbose({'msg':'calculationg response', 'path': path});
try {
// get the file extension
let extension = getExtension(path);
// returns the home-html file if the path is root.
if (path == "/") return [fs.readFileSync("./glob/home.html"), "text/html"];
// returns the global script or css if the extension is css or js and the root-path is glob.
if (path.includes("/glob") && extension == ".css" || getExtension(path) == ".js") {
if (extension == ".css") return [fs.readFileSync("." + path), "text/css"];
else return [fs.readFileSync("." + path), "text/javascript"];
}
// test the extension for differend file types.
switch (extension) {
case '.html':
case '.htm':
return [formatHtml(fs.readFileSync("./res/html/" + path)), "text/html"];
case '.css':
return [fs.readFileSync("./res/css/"+path), "text/css"];
case '.js':
return [fs.readFileSync("./res/scripts/"+path), "text/javascript"];
case '.json':
return [fs.readFileSync("./res/data/"+path), "text/plain"];
// return some images
case '.ico':
return [fs.readFileSync("./res/img/"+path), "image/x-icon"]
case '.jpg':
return [fs.readFileSync("./res/img/"+path), "image/jpeg"];
case '.png':
return [fs.readFileSync("./res/img/"+path), "image/png"];
// return some videos
case '.mp4':
return [fs.readFileSync("./res/vid/"+path), "video/mpeg"];
// default return
default:
// if the extension is not in those above, the access is not allowed.
logger.verbose({'msg': 'Illegal request', 'path': path})
return ["Not Allowed", "text/plain"];
}
} catch (error) {
logger.error(error);
return "Error";
}
}
/**
* Formats the html string by adding a link to the standard css and to the standard javascript file.
* @param {String} htmlstring A string read from an html file or a html document string itself.
* @return {String} A html-string that represents a document.
*/
function formatHtml(htmlstring) {
logger.verbose({'msg': 'Formatting HTML', 'htmlstring': htmlstring});
try {
let dom = new JSDOM(htmlstring); // creates a dom from the html string
let document = dom.window.document;
let head = document.getElementsByTagName('head')[0]; // gets the documents head
let css = document.createElement('link'); // create a link for the css embeding
css.setAttribute("rel", "stylesheet");
css.setAttribute("type", "text/css");
css.setAttribute("href", defaultCss);
head.prepend(css); // prepend the link to the head
let js = document.createElement('script'); // create a script for js embedding
js.setAttribute("type", "text/javascript");
js.setAttribute("src", defaultJs);
head.prepend(js); // prepend the script to the head
let jquery = document.createElement('script'); // create a script for js embedding
jquery.setAttribute("type", "text/javascript");
jquery.setAttribute("src", "/glob/jquery.js");
head.prepend(jquery); // prepend the script to the head
return dom.serialize(); // return a string of the document
} catch(error) {
logger.error(error);
return htmlstring;
}
}
// Executing the main function
if (typeof require != 'undefined' && require.main == module) {
logger.exceptions.handle(
new winston.transports.File({
filename: './.log/frontserver-exceptions.log'
})
);
logger.info("Starting up... "); // log the current date so that the logfile is better to read.
main();
}