diff --git a/app/lib/data.js b/app/lib/data.js index 0e2c10c..5f5530f 100644 --- a/app/lib/data.js +++ b/app/lib/data.js @@ -9,7 +9,7 @@ const utils = require('./utils') let lib = {}; -lib.baseDir = path.join(__dirname,'../.data/'); +lib.baseDir = path.join(__dirname,'/../.data/'); // write file lib.create = function(dir, fileName, data, callback) { @@ -40,8 +40,8 @@ lib.create = function(dir, fileName, data, callback) { lib.read = function(dir, fileName, callback) { // open file fs.readFile(lib.baseDir+dir+"/"+fileName+".json", 'utf8', function(err, data) { - console.log('_data.read err:',err); - console.log('_data.read data:',data); + //console.log('_data.read err:',err); + //console.log('_data.read data:',data); if(!err && data) { callback(false, utils.parseJsonToObj(data)); } else { @@ -53,7 +53,7 @@ lib.read = function(dir, fileName, callback) { lib.update = function(dir, fileName, data, callback) { // try to open the file - console.log("lib.update data:",data); + //console.log("lib.update data:",data); fs.open(lib.baseDir+dir+"/"+fileName+".json",'r+',function(err, fileDescriptor) { if(!err && fileDescriptor) { let stringifiedData = JSON.stringify(data); diff --git a/app/lib/logs.js b/app/lib/logs.js new file mode 100644 index 0000000..3a991fd --- /dev/null +++ b/app/lib/logs.js @@ -0,0 +1,142 @@ +/* + * Library for storing and rotating log files + */ + +// dependencies +let fs = require('fs'); +let path = require('path'); +let zlib = require('zlib'); + +// container for the module +let lib = {}; + +lib.baseDir = path.join(__dirname, '/../.logs/'); + +// append a string to a file. Create file if it doesn't exist yet. +lib.append = function(fileName, dataString, callback) { + // open file for appending + fs.open(lib.baseDir+fileName+'.log','a', function(err, fileDescriptor) { + if(!err && fileDescriptor) { + // append to the file and close it + fs.appendFile(fileDescriptor, dataString+'\n', function(err) { + if(!err) { + fs.close(fileDescriptor, function(err){ + if(!err) { + callback(false); + } else { + callback('Error closing file that was being appended',err); + } + }); + } else { + callback(500,{'Error': 'Failed to write data to log file,'+fileName}); + } + }); + } else { + callback('Could not open file,'+fileName+', to append to.'); + } + }); + +}; + + +lib.list = function(includeCompressedLogs, callback) { + fs.readdir(lib.baseDir, function(err, data) { + console.log('logs.list data',data); + if(!err && data) { + var trimmedFileNames = []; + data.forEach(function(fileName) { + if(fileName.indexOf('.log') > -1) { + trimmedFileNames.push(fileName.replace('.log','')); + } + // Add on the .gz files + if(fileName.indexOf('.gz.b64') > -1 && includeCompressedLogs) { + trimmedFileNames.push(fileName.replace('.gz.g64','')); + } + callback(false, trimmedFileNames); + }); + } else { + callback(err, data); + } + }); +}; + +// compress a .log file into a .gz.b64 file in same folder +lib.compress = function(logId, newFileId, callback) { + let sourceFile = logId+'.log'; + let destFile = newFileId+'.gz.b64'; + + // read the source file + fs.readFile(lib.baseDir+sourceFile, 'utf8', function(err, inputString) { + if(!err && inputString) { + // compress the data + zlib.gzip(inputString, function(err, buffer) { + if(!err && buffer) { + // send to file for writing + fs.open(lib.baseDir+destFile, 'wx', function(err, fileDescriptor) { + if(!err && fileDescriptor) { + fs.writeFile(fileDescriptor, buffer.toString('base64'), function(err) { + if(!err) { + fs.close(fileDescriptor, function(err) { + if(!err) { + callback(false); + } else { + callback(err); + } + }); + } else { + callback(err); + } + }); + } else { + callback(err); + } + }); + } else { + callback(err); + } + }); + } else { + callback(err); + } + }); + +}; + + +// decompress contents of a .gz.b64 file into a string variable +lib.decompress = function(fileId, callback) { + let fileName = fileId+'.gz.b64'; + fs.readFile(lib.baseDir+filename, 'utf8', function(err, str) { + if(!err && str) { + // decompress the data + let inputBuffer = Buffer.from(str, 'base64'); + zlib.unzip(inputBuffer, function(err, outputBuffer) { + if(!err && outputBuffer) { + let str = outputBuffer.toString(); + callback(false, str); + } else { + callback(err); + } + }); + } else { + callback(err); + } + }); +}; + + +// truncate a log file +lib.truncate = function(logId, callback) { + fs.truncate(lib.baseDir+logId+'.log', 0, function(err) { + if(!err) { + callback(false); + } else { + callback(err); + } + }); + +}; + + + +module.exports = lib; diff --git a/app/lib/workers.js b/app/lib/workers.js index d04f64f..f46795d 100644 --- a/app/lib/workers.js +++ b/app/lib/workers.js @@ -11,11 +11,10 @@ const fs = require('fs'); const utils = require('./utils') const path = require('path'); const _data = require('./data'); +const _logs = require('./logs'); let workers = {}; - - // look up all checks, get their data, send to a validator workers.gatherAllChecks = function() { // get all the checks @@ -147,11 +146,14 @@ workers.processCheckOutcome = function(origCheckData, checkOutcome) { // decide if an alert is warranted? is current state different than origCheck's state? let alertWarranted = origCheckData.lastChecked && origCheckData.state !== state ? true : false; + let timeOfCheck = Date.now(); + + workers.log(origCheckData, checkOutcome, state, alertWarranted, timeOfCheck); // update the check state data let newCheckData = origCheckData; newCheckData.state = state; - newCheckData.lastChecked = Date.now(); + newCheckData.lastChecked = timeOfCheck; // save the data _data.update('checks', newCheckData.id, newCheckData, function(err, ){ @@ -185,16 +187,83 @@ workers.loop = function() { setInterval(workers.gatherAllChecks,1000 * 60); }; +// rotate (aka compress) the log files +workers.rotateLogs = function() { + console.log('workers.rotateLogs start'); + // list all the non-compressed logs in the .logs folder + _logs.list(false, function(err, logs) { + console.log('workers.rotateLogs logs',logs); + if(!err && logs && logs.length) { + logs.forEach(function(logName) { + // compress data to a different file + let logId = logName.replace('.log', ''); // strip extension + let newFileId = logName+'-'+Date.now(); + _logs.compress(logId, newFileId, function(err) { + if(!err) { + // truncate the log + _logs.truncate(logId, function(err) { + if(!err) { + console.log('Success truncating log file'); + } else { + console.log('Error truncating the log file',err); + } + }); + } else { + console.log('Error: compressing log file: ',err); + } + }); + }); + } else { + console.log('Error: could not find any logs to rotate.') + } + }); +}; + +workers.logRotationLoop = function() { + setInterval(workers.rotateLogs, + 1000 * 60 * 60 * 24); // once per day +}; + workers.init = function() { // get list of checks and execute immediately workers.gatherAllChecks(); // start loop and check on schedule automatically + console.log('workers calling loop()'); workers.loop(); + console.log('workers calling rotateLogs()'); + // compress all the logs immediately + workers.rotateLogs(); + + // + workers.logRotationLoop(); }; +workers.log = function(origCheckData, checkOutcome, state, alertWarranted, timeOfCheck) { + // form the log data + let logData = { + 'check' : origCheckData, + 'outcome' : checkOutcome, + 'state' : state, + 'alert' : alertWarranted, + 'time' : timeOfCheck + }; + + let logString = JSON.stringify(logData); + // determine file to write to, one per check + let logFileName = origCheckData.id; + + // append log data to log file + _logs.append(logFileName, logString, function(err) { + if(!err) { + console.log('Logging to file, '+logFileName+', succeeded.'); + } else { + console.log('Logging to file, '+logFileName+', failed.'); + } + }) +}; diff --git a/hwassign2/https/cert.pem b/hwassign2/https/cert.pem new file mode 100644 index 0000000..32e623b --- /dev/null +++ b/hwassign2/https/cert.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEaDCCA1CgAwIBAgIJAMi6qD0EuesXMA0GCSqGSIb3DQEBBQUAMH8xCzAJBgNV +BAYTAnVzMQswCQYDVQQIEwJ2YTEOMAwGA1UEBxMFYnVya2UxDjAMBgNVBAoTBW9r +b3JuMQ4wDAYDVQQLEwVva29ybjESMBAGA1UEAxMJbG9jYWxob3N0MR8wHQYJKoZI +hvcNAQkBFhBncmVnb3JAb2tvcm4uY29tMB4XDTIwMDUwOTA3MDQ1M1oXDTMwMDUw +NzA3MDQ1M1owfzELMAkGA1UEBhMCdXMxCzAJBgNVBAgTAnZhMQ4wDAYDVQQHEwVi +dXJrZTEOMAwGA1UEChMFb2tvcm4xDjAMBgNVBAsTBW9rb3JuMRIwEAYDVQQDEwls +b2NhbGhvc3QxHzAdBgkqhkiG9w0BCQEWEGdyZWdvckBva29ybi5jb20wggEiMA0G +CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDAJpKgb4Fy8OmG4MUoH5qyIHljCLfR +AjprVPwWlMxv5vb0FNwXbjFYVwb5uBy49VoNrYHK6o0lDVaIhjPTWhnirm8HTj4l +6ylZ97JkKmI6PY8v2FYay5Cgvk71cxr4Yrj/5Gved/4RoOWeZbTFzW4VPVTH/cup +iI7vDaUiI7SCcC+z6DnU1ywH7wK1HuHtwMjUx7jYPXHWjMu0ZFX1Tx0nVS69Yln9 +K21ErhkN5nhJxgxLezJv3xjTdcfGhiVZbUUV8Ex3jhajnYrjXiGCmYceM06X6Bpi +p/UghnujhzLnirEQpFFlc2Yc+jeZM6BxJYBgZq1hFaKUBKJhdcHhml0XAgMBAAGj +geYwgeMwHQYDVR0OBBYEFIXcX4MqJdK8WX6rzOOXe+UjSDouMIGzBgNVHSMEgasw +gaiAFIXcX4MqJdK8WX6rzOOXe+UjSDouoYGEpIGBMH8xCzAJBgNVBAYTAnVzMQsw +CQYDVQQIEwJ2YTEOMAwGA1UEBxMFYnVya2UxDjAMBgNVBAoTBW9rb3JuMQ4wDAYD +VQQLEwVva29ybjESMBAGA1UEAxMJbG9jYWxob3N0MR8wHQYJKoZIhvcNAQkBFhBn +cmVnb3JAb2tvcm4uY29tggkAyLqoPQS56xcwDAYDVR0TBAUwAwEB/zANBgkqhkiG +9w0BAQUFAAOCAQEAvwmX6FFDZ+ZLpp0ppzJqd0WjSdDY/ADEENriDv9n6FxUSeuF +VYaPDI+N55v1ivx/FIWJo5ctipmwHv5XsYjOIS/YWCV6LUbYLrCkiLenk7DCgsCH +ZtLg4QyaZ3vmDzgqQtZBnKsk6NFYwNQILNelSFkwTKnh2dPVj5ZNTj62LL31muDb +fG4KXKHHyInaSbueRrqtzenBmW9lAT6bkBxMl9jvzv4SPF/X6EMDoCGL7mtdt/ur +X8XhpyQKQATxVk0yQDpiZBSk5hTM0eQ1WEMxKxmDhg+wZEGPTmtzCVBsM6e6SS4N +0GQ1xAb/z0EOd4ws/6poC/GbtgRBb3pUzOqLlw== +-----END CERTIFICATE----- diff --git a/hwassign2/https/key.pem b/hwassign2/https/key.pem new file mode 100644 index 0000000..ab3573e --- /dev/null +++ b/hwassign2/https/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpQIBAAKCAQEAwCaSoG+BcvDphuDFKB+asiB5Ywi30QI6a1T8FpTMb+b29BTc +F24xWFcG+bgcuPVaDa2ByuqNJQ1WiIYz01oZ4q5vB04+JespWfeyZCpiOj2PL9hW +GsuQoL5O9XMa+GK4/+Rr3nf+EaDlnmW0xc1uFT1Ux/3LqYiO7w2lIiO0gnAvs+g5 +1NcsB+8CtR7h7cDI1Me42D1x1ozLtGRV9U8dJ1UuvWJZ/SttRK4ZDeZ4ScYMS3sy +b98Y03XHxoYlWW1FFfBMd44Wo52K414hgpmHHjNOl+gaYqf1IIZ7o4cy54qxEKRR +ZXNmHPo3mTOgcSWAYGatYRWilASiYXXB4ZpdFwIDAQABAoIBAQCyTBiKnm0zxrAq +466OSKU23ENGzfQjETo8FiPEoFYDEAHfAuwnIazQGBD07w5X4fKp9nIVZAeMdg/x +pvW4DEcwvENyN/wbG1bHkLwLjfiPqkePUeK0Amg1f+GsiB5ilNitObuONzGeEfp/ +PEi9sp2PP9XHrYypx3k6ASNVxmPdE7i6jdzszTN9nHrcMqm+Qcf2l56Szvp/tyo5 ++XirhKi9Ud3iYKXjuRbO9id+MAMkViFM+5FWDn2G1MuPA0cPGRYD8LD07yEAJici +/feuCf6E2O2VwacxmWBMvkss1D0SDzrZzfLVW60aeRxA6fEvBGK2bzvEpWB9ZMwC +4il+FQbBAoGBAOLTmUoeY+Q3WdRcjTP2fook9KE6vqo0Sgu/TBb2v0YE1zDKeYM6 +uqpe4xAx9o/D+RuwJFcxYSuPXmhUEoLliCuliNWB68kACmu1WXdp6ua6VQyozVej +O50/MVlpRqXkYTFQh1q6nUFjVh0J80U2Hnk4E8EIoOkQBTSYkX2ZRXIPAoGBANjd +P+bLNB31ZHbmxCMgUFAQF/mMOpxlt+OZraDssLm1hHYgUCTJ9HJf1iea9Vd0gHOD +5WMPExp0qVJDW8g6m9iSX5jh/+bkSimQgOxkAwUog4lrkCxuW+JAnL/XnO/uNqEj +LB2im821yt86X+uV3bcTxbzTtgVLmWnhJa8OZUx5AoGBAJNEesPqk0R6w3HjXTId +me6rK8D6WQw8ws55tzc5oNofDm/5JYeUO9mdnaLILaMNw9C8Pfv2bXZQsSTfYN1n +lU9xPDQTFDj+M8XWim3DcOW4mLuNZTS/IFdpzeqVNW9Dpe7Ur+yyOKNZkXFtImsP +Rh3B0OGFbqOi6R5K6Ds5piL1AoGAXy5cSZOOQEff3D/UfgZEuZ8WprRhVRtf5kkg +56x6tEdy59Wu1za8Tya4+5ELdWLwrcKJ/zwyij4BwtVFh1AR7q/vvU4T3ub7ldqS +ey46FR1+/eVz1cxqD5eENL8RZk0LNRYW2rrv3w2XCPq59tBEC4JmG0ZgcVqI7uue +eoK6+yECgYEAjNW7DyL4KtnWcJVZNETZYuGNY8sAdkOMLpsp9fXPd1y+7OqEQRLn +xxB1JqKdxGjxUjbzdPss/BPQn4iLxLTIo6PbKSEjESNF1sbutkNf+fTmCGc3/spA +lmMcYmRSMJI+Ok31QIXsqnD2IiTeIMbqq+ZjIhg6wDSf6YXtSHoPQ9I= +-----END RSA PRIVATE KEY----- diff --git a/hwassign2/index.js b/hwassign2/index.js new file mode 100644 index 0000000..f5026e4 --- /dev/null +++ b/hwassign2/index.js @@ -0,0 +1,52 @@ +"use strict"; +/* +* Launch pizza delivery APIs + */ + +// dependencies +const http = require('http'); +const config = require('./lib/config'); +const UnsecureServer = require('./lib/unsecureserver'); +const SecureServer = require('./lib/secureserver'); +const fs = require('fs'); +const path = require('path'); +const Router = require('./lib/router'); +const UserRouter = require('./lib/router_user'); +const TokenRouter = require('./lib/router_token'); + +let app = {}; + +app.init = function() { + + const options = { + key: fs.readFileSync(path.join(__dirname, '/https/key.pem')), + cert: fs.readFileSync(path.join(__dirname, '/https/cert.pem')) + }; + + console.log('env = ', config); + + // initialize all routers + let router = new Router(); + let userRouter = new UserRouter(); + let tokenRouter = new TokenRouter(); + + // initialize servers, http and https + let serverHttp = new UnsecureServer(config.httpPort, config.envName); + serverHttp.router = router; + serverHttp.userRouter = userRouter; + serverHttp.tokenRouter = tokenRouter; + let serverHttps = new SecureServer(options, config.httpsPort, config.envName); + serverHttps.router = router; + serverHttps.userRouter = userRouter; + serverHttps.tokenRouter = tokenRouter; + + serverHttp.init(); + serverHttps.init(); + + // initialize router for REST requests + + +}; + +app.init(); + diff --git a/hwassign2/lib/cart.js b/hwassign2/lib/cart.js new file mode 100644 index 0000000..e69de29 diff --git a/hwassign2/lib/config.js b/hwassign2/lib/config.js new file mode 100644 index 0000000..bd54105 --- /dev/null +++ b/hwassign2/lib/config.js @@ -0,0 +1,24 @@ +"use strict"; +/* + * Define configurations for testing and production + */ +let environments = {}; +let choices = ['staging','production']; +environments.staging = { + 'envName': 'staging', + 'httpPort': '3001', + 'httpsPort': '3002' +}; + + +environments.production = { + 'envName': 'production', + 'httpPort': '4001', + 'httpsPort': '4002' +}; + +let choice = typeof(process.env.NODE_ENV) == 'string' && process.env.NODE_ENV.length > 0 + ? (choices.indexOf(process.env.NODE_ENV) > -1 ? process.env.NODE_ENV : 'staging') + : 'staging'; + +module.exports = environments[choice]; diff --git a/hwassign2/lib/menu.js b/hwassign2/lib/menu.js new file mode 100644 index 0000000..e69de29 diff --git a/hwassign2/lib/menuitem.js b/hwassign2/lib/menuitem.js new file mode 100644 index 0000000..e69de29 diff --git a/hwassign2/lib/order.js b/hwassign2/lib/order.js new file mode 100644 index 0000000..e69de29 diff --git a/hwassign2/lib/receipt.js b/hwassign2/lib/receipt.js new file mode 100644 index 0000000..e69de29 diff --git a/hwassign2/lib/router.js b/hwassign2/lib/router.js new file mode 100644 index 0000000..8957a70 --- /dev/null +++ b/hwassign2/lib/router.js @@ -0,0 +1,54 @@ +"use strict"; +/* + * Router class to handle routing http/https requests. + */ + +// dependencies +const util = require('util'); +const debug = util.debuglog('router'); +const url = require('url'); + +class Router { + constructor() { + debug('Instantiated Router.'); + } + route(inputData, callback) { + debug('inputData:', inputData); + this.dispatch(inputData, callback); +// callback(200, 'Hello\n') + } + + dispatch(data, callback) { + debug("callback inputdata:",data); + let acceptableMethods = ['post','get','put','delete']; + let reqMethod = data.method.toLowerCase(); + if(acceptableMethods.indexOf(reqMethod) != -1) { + this[reqMethod](data, callback); + } else { + callback("Invalid REST method requested: "+data.method); + } + } + + post(data, callback) { + debug('Router post()'); + callback(500, 'Router post'); + } + + get(data, callback) { + debug('Router get()'); + callback(500, 'Router get'); + } + + put(data, callback) { + debug('Router update()'); + callback(500, 'Router update'); + } + + delete(data, callback) { + debug('Router delete()'); + callback(500, 'Router delete'); + } + +} + +module.exports = Router; diff --git a/hwassign2/lib/router_token.js b/hwassign2/lib/router_token.js new file mode 100644 index 0000000..141434d --- /dev/null +++ b/hwassign2/lib/router_token.js @@ -0,0 +1,40 @@ +"use strict"; +/* + * A subclass of the Router class, that handles Token requests + */ +// dependencies +const Router = require('./router'); +const util = require('util'); +const debug = util.debuglog('router_token'); + +class TokenRouter extends Router { + constructor() { + super(); + debug('constructor'); + + } + + + post(data, callback) { + debug('post()'); + callback(200, 'TokenRouter post\n'); + } + + get(data, callback) { + debug('get()'); + callback(200, 'TokenRouter get\n'); + } + + put(data, callback) { + debug('update()'); + callback(200, 'TokenRouter update\n'); + } + + delete(data, callback) { + debug('delete()'); + callback(200, 'TokenRouter delete\n'); + } +} + + +module.exports = TokenRouter; diff --git a/hwassign2/lib/router_user.js b/hwassign2/lib/router_user.js new file mode 100644 index 0000000..9a69b94 --- /dev/null +++ b/hwassign2/lib/router_user.js @@ -0,0 +1,40 @@ +"use strict"; +/* + * A subclass of the Router class, that handles User requests + */ +// dependencies +const Router = require('./router'); +const util = require('util'); +const debug = util.debuglog('router_user'); + +class UserRouter extends Router { + constructor() { + super(); + debug('constructor'); + + } + + + post(data, callback) { + debug('post()'); + callback(200, 'UserRouter post\n'); + } + + get(data, callback) { + debug('get()'); + callback(200, 'UserRouter get\n'); + } + + put(data, callback) { + debug('update()'); + callback(200, 'UserRouter update\n'); + } + + delete(data, callback) { + debug('delete()'); + callback(200, 'UserRouter delete\n'); + } +} + + +module.exports = UserRouter; diff --git a/hwassign2/lib/secureserver.js b/hwassign2/lib/secureserver.js new file mode 100644 index 0000000..04db363 --- /dev/null +++ b/hwassign2/lib/secureserver.js @@ -0,0 +1,35 @@ +"use strict"; +/* + * Secure server class + */ +const UnsecureServer = require('./unsecureserver'); +const https = require('https'); +const util = require('util'); +const debug = util.debuglog('secureserver'); + +class SecureServer extends UnsecureServer { + constructor(options, port, envName) { + super(port, envName); + this.options = options; + this.protocol = 'https'; + } + + init() { + // create secure server to handle secure requests + this.server = https.createServer(this.options, + function(req, res) { + this.parse(req, res, this.handle); + }.bind(this) + ); + + // start listening for requests + this.server.listen(this.port, + function() { + debug('listening on HTTPS '+ this.envName+' port:'+this.port); + }.bind(this) + ); + } +} + + +module.exports = SecureServer; diff --git a/hwassign2/lib/token.js b/hwassign2/lib/token.js new file mode 100644 index 0000000..e69de29 diff --git a/hwassign2/lib/unsecureserver.js b/hwassign2/lib/unsecureserver.js new file mode 100644 index 0000000..7006767 --- /dev/null +++ b/hwassign2/lib/unsecureserver.js @@ -0,0 +1,151 @@ +"use strict"; +/* + * Server class + */ + +// dependencies +const http = require('http'); +const url = require('url'); +const util = require('util'); +const StringDecoder = require('string_decoder').StringDecoder; +const debug = util.debuglog('unsecureserver'); +const utils = require('./utils'); + +//const Router = require('./router'); + +class UnsecureServer { + constructor(portNum, envName) { + this.envName = envName; + this.protocol = 'http'; + this.port = portNum; + this.routerMap = { + 'user': this.userRouter, + 'token':this.tokenRouter + }; + } + + set router(rtr) { + this._router = rtr; + } + get router() { + return this._router; + } + set userRouter(rtr) { + this._userRouter = rtr; + this.routerMap['user'] = this._userRouter; + } + get userRouter() { + return this._userRouter; + } + set tokenRouter(rtr) { + this._tokenRouter = rtr; + this.routerMap['token'] = this._tokenRouter; + } + get tokenRouter() { + return this._tokenRouter; + } + getTimestamp(date) { + return { unix: date.getTime(), utc: date.toUTCString()}; + } + init() { + // create http server to handle unsecured requests + this.server = http.createServer( + function(req, res) { + this.parse(req, res, this.handle); + }.bind(this) + ); + + // start listening for unsecured requests + this.server.listen(this.port, + function() { + debug('listening on HTTP '+ this.envName+' port:'+this.port); + }.bind(this) + ); + } + + parse (req, res, handlerCallback) { + let parsedUrl = url.parse(req.url, true); + debug('parsedUrl:',parsedUrl); + +// this.router.route(req, res); + + // get path + let pathName = parsedUrl.pathname; + let trimmedPath = pathName.replace(/^\/+|\/+$/g, ''); + let method = req.method.toLowerCase(); + let queryStrObj = parsedUrl.query; + let headers = req.headers; + let stringDecoder = new StringDecoder('utf-8'); + let buffer = ''; + req.on('data',function(data) { + buffer += stringDecoder.write(data); + }); + + req.on('end', function() { + buffer += stringDecoder.end(); + + //log response + debug("Request received at: "+this.getTimestamp(new Date()).utc); + debug('req.url='+req.url); + debug("pathname="+pathName); + debug('trimmedPath='+trimmedPath); + debug("request method="+method); + debug("query parameters",queryStrObj); + debug("headers",headers); + debug("payload/buffer: ", buffer); + + // choose handler for this request, or notFound if no handlers are defined + if(trimmedPath.length > 0 ) { + let pathParts = trimmedPath.split("/"); + let chosenPath = pathParts[0]; + debug("chosenPath:" + chosenPath); + let chosenHandler = null; + debug('this.routerMap:',this.routerMap); + if (typeof this.routerMap[chosenPath] != 'undefined') { + chosenHandler = this.routerMap[chosenPath]; + } + debug("chosenHandler=", chosenHandler); + let inputData = { + 'method': req.method, + 'trimmedPath': trimmedPath, + 'headers': headers, + 'payload': utils.parseJsonToObj(buffer), + 'queryStringObject': queryStrObj + }; + debug("inputData: ", inputData); + handlerCallback(inputData, req, res, chosenHandler); + } + }.bind(this)); + } + + handle(inputData, req, res, chosenRouter) { + if (chosenRouter != null) { + chosenRouter.route(inputData, function (statusCode, responseDataObj) { + debug("response code:" + statusCode + ", responseDataObj", responseDataObj); + debug("typeof(responseDataObj):" + typeof (responseDataObj)); + let retStatusCode = typeof (statusCode) == 'number' ? statusCode : 200; + let payloadString = "{}"; + if (typeof (responseDataObj) == 'object') { + payloadString = JSON.stringify(responseDataObj); + } else { + payloadString = responseDataObj; + } + debug("Returning statusCode: " + retStatusCode + ", responseObj: " + payloadString); + res.setHeader('Content-Type', 'application/json'); + res.writeHead(retStatusCode); + res.end(payloadString); + + }); + } else { + debug("Returning statusCode: 404, responseObj: "); + res.setHeader('Content-Type', 'application/json'); + res.writeHead(404); + res.end('Error: Unsupported path.\n'); + } + + } + +}; + + +module.exports = UnsecureServer; diff --git a/hwassign2/lib/user.js b/hwassign2/lib/user.js new file mode 100644 index 0000000..e69de29 diff --git a/hwassign2/lib/utils.js b/hwassign2/lib/utils.js new file mode 100644 index 0000000..daa0905 --- /dev/null +++ b/hwassign2/lib/utils.js @@ -0,0 +1,110 @@ +// utilities + +const config = require('./config'); +const crypto = require('crypto'); +const querystring = require('querystring') +const https = require('https'); + +let utils = { +}; + + +utils.hash = function(str) { + if(typeof(str) == 'string' && str.length > 0) { + let hashedVal = crypto.createHmac('sha256',config.hashingKey).update(str).digest('hex'); + return hashedVal; + } else { + return false; + } +}; + + +utils.parseJsonToObj = function(str) { + try { + return JSON.parse(str); + } + catch(e) { + return {}; + } +}; +utils.getRandomSection = function() { + return Math.random().toString(36).substring(2, 15); +}; +utils.getRandomString = function(maxSize) { + if(typeof(maxSize) == 'number') { + + let randStr = utils.getRandomSection(); + while (randStr.length < maxSize) { + randStr += utils.getRandomSection(); + } + return randStr.substr(0, maxSize); + } else { + return false; + } +}; + +utils.sendTwilioSms = function(phone, msg, callback) { + // validate parameters + phone = typeof(phone) == 'string' && phone.trim().length == 10 ? phone.trim(): false; + msg = typeof(msg) == 'string' && msg.trim().length >= 0 && msg.trim().length <= 1600 ? msg.trim(): false; + + if(phone && msg) { + // configure request payload that will be sent to twilio + let payload = { + 'From': config.twilio.fromPhone, + 'To': '+1'+phone, + 'Body': msg + }; + + let payloadString = querystring.stringify(payload); + + let requestDetails = { + 'protocol': 'https:', + 'hostname': 'api.twilio.com', + 'method': 'POST', + 'path' : '/2010-04-01/Accounts/'+config.twilio.accountSid+'/Messages.json', + 'auth' : config.twilio.accountSid+':'+config.twilio.authToken, + 'headers' : { + 'Content-Type' : 'application/x-www-form-urlencoded', + 'Content-Length' : Buffer.byteLength(payloadString) + } + + }; + + // instantiate the request object + let req = https.request(requestDetails, function(res) { + let status = res.statusCode; + if(status == 200 || status == 201) { + callback(false); + } else { + console.log('Status code was '+status); + console.log('twilio res:',res); + callback(status); + } + }); + + // bind to any error event + req.on('error', function(err) { + callback(err); + }); + + // add the payload + req.write(payloadString); + console.log('utils.twilio sending req',req); + req.end(); + + } else { + callback('Required parameters missing or invalid.'); + } +}; + + + + + + + + + + +module.exports = utils; diff --git a/hwassign2/readme.txt b/hwassign2/readme.txt new file mode 100644 index 0000000..052b1f5 --- /dev/null +++ b/hwassign2/readme.txt @@ -0,0 +1,43 @@ +Homework Assignment #2 + + +Details (Scenario): + +You are building the API for a pizza-delivery company. Don't worry about a frontend, just build the API. +Here's the spec from your project manager: + +1. New users can be created, their information can be edited, and they can be deleted. We should store their name, email +address, and street address. + +2. Users can log in and log out by creating or destroying a token. + +3. When a user is logged in, they should be able to GET all the possible menu items (these items can be hardcoded into +the system). + +4. A logged-in user should be able to fill a shopping cart with menu items + +5. A logged-in user should be able to create an order. You should integrate with the Sandbox of Stripe.com to accept +their payment. Note: Use the stripe sandbox for your testing. Follow this link and click on the "tokens" tab to see the +fake tokens you can use server-side to confirm the integration is working: https://stripe.com/docs/testing#cards + +6. When an order is placed, you should email the user a receipt. You should integrate with the sandbox of Mailgun.com +for this. Note: Every Mailgun account comes with a sandbox email account domain (whatever@sandbox123.mailgun.org) that +you can send from by default. So, there's no need to setup any DNS for your domain for this task +https://documentation.mailgun.com/en/latest/faqs.html#how-do-i-pick-a-domain-name-for-my-mailgun-account + +Important Note: If you use external libraries (NPM) to integrate with Stripe or Mailgun, you will not pass this +assignment. You must write your API calls from scratch. Look up the "Curl" documentation for both APIs so you can figure +out how to craft your API calls. + +This is an open-ended assignment. You may take any direction you'd like to go with it, as long as your project includes +the requirements. It can include anything else you wish as well. + +And please: Don't forget to document how a client should interact with the API you create! + + +Turning it In: + +Zip up your code files and attach them here to receive a grade and continue with the course. + +Submit your assignment +You may only submit one file with maximum 100 MB in size diff --git a/hwassign2/router.js b/hwassign2/router.js new file mode 100644 index 0000000..e69de29