From 8660553d0f4633aeb950b8484c93b15425fe3ffa Mon Sep 17 00:00:00 2001 From: baldo Date: Sat, 23 Jul 2016 11:30:41 +0200 Subject: [PATCH] Filtering of nodes in admin panel. --- admin/js/main.js | 32 ++++++- admin/views/dashboardStats.html | 118 +++++++++++++------------ server/resources/mailResource.js | 2 +- server/resources/monitoringResource.js | 2 +- server/resources/nodeResource.js | 2 +- server/resources/taskResource.js | 2 +- server/services/nodeService.js | 2 +- server/utils/resources.js | 72 +++++++++++---- shared/validation/constraints.js | 20 +++++ 9 files changed, 176 insertions(+), 76 deletions(-) diff --git a/admin/js/main.js b/admin/js/main.js index 8c9ef61..6975ce7 100644 --- a/admin/js/main.js +++ b/admin/js/main.js @@ -122,14 +122,44 @@ angular.module('ffffngAdmin').config(function(NgAdminConfigurationProvider, Rest }) ]) .filters([ - nga.field('q') + nga.field('q', 'template') .label('') .pinned(true) .template( '
' + '' + '
'), + nga.field('hasKey', 'choice') + .label('VPN key') + .pinned(false) + .choices([ + { label: 'VPN key set', value: true }, + { label: 'no VPN key', value: false } + ]), + nga.field('hasCoords', 'choice') + .label('GPS coordinates') + .pinned(false) + .choices([ + { label: 'coordinates set', value: true }, + { label: 'no coordinates', value: false } + ]), + nga.field('onlineState', 'choice') + .label('Online state') + .pinned(false) + .choices([ + { label: 'online', value: 'ONLINE' }, + { label: 'offline', value: 'OFFLINE' } + ]), + nga.field('monitoringState', 'choice') + .label('Monitoring') + .pinned(false) + .choices([ + { label: 'pending', value: 'pending' }, + { label: 'active', value: 'active' }, + { label: 'disabled', value: 'disabled' } + ]) ]) + .actions(['']) .listActions( ' ' + ' ' + diff --git a/admin/views/dashboardStats.html b/admin/views/dashboardStats.html index ffaaa17..e6fe0eb 100644 --- a/admin/views/dashboardStats.html +++ b/admin/views/dashboardStats.html @@ -2,87 +2,95 @@

Dashboard / Statistics

-

Nodes

+

Nodes

-
-
-
-
- -
-
-
{{ stats.nodes.registered }}
-
Total Registered
+ +
+
+
+
+ +
+
+
{{ stats.nodes.registered }}
+
Total Registered
+
-
+
-
-
-
-
- -
-
-
{{ stats.nodes.withVPN }}
-
With VPN-Key
+ +
+
+
+
+ +
+
+
{{ stats.nodes.withVPN }}
+
With VPN-Key
+
-
+
-
-
-
-
- -
-
-
{{ stats.nodes.withCoords }}
-
With Coordinates
+ +
+
+
+
+ +
+
+
{{ stats.nodes.withCoords }}
+
With Coordinates
+
-
+
-

Monitoring

-
-
-
-
-
-
- -
- -
-
-
- diff --git a/server/resources/mailResource.js b/server/resources/mailResource.js index 079a973..a970581 100644 --- a/server/resources/mailResource.js +++ b/server/resources/mailResource.js @@ -44,7 +44,7 @@ angular.module('ffffng').factory('MailResource', function ( }, getAll: function (req, res) { - Resources.getValidRestParams('list', req, function (err, restParams) { + Resources.getValidRestParams('list', null, req, function (err, restParams) { if (err) { return Resources.error(res, err); } diff --git a/server/resources/monitoringResource.js b/server/resources/monitoringResource.js index 981f9a9..f746341 100644 --- a/server/resources/monitoringResource.js +++ b/server/resources/monitoringResource.js @@ -14,7 +14,7 @@ angular.module('ffffng').factory('MonitoringResource', function ( return { getAll: function (req, res) { - Resources.getValidRestParams('list', req, function (err, restParams) { + Resources.getValidRestParams('list', null, req, function (err, restParams) { if (err) { return Resources.error(res, err); } diff --git a/server/resources/nodeResource.js b/server/resources/nodeResource.js index 01fdd52..a2b6898 100644 --- a/server/resources/nodeResource.js +++ b/server/resources/nodeResource.js @@ -98,7 +98,7 @@ angular.module('ffffng').factory('NodeResource', function ( }, getAll: function (req, res) { - Resources.getValidRestParams('list', req, function (err, restParams) { + Resources.getValidRestParams('list', 'node', req, function (err, restParams) { if (err) { return Resources.error(res, err); } diff --git a/server/resources/taskResource.js b/server/resources/taskResource.js index 698c134..009b2ec 100644 --- a/server/resources/taskResource.js +++ b/server/resources/taskResource.js @@ -75,7 +75,7 @@ angular.module('ffffng').factory('TaskResource', function ( return { getAll: function (req, res) { - Resources.getValidRestParams('list', req, function (err, restParams) { + Resources.getValidRestParams('list', null, req, function (err, restParams) { if (err) { return Resources.error(res, err); } diff --git a/server/services/nodeService.js b/server/services/nodeService.js index de8e95e..461e280 100644 --- a/server/services/nodeService.js +++ b/server/services/nodeService.js @@ -263,7 +263,7 @@ angular.module('ffffng') var pending = value === 'pending'; node.monitoring = active || pending; node.monitoringConfirmed = active; - node.monitoringState = active ? 'active' : (pending ? 'pending' : ''); + node.monitoringState = active ? 'active' : (pending ? 'pending' : 'disabled'); } else if (key === 'monitoringToken') { nodeSecrets.monitoringToken = value; } else { diff --git a/server/utils/resources.js b/server/utils/resources.js index 353995f..02fe5e4 100644 --- a/server/utils/resources.js +++ b/server/utils/resources.js @@ -70,45 +70,64 @@ angular.module('ffffng').factory('Resources', function (_, Constraints, Validato }; } + function getConstrainedValues(data, constraints) { + var values = {}; + _.each(_.keys(constraints), function (key) { + var value = data[key]; + values[key] = _.isUndefined(value) && !_.isUndefined(constraints[key].default) + ? constraints[key].default + : value; + }); + return values; + } + return { getData: function (req) { return _.extend({}, req.body, req.params, req.query); }, - getValidRestParams: function(type, req, callback) { + getValidRestParams: function(type, subtype, req, callback) { var constraints = Constraints.rest[type]; if (!_.isPlainObject(constraints)) { Logger.tag('validation', 'rest').error('Unknown REST resource type: {}', type); return callback({data: 'Internal error.', type: ErrorTypes.internalError}); } + var filterConstraints = {}; + if (subtype) { + filterConstraints = Constraints[subtype + 'Filters']; + if (!_.isPlainObject(filterConstraints)) { + Logger.tag('validation', 'rest').error('Unknown REST resource subtype: {}', subtype); + return callback({data: 'Internal error.', type: ErrorTypes.internalError}); + } + } + var data = this.getData(req); - var restParams = {}; - _.each(_.keys(constraints), function (key) { - var value = data[key]; - restParams[key] = _.isUndefined(value) && !_.isUndefined(constraints[key].default) - ? constraints[key].default - : value; - }); + var restParams = getConstrainedValues(data, constraints); + var filterParams = getConstrainedValues(data, filterConstraints); var areValidParams = Validator.forConstraints(constraints); - if (!areValidParams(restParams)) { + var areValidFilters = Validator.forConstraints(filterConstraints); + if (!areValidParams(restParams) || !areValidFilters(filterParams)) { return callback({data: 'Invalid REST parameters.', type: ErrorTypes.badRequest}); } + restParams.filters = filterParams; + callback(null, restParams); }, filter: function (entities, allowedFilterFields, restParams) { var query = restParams.q; - if (!query) { - return entities; + if (query) { + query = _.toLower(query.trim()); } - query = _.toLower(query.trim()); - - return _.filter(entities, function (entity) { + function queryMatches(entity) { + if (!query) { + return true; + } return _.some(allowedFilterFields, function (field) { var value = entity[field]; if (_.isNumber(value)) { @@ -125,7 +144,30 @@ angular.module('ffffng').factory('Resources', function (_, Constraints, Validato } return _.includes(value, query); - }) + }); + } + + var filters = restParams.filters; + + function filtersMatch(entity) { + if (_.isEmpty(filters)) { + return true; + } + + return _.every(filters, function (value, key) { + if (_.isUndefined(value)) { + return true; + } + if (_.startsWith(key, 'has')) { + var entityKey = key.substr(3, 1).toLowerCase() + key.substr(4); + return _.isEmpty(entity[entityKey]).toString() !== value; + } + return entity[key] === value; + }); + } + + return _.filter(entities, function (entity) { + return queryMatches(entity) && filtersMatch(entity); }); }, diff --git a/shared/validation/constraints.js b/shared/validation/constraints.js index 613d625..04ab161 100644 --- a/shared/validation/constraints.js +++ b/shared/validation/constraints.js @@ -47,6 +47,26 @@ angular.module('ffffng').constant('Constraints', { optional: false } }, + nodeFilters: { + hasKey: { + type: 'boolean', + optional: true + }, + hasCoords: { + type: 'boolean', + optional: true + }, + onlineState: { + type: 'string', + regex: /^(ONLINE|OFFLINE)$/, + optional: true + }, + monitoringState: { + type: 'string', + regex: /^(disabled|active|pending)$/, + optional: true + } + }, rest: { list: { _page: {