diff --git a/app/.nvmrc b/app/.nvmrc new file mode 100644 index 0000000..515be8f --- /dev/null +++ b/app/.nvmrc @@ -0,0 +1 @@ +4.4 diff --git a/app/api/controllers/FileController.js b/app/api/controllers/FileController.js index 2d60a29..b82ff11 100644 --- a/app/api/controllers/FileController.js +++ b/app/api/controllers/FileController.js @@ -5,93 +5,89 @@ * @help :: See http://sailsjs.org/#!/documentation/concepts/Controllers */ -var fs = require('fs-extra') - ,path = require('path') +/* global sails */ + +const fs = require('fs-extra') +const path = require('path') // Source: http://weeknumber.net/how-to/javascript // Returns the ISO week of the date. -var getWeek = function(dt) { - var date = new Date(dt.getTime()); - date.setHours(0, 0, 0, 0); - // Thursday in current week decides the year. - date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7); - // January 4 is always in week 1. - var week1 = new Date(date.getFullYear(), 0, 4); - // Adjust to Thursday in week 1 and count number of weeks from date to week1. - return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - - 3 + (week1.getDay() + 6) % 7) / 7); +const getWeek = function (dt) { + const date = new Date(dt.getTime()) + date.setHours(0, 0, 0, 0) + // Thursday in current week decides the year. + date.setDate(date.getDate() + 3 - (date.getDay() + 6) % 7) + // January 4 is always in week 1. + const week1 = new Date(date.getFullYear(), 0, 4) + // Adjust to Thursday in week 1 and count number of weeks from date to week1. + return 1 + Math.round(((date.getTime() - week1.getTime()) / 86400000 - 3 + (week1.getDay() + 6) % 7) / 7) } module.exports = { - - /** - * Handle incoming file upload. - */ - upload: function(req, res) { - // Use the session timestamp as the file timestamp (so that pcap parts for example - // end up to the same folder .. ) - var dt = new Date(parseInt(req.params.filename.split('_')[0])); - - sails.log.debug('[FileController] [' + dt.toString() + '] upload req client=' + - req.clientip+', version=' + req.params.version + - ', deviceid=' + req.params.deviceid + - ', filename=' + req.params.filename); - - var dstdir = path.join( - sails.config.upload.datadir, - req.params.deviceid, - req.params.version.replace(/,/g,'.'), - dt.getFullYear()+'', - getWeek(dt)+''); - - sails.log.verbose("[FileController] ensure dir " + dstdir); - - fs.ensureDir(dstdir, function(err) { - if (err) { - sails.log.error(err); - return res.serverError(); - } - - // ug+rwx, o+rx - fs.chmodSync(dstdir, '775'); - - var dstfile = path.join(dstdir, req.params.filename); - sails.log.verbose("[FileController] start writing " + dstfile); - - var pipe = req.pipe(fs.createWriteStream(dstfile)); - - pipe.on('error', function(err) { - sails.log.error(err); - return res.serverError(); - }); - - pipe.on('finish', function() { - fs.stat(dstfile, function(err, stats) { - if (err || !stats || !stats.isFile()) { - sails.log.error("[FileController] failed to create " + - dstfile); - if (err) sails.log.error(err); - return res.serverError(); - - } else if (stats['size'] != req.headers['content-length']) { - // missing bytes - sails.log.debug("[FileController] failed to write " + - dstfile + ", wrote " + stats['size'] + " bytes " + - " got " + req.headers['content-length'] + " bytes"); - - fs.unlink(dstfile); - return res.serverError(); - - } else { - // all good - // ug+rw, o+r - fs.chmodSync(dstfile, '664'); - sails.log.debug("[FileController] wrote " + stats['size'] + - " bytes to " + dstfile); - return res.ok(); - } - }); // fstat - }); // onFinish - }); // ensureDir - } // upload -}; + /** + * Handle incoming file upload. + */ + upload: function (req, res) { + // Use the session timestamp as the file timestamp (so that pcap parts for example + // end up to the same folder .. ) + const dt = new Date(parseInt(req.params.filename.split('_')[0])) + + sails.log.debug('[FileController] [' + dt.toString() + '] upload req client=' + + req.clientip + ', version=' + req.params.version + + ', deviceid=' + req.params.deviceid + + ', filename=' + req.params.filename) + + const dstdir = path.join( + sails.config.upload.datadir, + req.params.deviceid, + req.params.version.replace(/,/g, '.'), + dt.getFullYear().toString(), + getWeek(dt).toString()) + + sails.log.verbose('[FileController] ensure dir ' + dstdir) + + fs.ensureDir(dstdir, function (err) { + if (err) { + sails.log.error(err) + return res.serverError() + } + + // ug+rwx, o+rx + fs.chmodSync(dstdir, '775') + + const dstfile = path.join(dstdir, req.params.filename) + sails.log.verbose('[FileController] start writing ' + dstfile) + + const pipe = req.pipe(fs.createWriteStream(dstfile)) + + pipe.on('error', function (err) { + sails.log.error(err) + return res.serverError() + }) + + pipe.on('finish', function () { + fs.stat(dstfile, function (err, stats) { + if (err || !stats || !stats.isFile()) { + sails.log.error('[FileController] failed to create ' + dstfile) + if (err) sails.log.error(err) + return res.serverError() + } else if (stats['size'].toString() !== req.headers['content-length']) { + // missing bytes + sails.log.debug('[FileController] failed to write ' + + dstfile + ', wrote ' + stats['size'] + ' bytes ' + + ' got ' + req.headers['content-length'] + ' bytes') + + fs.unlink(dstfile) + return res.serverError() + } else { + // all good + // ug+rw, o+r + fs.chmodSync(dstfile, '664') + sails.log.debug('[FileController] wrote ' + stats['size'] + ' bytes to ' + dstfile) + return res.ok() + } + }) // fstat + }) // onFinish + }) // ensureDir + } // upload +} diff --git a/app/api/responses/badRequest.js b/app/api/responses/badRequest.js index 0d37825..3ad08b6 100644 --- a/app/api/responses/badRequest.js +++ b/app/api/responses/badRequest.js @@ -14,48 +14,48 @@ * ); * ``` */ - -module.exports = function badRequest(data, options) { - +module.exports = function (data, options) { + 'use strict' // Get access to `req`, `res`, & `sails` - var req = this.req; - var res = this.res; - var sails = req._sails; + const req = this.req + const res = this.res + const sails = req._sails // Set status code - res.status(400); + res.status(400) // Log error to console - if (data !== undefined) { - sails.log.verbose('Sending 400 ("Bad Request") response: \n',data); + if (data) { + sails.log.verbose('Sending 400 ("Bad Request") response: \n', data) + } else { + sails.log.verbose('Sending 400 ("Bad Request") response') } - else sails.log.verbose('Sending 400 ("Bad Request") response'); // Only include errors in response if application environment // is not set to 'production'. In production, we shouldn't // send back any identifying information about errors. if (sails.config.environment === 'production' && sails.config.keepResponseErrors !== true) { - data = undefined; + data = undefined } // If the user-agent wants JSON, always respond with JSON // If views are disabled, revert to json if (req.wantsJSON || sails.config.hooks.views === false) { - return res.jsonx(data); + return res.jsonx(data) } // If second argument is a string, we take that to mean it refers to a view. // If it was omitted, use an empty object (`{}`) - options = (typeof options === 'string') ? { view: options } : options || {}; + options = (typeof options === 'string') ? { view: options } : options || {} // Attempt to prettify data for views, if it's a non-error object - var viewData = data; - if (!(viewData instanceof Error) && 'object' == typeof viewData) { + let viewData = data + + if (!(viewData instanceof Error) && (typeof viewData === 'object')) { try { - viewData = require('util').inspect(data, {depth: null}); - } - catch(e) { - viewData = undefined; + viewData = require('util').inspect(data, {depth: null}) + } catch (e) { + viewData = undefined } } @@ -63,14 +63,12 @@ module.exports = function badRequest(data, options) { // Otherwise try to guess an appropriate view, or if that doesn't // work, just send JSON. if (options.view) { - return res.view(options.view, { data: viewData, title: 'Bad Request' }); + return res.view(options.view, { data: viewData, title: 'Bad Request' }) + } else { + // If no second argument provided, try to serve the implied view, + // but fall back to sending JSON(P) if no view can be inferred. + return res.guessView({ data: viewData, title: 'Bad Request' }, function () { + return res.jsonx(data) + }) } - - // If no second argument provided, try to serve the implied view, - // but fall back to sending JSON(P) if no view can be inferred. - else return res.guessView({ data: viewData, title: 'Bad Request' }, function couldNotGuessView () { - return res.jsonx(data); - }); - -}; - +} diff --git a/app/api/responses/forbidden.js b/app/api/responses/forbidden.js index ca94852..6b20d15 100644 --- a/app/api/responses/forbidden.js +++ b/app/api/responses/forbidden.js @@ -11,48 +11,48 @@ * return res.forbidden('Access denied.'); * ``` */ - -module.exports = function forbidden (data, options) { - +module.exports = function (data, options) { + 'use strict' // Get access to `req`, `res`, & `sails` - var req = this.req; - var res = this.res; - var sails = req._sails; + const req = this.req + const res = this.res + const sails = req._sails // Set status code - res.status(403); + res.status(403) // Log error to console - if (data !== undefined) { - sails.log.verbose('Sending 403 ("Forbidden") response: \n',data); + if (data) { + sails.log.verbose('Sending 403 ("Forbidden") response: \n', data) + } else { + sails.log.verbose('Sending 403 ("Forbidden") response') } - else sails.log.verbose('Sending 403 ("Forbidden") response'); // Only include errors in response if application environment // is not set to 'production'. In production, we shouldn't // send back any identifying information about errors. if (sails.config.environment === 'production' && sails.config.keepResponseErrors !== true) { - data = undefined; + data = undefined } // If the user-agent wants JSON, always respond with JSON // If views are disabled, revert to json if (req.wantsJSON || sails.config.hooks.views === false) { - return res.jsonx(data); + return res.jsonx(data) } // If second argument is a string, we take that to mean it refers to a view. // If it was omitted, use an empty object (`{}`) - options = (typeof options === 'string') ? { view: options } : options || {}; + options = (typeof options === 'string') ? { view: options } : options || {} // Attempt to prettify data for views, if it's a non-error object - var viewData = data; - if (!(viewData instanceof Error) && 'object' == typeof viewData) { + let viewData = data + + if (!(viewData instanceof Error) && (typeof viewData === 'object')) { try { - viewData = require('util').inspect(data, {depth: null}); - } - catch(e) { - viewData = undefined; + viewData = require('util').inspect(data, {depth: null}) + } catch (e) { + viewData = undefined } } @@ -60,30 +60,25 @@ module.exports = function forbidden (data, options) { // Otherwise try to guess an appropriate view, or if that doesn't // work, just send JSON. if (options.view) { - return res.view(options.view, { data: viewData, title: 'Forbidden' }); - } - - // If no second argument provided, try to serve the default view, - // but fall back to sending JSON(P) if any errors occur. - else return res.view('403', { data: viewData, title: 'Forbidden' }, function (err, html) { - - // If a view error occured, fall back to JSON(P). - if (err) { - // - // Additionally: - // • If the view was missing, ignore the error but provide a verbose log. - if (err.code === 'E_VIEW_FAILED') { - sails.log.verbose('res.forbidden() :: Could not locate view for error page (sending JSON instead). Details: ',err); - } - // Otherwise, if this was a more serious error, log to the console with the details. - else { - sails.log.warn('res.forbidden() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err); + return res.view(options.view, { data: viewData, title: 'Forbidden' }) + } else { + // If no second argument provided, try to serve the default view, + // but fall back to sending JSON(P) if any errors occur. + return res.view('403', { data: viewData, title: 'Forbidden' }, function (err, html) { + // If a view error occured, fall back to JSON(P). + if (err) { + // Additionally: + // • If the view was missing, ignore the error but provide a verbose log. + if (err.code === 'E_VIEW_FAILED') { + sails.log.verbose('res.forbidden() :: Could not locate view for error page (sending JSON instead). Details: ', err) + } else { + // Otherwise, if this was a more serious error, log to the console with the details. + sails.log.warn('res.forbidden() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err) + } + return res.jsonx(data) } - return res.jsonx(data); - } - - return res.send(html); - }); - -}; + return res.send(html) + }) + } +} diff --git a/app/api/responses/notFound.js b/app/api/responses/notFound.js index 8f0cd03..f41f33d 100644 --- a/app/api/responses/notFound.js +++ b/app/api/responses/notFound.js @@ -16,48 +16,48 @@ * or route blueprints (i.e. "shadow routes", Sails will call `res.notFound()` * automatically. */ - -module.exports = function notFound (data, options) { - +module.exports = function (data, options) { + 'use strict' // Get access to `req`, `res`, & `sails` - var req = this.req; - var res = this.res; - var sails = req._sails; + const req = this.req + const res = this.res + const sails = req._sails // Set status code - res.status(404); + res.status(404) // Log error to console - if (data !== undefined) { - sails.log.verbose('Sending 404 ("Not Found") response: \n',data); + if (data) { + sails.log.verbose('Sending 404 ("Not Found") response: \n', data) + } else { + sails.log.verbose('Sending 404 ("Not Found") response') } - else sails.log.verbose('Sending 404 ("Not Found") response'); // Only include errors in response if application environment // is not set to 'production'. In production, we shouldn't // send back any identifying information about errors. if (sails.config.environment === 'production' && sails.config.keepResponseErrors !== true) { - data = undefined; + data = undefined } // If the user-agent wants JSON, always respond with JSON // If views are disabled, revert to json if (req.wantsJSON || sails.config.hooks.views === false) { - return res.jsonx(data); + return res.jsonx(data) } // If second argument is a string, we take that to mean it refers to a view. // If it was omitted, use an empty object (`{}`) - options = (typeof options === 'string') ? { view: options } : options || {}; + options = (typeof options === 'string') ? { view: options } : options || {} // Attempt to prettify data for views, if it's a non-error object - var viewData = data; - if (!(viewData instanceof Error) && 'object' == typeof viewData) { + let viewData = data + + if (!(viewData instanceof Error) && (typeof viewData) === 'object') { try { - viewData = require('util').inspect(data, {depth: null}); - } - catch(e) { - viewData = undefined; + viewData = require('util').inspect(data, {depth: null}) + } catch (e) { + viewData = undefined } } @@ -65,30 +65,25 @@ module.exports = function notFound (data, options) { // Otherwise try to guess an appropriate view, or if that doesn't // work, just send JSON. if (options.view) { - return res.view(options.view, { data: viewData, title: 'Not Found' }); - } - - // If no second argument provided, try to serve the default view, - // but fall back to sending JSON(P) if any errors occur. - else return res.view('404', { data: viewData, title: 'Not Found' }, function (err, html) { - - // If a view error occured, fall back to JSON(P). - if (err) { - // - // Additionally: - // • If the view was missing, ignore the error but provide a verbose log. - if (err.code === 'E_VIEW_FAILED') { - sails.log.verbose('res.notFound() :: Could not locate view for error page (sending JSON instead). Details: ',err); - } - // Otherwise, if this was a more serious error, log to the console with the details. - else { - sails.log.warn('res.notFound() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err); + return res.view(options.view, { data: viewData, title: 'Not Found' }) + } else { + // If no second argument provided, try to serve the default view, + // but fall back to sending JSON(P) if any errors occur. + return res.view('404', { data: viewData, title: 'Not Found' }, function (err, html) { + // If a view error occured, fall back to JSON(P). + if (err) { + // Additionally: + // • If the view was missing, ignore the error but provide a verbose log. + if (err.code === 'E_VIEW_FAILED') { + sails.log.verbose('res.notFound() :: Could not locate view for error page (sending JSON instead). Details: ', err) + } else { + // Otherwise, if this was a more serious error, log to the console with the details. + sails.log.warn('res.notFound() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err) + } + return res.jsonx(data) } - return res.jsonx(data); - } - - return res.send(html); - }); - -}; + return res.send(html) + }) + } +} diff --git a/app/api/responses/ok.js b/app/api/responses/ok.js index eb70144..9952089 100644 --- a/app/api/responses/ok.js +++ b/app/api/responses/ok.js @@ -10,37 +10,36 @@ * @param {String|Object} options * - pass string to render specified view */ - -module.exports = function sendOK (data, options) { - +module.exports = function (data, options) { + 'use strict' // Get access to `req`, `res`, & `sails` - var req = this.req; - var res = this.res; - var sails = req._sails; + const req = this.req + const res = this.res + const sails = req._sails - sails.log.silly('res.ok() :: Sending 200 ("OK") response'); + sails.log.silly('res.ok() :: Sending 200 ("OK") response') // Set status code - res.status(200); + res.status(200) // If appropriate, serve data as JSON(P) // If views are disabled, revert to json if (req.wantsJSON || sails.config.hooks.views === false) { - return res.jsonx(data); + return res.jsonx(data) } // If second argument is a string, we take that to mean it refers to a view. // If it was omitted, use an empty object (`{}`) - options = (typeof options === 'string') ? { view: options } : options || {}; + options = (typeof options === 'string') ? { view: options } : options || {} // Attempt to prettify data for views, if it's a non-error object - var viewData = data; - if (!(viewData instanceof Error) && 'object' == typeof viewData) { + let viewData = data + + if (!(viewData instanceof Error) && (typeof viewData === 'object')) { try { - viewData = require('util').inspect(data, {depth: null}); - } - catch(e) { - viewData = undefined; + viewData = require('util').inspect(data, {depth: null}) + } catch (e) { + viewData = undefined } } @@ -48,13 +47,12 @@ module.exports = function sendOK (data, options) { // Otherwise try to guess an appropriate view, or if that doesn't // work, just send JSON. if (options.view) { - return res.view(options.view, { data: viewData, title: 'OK' }); + return res.view(options.view, { data: viewData, title: 'OK' }) + } else { + // If no second argument provided, try to serve the implied view, + // but fall back to sending JSON(P) if no view can be inferred. + return res.guessView({ data: viewData, title: 'OK' }, function () { + return res.jsonx(data) + }) } - - // If no second argument provided, try to serve the implied view, - // but fall back to sending JSON(P) if no view can be inferred. - else return res.guessView({ data: viewData, title: 'OK' }, function couldNotGuessView () { - return res.jsonx(data); - }); - -}; +} diff --git a/app/api/responses/serverError.js b/app/api/responses/serverError.js index 537c248..51d27fc 100644 --- a/app/api/responses/serverError.js +++ b/app/api/responses/serverError.js @@ -11,48 +11,48 @@ * error is encountered, Sails will call `res.serverError()` * automatically. */ - -module.exports = function serverError (data, options) { - +module.exports = function (data, options) { + 'use strict' // Get access to `req`, `res`, & `sails` - var req = this.req; - var res = this.res; - var sails = req._sails; + const req = this.req + const res = this.res + const sails = req._sails // Set status code - res.status(500); + res.status(500) // Log error to console - if (data !== undefined) { - sails.log.error('Sending 500 ("Server Error") response: \n',data); + if (data) { + sails.log.error('Sending 500 ("Server Error") response: \n', data) + } else { + sails.log.error('Sending empty 500 ("Server Error") response') } - else sails.log.error('Sending empty 500 ("Server Error") response'); // Only include errors in response if application environment // is not set to 'production'. In production, we shouldn't // send back any identifying information about errors. if (sails.config.environment === 'production' && sails.config.keepResponseErrors !== true) { - data = undefined; + data = undefined } // If the user-agent wants JSON, always respond with JSON // If views are disabled, revert to json if (req.wantsJSON || sails.config.hooks.views === false) { - return res.jsonx(data); + return res.jsonx(data) } // If second argument is a string, we take that to mean it refers to a view. // If it was omitted, use an empty object (`{}`) - options = (typeof options === 'string') ? { view: options } : options || {}; + options = (typeof options === 'string') ? { view: options } : options || {} // Attempt to prettify data for views, if it's a non-error object - var viewData = data; - if (!(viewData instanceof Error) && 'object' == typeof viewData) { + let viewData = data + + if (!(viewData instanceof Error) && (typeof viewData === 'object')) { try { - viewData = require('util').inspect(data, {depth: null}); - } - catch(e) { - viewData = undefined; + viewData = require('util').inspect(data, {depth: null}) + } catch (e) { + viewData = undefined } } @@ -60,30 +60,26 @@ module.exports = function serverError (data, options) { // Otherwise try to guess an appropriate view, or if that doesn't // work, just send JSON. if (options.view) { - return res.view(options.view, { data: viewData, title: 'Server Error' }); - } - - // If no second argument provided, try to serve the default view, - // but fall back to sending JSON(P) if any errors occur. - else return res.view('500', { data: viewData, title: 'Server Error' }, function (err, html) { + return res.view(options.view, { data: viewData, title: 'Server Error' }) + } else { + // If no second argument provided, try to serve the default view, + // but fall back to sending JSON(P) if any errors occur. + return res.view('500', { data: viewData, title: 'Server Error' }, function (err, html) { + // If a view error occured, fall back to JSON(P). + if (err) { + // Additionally: + // • If the view was missing, ignore the error but provide a verbose log. + if (err.code === 'E_VIEW_FAILED') { + sails.log.verbose('res.serverError() :: Could not locate view for error page (sending JSON instead). Details: ', err) + } else { + // Otherwise, if this was a more serious error, log to the console with the details. + sails.log.warn('res.serverError() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err) + } - // If a view error occured, fall back to JSON(P). - if (err) { - // - // Additionally: - // • If the view was missing, ignore the error but provide a verbose log. - if (err.code === 'E_VIEW_FAILED') { - sails.log.verbose('res.serverError() :: Could not locate view for error page (sending JSON instead). Details: ',err); + return res.jsonx(data) } - // Otherwise, if this was a more serious error, log to the console with the details. - else { - sails.log.warn('res.serverError() :: When attempting to render error page view, an error occured (sending JSON instead). Details: ', err); - } - return res.jsonx(data); - } - - return res.send(html); - }); - -}; + return res.send(html) + }) + } +} diff --git a/app/app.js b/app/app.js index 85c0af7..734d764 100644 --- a/app/app.js +++ b/app/app.js @@ -20,40 +20,41 @@ // Ensure we're in the project directory, so relative paths work as expected // no matter where we actually lift from. -process.chdir(__dirname); +process.chdir(__dirname) // Ensure a "sails" can be located: -(function() { - var sails; +;(function () { + let sails + try { - sails = require('sails'); + sails = require('sails') } catch (e) { - console.error('To run an app using `node app.js`, you usually need to have a version of `sails` installed in the same directory as your app.'); - console.error('To do that, run `npm install sails`'); - console.error(''); - console.error('Alternatively, if you have sails installed globally (i.e. you did `npm install -g sails`), you can use `sails lift`.'); - console.error('When you run `sails lift`, your app will still use a local `./node_modules/sails` dependency if it exists,'); - console.error('but if it doesn\'t, the app will run with the global sails instead!'); - return; + console.error('To run an app using `node app.js`, you usually need to have a version of `sails` installed in the same directory as your app.') + console.error('To do that, run `npm install sails`') + console.error('') + console.error('Alternatively, if you have sails installed globally (i.e. you did `npm install -g sails`), you can use `sails lift`.') + console.error('When you run `sails lift`, your app will still use a local `./node_modules/sails` dependency if it exists,') + console.error('but if it doesn\'t, the app will run with the global sails instead!') + return } // Try to get `rc` dependency - var rc; + let rc + try { - rc = require('rc'); + rc = require('rc') } catch (e0) { try { - rc = require('sails/node_modules/rc'); + rc = require('sails/node_modules/rc') } catch (e1) { - console.error('Could not find dependency: `rc`.'); - console.error('Your `.sailsrc` file(s) will be ignored.'); - console.error('To resolve this, run:'); - console.error('npm install rc --save'); - rc = function () { return {}; }; + console.error('Could not find dependency: `rc`.') + console.error('Your `.sailsrc` file(s) will be ignored.') + console.error('To resolve this, run:') + console.error('npm install rc --save') + rc = function () { return {} } } } - // Start server - sails.lift(rc('sails')); -})(); + sails.lift(rc('sails')) +})() diff --git a/app/config/bootstrap.js b/app/config/bootstrap.js index 60cfa4a..e9fdac2 100644 --- a/app/config/bootstrap.js +++ b/app/config/bootstrap.js @@ -8,12 +8,11 @@ * For more information on bootstrapping your app, check out: * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.bootstrap.html */ - -module.exports.bootstrap = function(cb) { - // make sure the upload data directory exists - sails.log.verbose("Boostrap ensure dir: " + sails.config.upload.datadir) - require('fs-extra').ensureDir(sails.config.upload.datadir, function (err) { - if (err) sails.log.error(err); - cb(); - }); -}; +module.exports.bootstrap = function (cb) { + // make sure the upload data directory exists + sails.log.verbose('Boostrap ensure dir: ' + sails.config.upload.datadir) + require('fs-extra').ensureDir(sails.config.upload.datadir, function (err) { + if (err) sails.log.error(err) + cb() + }) +} diff --git a/app/config/env/development.js b/app/config/env/development.js index 94bc78e..b2c8419 100644 --- a/app/config/env/development.js +++ b/app/config/env/development.js @@ -9,11 +9,8 @@ * any private information to this file! * */ - module.exports = { - log: { level: 'verbose' } - -}; +} diff --git a/app/config/env/production.js b/app/config/env/production.js index ac531c5..3adcdb6 100644 --- a/app/config/env/production.js +++ b/app/config/env/production.js @@ -9,11 +9,8 @@ * any private information to this file! * */ - module.exports = { - log: { - level: "debug" + level: 'debug' } - -}; +} diff --git a/app/config/globals.js b/app/config/globals.js index 6160b51..f778546 100644 --- a/app/config/globals.js +++ b/app/config/globals.js @@ -9,7 +9,6 @@ * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.globals.html */ module.exports.globals = { - /**************************************************************************** * * * Expose the lodash installed in Sails core as a global variable. If this * @@ -55,4 +54,4 @@ module.exports.globals = { * * ****************************************************************************/ models: false -}; +} diff --git a/app/config/http.js b/app/config/http.js index bf27ad1..a916a07 100644 --- a/app/config/http.js +++ b/app/config/http.js @@ -8,9 +8,7 @@ * For more information on configuration, check out: * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.http.html */ - module.exports.http = { - /**************************************************************************** * * * Express middleware to use for every Sails request. To add custom * @@ -20,66 +18,56 @@ module.exports.http = { * `customMiddleware` config option. * * * ****************************************************************************/ - middleware: { + /*************************************************************************** + * * + * The order in which middleware should be run for HTTP request. (the Sails * + * router is invoked by the "router" middleware below.) * + * * + ***************************************************************************/ + order: [ + 'startRequestTimer', + 'setClientIP', + // 'cookieParser', + // 'session', + // 'bodyParser', + // 'handleBodyParserError', + // 'compress', + // 'methodOverride', + // 'poweredBy', + // '$custom', + 'router', + 'www', + // 'favicon', + '404', + '500' + ], + /* Small helper middleware to set the client IP in the req. */ + setClientIP: function(req, res, next) { + req.clientip = (req.headers['x-forwarded-for'] || // set by forward proxy + req.socket.remoteAddress || // direct connections + req.ip).replace('::ffff:','').trim() - /*************************************************************************** - * * - * The order in which middleware should be run for HTTP request. (the Sails * - * router is invoked by the "router" middleware below.) * - * * - ***************************************************************************/ - - order: [ - 'startRequestTimer', - 'setClientIP', - // 'cookieParser', - // 'session', - // 'bodyParser', - // 'handleBodyParserError', - // 'compress', - // 'methodOverride', - // 'poweredBy', - // '$custom', - 'router', - 'www', - // 'favicon', - '404', - '500' - ], - - /* Small helper middleware to set the client IP in the req. */ - setClientIP : function(req, res, next) { - req.clientip = ( - req.headers['x-forwarded-for'] || // set by forward proxy - req.socket.remoteAddress || // direct connections - req.ip - ).replace('::ffff:','').trim(); - - sails.log("Request :: ", req.clientip, req.method, req.url); - - return next(); - }, - - /*************************************************************************** - * * - * The body parser that will handle incoming multipart HTTP requests. By * - * default as of v0.10, Sails uses * - * [skipper](http://github.com/balderdashy/skipper). See * - * http://www.senchalabs.org/connect/multipart.html for other options. * - * * - * Note that Sails uses an internal instance of Skipper by default; to * - * override it and specify more options, make sure to "npm install skipper" * - * in your project first. You can also specify a different body parser or * - * a custom function with req, res and next parameters (just like any other * - * middleware function). * - * * - ***************************************************************************/ + sails.log("Request :: ", req.clientip, req.method, req.url) + return next() + }, + /*************************************************************************** + * * + * The body parser that will handle incoming multipart HTTP requests. By * + * default as of v0.10, Sails uses * + * [skipper](http://github.com/balderdashy/skipper). See * + * http://www.senchalabs.org/connect/multipart.html for other options. * + * * + * Note that Sails uses an internal instance of Skipper by default; to * + * override it and specify more options, make sure to "npm install skipper" * + * in your project first. You can also specify a different body parser or * + * a custom function with req, res and next parameters (just like any other * + * middleware function). * + * * + ***************************************************************************/ // bodyParser: require('skipper')({strict: true}) - }, - /*************************************************************************** * * * The number of seconds to cache flat files on disk being served by * @@ -89,6 +77,5 @@ module.exports.http = { * since that's the only time Express will cache flat-files. * * * ***************************************************************************/ - // cache: 31557600000 -}; +} diff --git a/app/config/policies.js b/app/config/policies.js index 6e38145..8fd4166 100644 --- a/app/config/policies.js +++ b/app/config/policies.js @@ -15,10 +15,7 @@ * For more information on configuring policies, check out: * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.policies.html */ - - module.exports.policies = { - /*************************************************************************** * * * Default policy for all controllers and actions (`true` allows public * @@ -26,10 +23,8 @@ module.exports.policies = { * * ***************************************************************************/ '*': false, - /* Alllow all methods of the FileController. */ FileController: { - '*': true + '*': true } - -}; +} diff --git a/app/config/routes.js b/app/config/routes.js index 3ce3ee6..054610a 100644 --- a/app/config/routes.js +++ b/app/config/routes.js @@ -19,9 +19,7 @@ * For more information on configuring custom routes, check out: * http://sailsjs.org/#!/documentation/concepts/Routes/RouteTargetSyntax.html */ - module.exports.routes = { - /*************************************************************************** * * * Make the view located at `views/homepage.ejs` (or `views/homepage.jade`, * @@ -31,11 +29,9 @@ module.exports.routes = { * `assets` directory) * * * ***************************************************************************/ - //'/': { // view: 'homepage' //} - /*************************************************************************** * * * Custom routes here... * @@ -45,13 +41,13 @@ module.exports.routes = { * for configuration options and examples. * * * ***************************************************************************/ - 'POST /:version/:deviceid/:filename': { - controller : "FileController", - action : "upload" + 'POST /:version/:deviceid/:filename': { + controller: 'FileController', + action: 'upload' }, - 'PUT /:version/:deviceid/:filename': { - controller : "FileController", - action : "upload" + 'PUT /:version/:deviceid/:filename': { + controller: 'FileController', + action: 'upload' } -}; +} diff --git a/app/config/upload.js b/app/config/upload.js index 53e4d66..714fdaa 100644 --- a/app/config/upload.js +++ b/app/config/upload.js @@ -2,10 +2,7 @@ * Upload app settings. * */ - module.exports.upload = { - /** Incoming files destination folder. */ - datadir : "/data" - -}; + datadir: '/data' +} diff --git a/app/package.json b/app/package.json index 5d1d31f..15320de 100644 --- a/app/package.json +++ b/app/package.json @@ -5,21 +5,23 @@ "description": "Handles raw data file uploads from Hostview clients.", "keywords": [], "dependencies": { + "fs-extra": "^0.30.0", "include-all": "^0.1.6", "rc": "^1.0.1", - "fs-extra": "^0.30.0", "sails": "^0.12.3", "sails-disk": "^0.10.9" }, "devDependencies": { + "chai": "^3.5.0", "mocha": "^2.5.3", - "supertest" : "^1.2.0", - "chai" : "^3.5.0" + "standard": "^10.0.3", + "supertest": "^1.2.0" }, "scripts": { "debug": "node debug app.js", "start": "node app.js", - "test": "node ./node_modules/mocha/bin/mocha test/bootstrap.js test/*.test.js" + "test": "node ./node_modules/mocha/bin/mocha test/bootstrap.js test/*.test.js", + "lint": "./node_modules/.bin/standard app.js config/**/*.js api/**/*.js test/**/*.js" }, "repository": { "type": "git", diff --git a/app/test/1468403375105_foo.txt b/app/test/1468403375105_foo.txt deleted file mode 100644 index e7cbb71..0000000 --- a/app/test/1468403375105_foo.txt +++ /dev/null @@ -1 +0,0 @@ -testfile \ No newline at end of file diff --git a/app/test/1468852623282_stats.db.zip b/app/test/1468852623282_stats.db.zip new file mode 100644 index 0000000..32b0cc7 Binary files /dev/null and b/app/test/1468852623282_stats.db.zip differ diff --git a/app/test/FileController.test.js b/app/test/FileController.test.js index d3290e1..d062176 100644 --- a/app/test/FileController.test.js +++ b/app/test/FileController.test.js @@ -1,40 +1,85 @@ -var request = require('supertest'); -var assert = require('chai').assert; -var fs = require('fs'); - -describe('FileController', function() { - - describe('get /foo', function() { - it('should return 404', function(done) { - request(sails.hooks.http.app) - .get('/foo') - .expect(404, done); - }); - }); - - describe('#update()', function() { - it('should return 200 on upload', function(done) { - // check file size - fs.stat('/app/test/foo.txt', function(err, stats) { - if (err) return done(err); - - // upload req - var req = request(sails.hooks.http.app) - .post('/1234/5678/foo.txt') - .set('Content-Type', 'application/json') - .set('Content-Length', stats['size']); - - var stream = fs.createReadStream('/app/test/foo.txt'); - stream.pipe(req); - - stream.on('end', function() { - req.expect(200); - req.end(function(err, res) { - if (err) return done(err); - done(); - }); - }); - }); - }); - }); -}); \ No newline at end of file +/* global describe it sails */ + +const request = require('supertest') +const fs = require('fs') +const assert = require('chai').assert + +const rmdir = function (path) { + if (fs.existsSync(path)) { + const files = fs.readdirSync(path) + + files.forEach(function (file, index) { + const curPath = path + '/' + file + + if (fs.lstatSync(curPath).isDirectory()) { // recurse + rmdir(curPath) + } else { // delete file + fs.unlinkSync(curPath) + } + }) + + fs.rmdirSync(path) + } +} + +describe('FileController', function () { + describe('get /foo', function () { + it('should return 404', function (done) { + request(sails.hooks.http.app) + .get('/foo') + .expect(404, done) + }) + }) + + describe('#update()', function () { + it('should return 200 on upload', function (done) { + // check file size + fs.stat('test/1468852623282_stats.db.zip', function (err, stats) { + if (err) return done(err) + + // upload req + const req = request(sails.hooks.http.app) + .post('/1234/5678/1468852623282_stats.db.zip') + .set('Content-Type', 'application/json') + .set('Content-Length', stats['size']) + + const stream = fs.createReadStream('test/1468852623282_stats.db.zip') + stream.pipe(req) + + stream.on('end', function () { + req.expect(200) + req.end(function (err, res) { + if (err) return done(err) + + rmdir('test/tmp') + done() + }) + }) + }) + }) + }) + + describe('upload json', function () { + it('should return 200 on upload', function (done) { + // check file size + const json = '{"foo":"bar"}' + + // upload req /:version/:deviceid/:filename + const req = request(sails.hooks.http.app) + .post('/1234/5678/1468852623282_adapterid_type.json') + .set('Content-Type', 'application/json') + .send(json) + .expect(200) + .end(function (err, res) { + if (err) return done(err) + + fs.readFile('test/tmp/5678/1234/2016/29/1468852623282_adapterid_type.json', 'utf8', function (err, content) { + assert.equal(content, json) + + rmdir('test/tmp') + done() + }) + }) + }) + }) +}) diff --git a/app/test/bootstrap.js b/app/test/bootstrap.js index fb602a6..b9f8f18 100644 --- a/app/test/bootstrap.js +++ b/app/test/bootstrap.js @@ -1,20 +1,24 @@ -var sails = require('sails'); +/* global before after */ -before(function(done) { +const sails = require('sails') +before(function (done) { // Increase the Mocha timeout so that Sails has enough time to lift. - this.timeout(5000); + this.timeout(5000) sails.lift({ // configuration for testing purposes - }, function(err, server) { - if (err) return done(err); + upload: { + datadir: 'test/tmp' + } + }, function (err, server) { + if (err) return done(err) // here you can load fixtures, etc. - done(err, sails); - }); -}); + done(err, sails) + }) +}) -after(function(done) { +after(function (done) { // here you can clear fixtures, etc. - sails.lower(done); -}); + sails.lower(done) +}) diff --git a/dev.yml b/dev.yml index 2e4af5a..8991e09 100644 --- a/dev.yml +++ b/dev.yml @@ -1,11 +1,11 @@ upload: build: . ports: - - "1337:1337" + - "1337:1337" environment: - NODE_ENV: development - sails_upload__datadir: /data + NODE_ENV: development + sails_upload__datadir: /data volumes: - - ./data:/data - - ./app:/app - - /app/node_modules \ No newline at end of file + - ./data:/data + - ./app:/app + - /app/node_modules \ No newline at end of file diff --git a/processes.json b/processes.json index 476e8b9..e5e7a91 100644 --- a/processes.json +++ b/processes.json @@ -1,19 +1,19 @@ { - "apps" : [{ - "name" : "hostviewupload", - "script" : "app.js", - "instances" : "4", - "exec_mode" : "cluster_mode", - "error_file" : "/home/nodeapp/logs/hostviewupload.log", - "out_file" : "/home/nodeapp/logs/hostviewupload.log", - "pid_file" : "/home/nodeapp/pids/hostviewupload", - "run_as_user" : "nodeapp", - "run_as_group" : "nodeapp", - "cwd" : "/home/nodeapp/apps/hostviewupload/app", + "apps": [{ + "name": "hostviewupload", + "script": "app.js", + "instances": "4", + "exec_mode": "cluster_mode", + "error_file": "/home/nodeapp/logs/hostviewupload.log", + "out_file": "/home/nodeapp/logs/hostviewupload.log", + "pid_file": "/home/nodeapp/pids/hostviewupload", + "run_as_user": "nodeapp", + "run_as_group": "nodeapp", + "cwd": "/home/nodeapp/apps/hostviewupload/app", "env": { - "NODE_ENV" : "production", - "PORT" : "3100", - "sails_upload__datadir" : "/home/nodeapp/hostviewdata" + "NODE_ENV": "production", + "PORT": "3100", + "sails_upload__datadir": "/home/nodeapp/hostviewdata" } }] } \ No newline at end of file diff --git a/prod.yml b/prod.yml index 647f711..b91a3da 100644 --- a/prod.yml +++ b/prod.yml @@ -6,47 +6,47 @@ services: networks: - backtier volumes: - - ./proxy/nginx.conf:/etc/nginx/nginx.conf - - ./proxy/default.conf:/etc/nginx/conf.d/default.conf + - ./proxy/nginx.conf:/etc/nginx/nginx.conf + - ./proxy/default.conf:/etc/nginx/conf.d/default.conf ports: - - "3100:80" + - "3100:80" links: - - upload1 - - upload2 - - upload3 + - upload1 + - upload2 + - upload3 upload1: build: . image: hostview/upload volumes: - - ./data:/data + - ./data:/data networks: - - backtier + - backtier environment: - NODE_ENV: production - sails_upload__datadir: /data + NODE_ENV: production + sails_upload__datadir: /data upload2: build: . image: hostview/upload volumes: - - ./data:/data + - ./data:/data networks: - - backtier + - backtier environment: - NODE_ENV: production - sails_upload__datadir: /data + NODE_ENV: production + sails_upload__datadir: /data upload3: build: . image: hostview/upload volumes: - - ./data:/data + - ./data:/data networks: - - backtier + - backtier environment: - NODE_ENV: production - sails_upload__datadir: /data + NODE_ENV: production + sails_upload__datadir: /data networks: backtier: