diff --git a/admin/js/config.js b/admin/js/config.js index 53376f3..e624f11 100644 --- a/admin/js/config.js +++ b/admin/js/config.js @@ -35,6 +35,8 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Cons .listView() .title('Nodes') .perPage(30) + .sortDir('ASC') + .sortField('hostname') .actions([]) .batchActions([]) .exportFields([]) @@ -52,13 +54,17 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Cons ? '' : ''; }), - nga.field('monitoring').cssClasses(nodeClasses).template(function (node) { - if (!node.values.monitoring) { - return ''; + nga.field('monitoringState').cssClasses(nodeClasses).template(function (node) { + switch (node.values.monitoringState) { + case 'active': + return ''; + + case 'pending': + return ''; + + default: + return ''; } - return node.values.monitoringConfirmed - ? '' - : ''; }) ]) .listActions([ @@ -104,6 +110,8 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Cons .listView() .title('Background-Jobs') .perPage(30) + .sortDir('ASC') + .sortField('id') .actions([]) .batchActions([]) .exportFields([]) diff --git a/server/resources/nodeResource.js b/server/resources/nodeResource.js index 8215471..c30d27d 100644 --- a/server/resources/nodeResource.js +++ b/server/resources/nodeResource.js @@ -100,15 +100,22 @@ angular.module('ffffng').factory('NodeResource', function ( return Resources.error(res, err); } - // TODO: Sort + Filter + // TODO: Filter - return NodeService.getAllNodes(restParams._page, restParams._perPage, function (err, nodes, total) { + return NodeService.getAllNodes(function (err, nodes, total) { if (err) { return Resources.error(res, err); } + var sortedNodes = Resources.sort( + nodes, + ['token', 'mac', 'hostname', 'key', 'coords', 'monitoringState'], + restParams + ); + var pageNodes = Resources.getPageEntities(sortedNodes, restParams); + res.set('X-Total-Count', total); - return Resources.success(res, nodes); + return Resources.success(res, pageNodes); }); }); } diff --git a/server/resources/taskResource.js b/server/resources/taskResource.js index b2ef6bd..db09100 100644 --- a/server/resources/taskResource.js +++ b/server/resources/taskResource.js @@ -79,15 +79,14 @@ angular.module('ffffng').factory('TaskResource', function ( return Resources.error(res, err); } - // TODO: Sort - - var tasks = _.values(Scheduler.getTasks()); + var tasks = Resources.sort( + _.values(Scheduler.getTasks()), + ['id', 'name', 'schedule', 'state', 'runningSince', 'lastRunStarted'], + restParams + ); var total = tasks.length; - var page = restParams._page; - var perPage = restParams._perPage; - - var pageTasks = tasks.slice((page - 1) * perPage, page * perPage); + var pageTasks = Resources.getPageEntities(tasks, restParams); res.set('X-Total-Count', total); return Resources.success(res, _.map(pageTasks, toExternalTask)); diff --git a/server/services/nodeService.js b/server/services/nodeService.js index fd53c83..7c4300b 100644 --- a/server/services/nodeService.js +++ b/server/services/nodeService.js @@ -217,6 +217,7 @@ angular.module('ffffng') var pending = value === 'pending'; node.monitoring = active || pending; node.monitoringConfirmed = active; + node.monitoringState = active ? 'active' : (pending ? 'pending' : ''); } else if (key === 'monitoringToken') { nodeSecrets.monitoringToken = value; } else { @@ -369,13 +370,12 @@ angular.module('ffffng') deleteNodeFile(token, callback); }, - getAllNodes: function (page, perPage, callback) { - var files = _.sortBy(findNodeFiles({})); + getAllNodes: function (callback) { + var files = findNodeFiles({}); var total = files.length; - var pageFiles = files.slice((page - 1) * perPage, page * perPage); async.mapLimit( - pageFiles, + files, MAX_PARALLEL_NODES_PARSING, parseNodeFile, function (err, nodes) { diff --git a/server/utils/resources.js b/server/utils/resources.js index fb634d2..2f09730 100644 --- a/server/utils/resources.js +++ b/server/utils/resources.js @@ -36,6 +36,24 @@ angular.module('ffffng').factory('Resources', function (_, Constraints, Validato callback(null, restParams); }, + sort: function (entities, allowedSortFields, restParams) { + var sortField = _.indexOf(allowedSortFields, restParams._sortField) >= 0 ? restParams._sortField : undefined; + if (!sortField) { + return entities; + } + + var sorted = _.sortBy(entities, [sortField]); + + return restParams._sortDir === 'ASC' ? sorted : _.reverse(sorted); + }, + + getPageEntities: function (entities, restParams) { + var page = restParams._page; + var perPage = restParams._perPage; + + return entities.slice((page - 1) * perPage, page * perPage); + }, + success: function (res, data) { respond(res, 200, data); }, diff --git a/server/validation/validator.js b/server/validation/validator.js index d597d45..92ce7a8 100644 --- a/server/validation/validator.js +++ b/server/validation/validator.js @@ -1,8 +1,10 @@ 'use strict'; angular.module('ffffng').factory('Validator', function (_, Strings, Logger) { + // TODO: sanitize input for further processing as specified by constraints (correct types, trimming, etc.) + function isValidBoolean(value) { - return _.isBoolean(value); + return _.isBoolean(value) || value === 'true' || value === 'false'; } function isValidNumber(constraint, value) { @@ -29,6 +31,14 @@ angular.module('ffffng').factory('Validator', function (_, Strings, Logger) { return true; } + function isValidEnum(constraint, value) { + if (!_.isString(value)) { + return false; + } + + return _.indexOf(constraint.allowed, value) >= 0; + } + function isValidString(constraint, value) { if (!_.isString(value)) { return false; @@ -50,6 +60,9 @@ angular.module('ffffng').factory('Validator', function (_, Strings, Logger) { case 'number': return isValidNumber(constraint, value); + case 'enum': + return isValidEnum(constraint, value); + case 'string': return isValidString(constraint, value); } diff --git a/shared/validation/constraints.js b/shared/validation/constraints.js index 4c8be92..20e864c 100644 --- a/shared/validation/constraints.js +++ b/shared/validation/constraints.js @@ -61,6 +61,17 @@ angular.module('ffffng').constant('Constraints', { max: 50, optional: true, default: 20 + }, + _sortDir: { + type: 'enum', + allowed: ['ASC', 'DESC'], + optional: true, + default: 'ASC' + }, + _sortField: { + type: 'string', + regex: /^[a-zA-Z0-9_]{1,32}$/, + optional: true } } }