Loading public/css/app.css +25 −0 Original line number Diff line number Diff line Loading @@ -108,3 +108,28 @@ table.shard-map { table-layout: fixed; } .info-text { color: #6F7579; } .noselect { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .empty-stat { font-size: 36px; font-weight: 300; text-align: center; line-height: 40px; min-width: 70px; margin-right: 15px; } .main-stat { font-size: 36px; font-weight: 300; float: left; line-height: 40px; min-width: 70px; margin-right: 15px; } .detail-stat { float: left; line-height: 20px; } .checkbox inline { padding-right: 6px; } No newline at end of file public/js/app.js +160 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,10 @@ angular.module('cerebro', ['ngRoute', 'ngAnimate', 'ui.bootstrap']) templateUrl: 'overview.html', controller: 'OverviewController' }) .when('/nodes', { templateUrl: 'nodes/index.html', controller: 'NodesController' }) .when('/connect', { templateUrl: 'connect.html', controller: 'ConnectController' Loading Loading @@ -679,6 +683,82 @@ angular.module('cerebro').controller('NavbarController', ['$scope', '$http', } ]); angular.module('cerebro').controller('NodesController', ['$scope', 'NodesDataService', 'AlertService', 'RefreshService', function($scope, NodesDataService, AlertService, RefreshService) { $scope._nodes = undefined; // keeps unfiltered list of nodes $scope.nodes = undefined; $scope.sortBy = 'name'; $scope.reverse = false; $scope.filter = new NodeFilter('', true, true, true, 0); $scope.$watch( function() { return RefreshService.lastUpdate(); }, function() { $scope.refresh(); }, true ); $scope.$watch('filter', function() { $scope.refreshVisibleNodes(); }, true); $scope.setSortBy = function(field) { if ($scope.sortBy === field) { $scope.reverse = !$scope.reverse; } $scope.sortBy = field; }; $scope.refreshVisibleNodes = function() { if ($scope._nodes) { $scope.nodes = $scope._nodes.filter(function(node) { return $scope.filter.matches(node); }); } else { $scope.nodes = undefined; } }; $scope.setNodes = function(nodes) { $scope._nodes = nodes; $scope.refreshVisibleNodes(); }; $scope.refresh = function() { NodesDataService.load( function(nodes) { $scope.setNodes(nodes); }, function(error) { $scope.setNodes(undefined); AlertService.error('Error while loading nodes data', error); } ); }; }] ); angular.module('cerebro').factory('NodesDataService', ['DataService', function(DataService) { this.load = function(success, error) { DataService.send('/nodes', {}, success, error); }; return this; } ]); angular.module('cerebro').controller('OverviewController', ['$scope', '$http', '$window', '$location', 'DataService', 'AlertService', 'ModalService', 'RefreshService', Loading Loading @@ -2027,6 +2107,40 @@ angular.module('cerebro').filter('startsWith', function() { }; }); angular.module('cerebro').filter('timeInterval', function() { var UNITS = ['yr', 'mo', 'd', 'h', 'min']; var UNIT_MEASURE = { yr: 31536000000, mo: 2678400000, wk: 604800000, d: 86400000, h: 3600000, min: 60000 }; function stringify(seconds) { var result = 'less than a minute'; for (var idx = 0; idx < UNITS.length; idx++) { var amount = Math.floor(seconds / UNIT_MEASURE[UNITS[idx]]); if (amount) { result = amount + UNITS[idx] + '.'; break; } } return result; } return function(seconds) { return stringify(seconds); }; }); function URLAutocomplete(mappings) { var PATHS = [ Loading Loading @@ -2168,6 +2282,52 @@ angular.module('cerebro').directive('ngPlainInclude', function() { }; }); angular.module('cerebro').directive('ngSortBy', function() { function updateSortingIcon(scope, elem, attrs) { var sorts = scope.sortBy === attrs.property; var sortIcon = elem.find('i'); sortIcon.removeClass('fa-sort-asc fa-sort-desc'); if (sorts) { if (scope.reverse) { sortIcon.addClass('fa-sort-desc'); } else { sortIcon.addClass('fa-sort-asc'); } } } function link(scope, elem, attrs) { scope.$watch( function() { return scope.sortBy; }, function() { updateSortingIcon(scope, elem, attrs); }); scope.$watch( function() { return scope.reverse; }, function() { updateSortingIcon(scope, elem, attrs); } ); } return { link: link, template: function(elem, attrs) { return '<a href="" target="_self" ng-click=setSortBy(\'' + attrs.property + '\')>' + attrs.text + '<i class="fa fa-fw fa-sort-asc"></i></a>'; } }; } ); angular.module('cerebro').factory('AceEditorService', function() { this.init = function(name) { Loading public/navbar.html +1 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ <div id="navbar" class="navbar-collapse collapse"> <ul class="nav navbar-nav" ng-show="host"> <li><a class="nav-item nav-link" ng-href="#overview?host={{host}}"><i class="fa fa-sitemap"></i> overview</a></li> <li><a class="nav-item nav-link" ng-href="#nodes?host={{host}}"><i class="fa fa-server"></i> nodes</a></li> <li><a class="nav-item nav-link" ng-href="#rest?host={{host}}"><i class="fa fa-edit"></i> rest</a></li> <li> <a target="_self" href="#" data-toggle="dropdown" class="dropdown-toggle"> Loading public/nodes/index.html 0 → 100644 +126 −0 Original line number Diff line number Diff line <div class="row"> <div class="col-lg-3 col-md-4 col-sm-5 col-xs-12 form-group"> <input type="text" ng-model="filter.name" class="form-control form-control-sm" placeholder="filter nodes by name"> </div> <div class="col-lg-6 col-md-6 col-sm-6 col-xs-12 noselect"> <div class="checkbox"> <label class="checkbox-inline"> <input type="checkbox" ng-model="filter.master"> <i class="fa fa-star"></i> master </label> <label class="checkbox-inline"> <input type="checkbox" ng-model="filter.data"> <i class="fa fa-hdd-o"></i> data </label> <label class="checkbox-inline"> <input type="checkbox" ng-model="filter.client"> <i class="fa fa-search"></i> client </label> </div> </div> </div> <table class="table table-bordered"> <thead> <tr> <th> <ng-sort-by property="name" text="name"></ng-sort-by> </th> <th> <ng-sort-by property="cpu.load" text="load"></ng-sort-by> </th> <th> <ng-sort-by property="cpu.process" text="process cpu %"></ng-sort-by> </th> <th> <ng-sort-by property="heap.percent" text="heap usage %"></ng-sort-by> </th> <th> <ng-sort-by property="disk.percent" text="disk usage %"></ng-sort-by> </th> <th> <ng-sort-by property="uptime" text="uptime"></ng-sort-by> </th> </tr> </thead> <tbody> <tr ng-repeat="node in nodes | orderBy:sortBy:reverse track by $index"> <td> <div class="row"> <div class="col-lg-12"> <div class="node-badges title"> <div ng-show="node.master"> <i ng-show="node.current_master" class="fa fa-fw fa-star" title="current master"></i> <i ng-show="!node.current_master" class="fa fa-fw fa-star-o" title="master eligible"></i> </div> <div ng-show="node.data"> <i class="fa fa-fw fa-hdd-o" title="data node"></i> </div> <div ng-show="node.client"> <i class="fa fa-fw fa-search" title="client node"></i> </div> <div ng-show="node.ingest"> <i class="fa fa-fw fa-crop" title="ingest node"></i> </div> </div> <div class="node-info"> <div class="title"> <span> {{node.name}} </span> </div> <div> <small>{{node.host}}</small> </div> </div> </div> </div> <div class="node-labels"> <span class="label label-details">JVM: {{node.jvm}}</span> <span class="label label-details">ES: {{node.version}}</span> </div> </td> <td> <div> <span class="main-stat">{{node.cpu.load | number:2}}</span> </div> </td> <td> <div> <span class="main-stat">{{node.cpu.process}}%</span> <span class="detail-stat"> <div>os cpu: {{node.cpu.os}}%</div> </span> </div> </td> <td> <div> <span class="main-stat">{{node.heap.percent}}%</span> <span class="detail-stat"> <div>used: {{node.heap.used}}</div> <div>max: {{node.heap.max}}</div> </span> </div> </td> <td> <div ng-show="node.disk.percent"> <span class="main-stat">{{node.disk.percent}}%</span> <span class="detail-stat"> <div>available: {{node.disk.available | bytes}}</div> <div>total: {{node.disk.total | bytes}}</div> </span> </div> <div class="empty-stat" ng-hide="node.disk.percent"> - </div> </td> <td> <div> <span class="main-stat">{{node.uptime | timeInterval}}</span> </div> </td> </tr> </tbody> </table> No newline at end of file src/app/app.routes.js +4 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,10 @@ angular.module('cerebro', ['ngRoute', 'ngAnimate', 'ui.bootstrap']) templateUrl: 'overview.html', controller: 'OverviewController' }) .when('/nodes', { templateUrl: 'nodes/index.html', controller: 'NodesController' }) .when('/connect', { templateUrl: 'connect.html', controller: 'ConnectController' Loading Loading
public/css/app.css +25 −0 Original line number Diff line number Diff line Loading @@ -108,3 +108,28 @@ table.shard-map { table-layout: fixed; } .info-text { color: #6F7579; } .noselect { -webkit-touch-callout: none; -webkit-user-select: none; -khtml-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .empty-stat { font-size: 36px; font-weight: 300; text-align: center; line-height: 40px; min-width: 70px; margin-right: 15px; } .main-stat { font-size: 36px; font-weight: 300; float: left; line-height: 40px; min-width: 70px; margin-right: 15px; } .detail-stat { float: left; line-height: 20px; } .checkbox inline { padding-right: 6px; } No newline at end of file
public/js/app.js +160 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,10 @@ angular.module('cerebro', ['ngRoute', 'ngAnimate', 'ui.bootstrap']) templateUrl: 'overview.html', controller: 'OverviewController' }) .when('/nodes', { templateUrl: 'nodes/index.html', controller: 'NodesController' }) .when('/connect', { templateUrl: 'connect.html', controller: 'ConnectController' Loading Loading @@ -679,6 +683,82 @@ angular.module('cerebro').controller('NavbarController', ['$scope', '$http', } ]); angular.module('cerebro').controller('NodesController', ['$scope', 'NodesDataService', 'AlertService', 'RefreshService', function($scope, NodesDataService, AlertService, RefreshService) { $scope._nodes = undefined; // keeps unfiltered list of nodes $scope.nodes = undefined; $scope.sortBy = 'name'; $scope.reverse = false; $scope.filter = new NodeFilter('', true, true, true, 0); $scope.$watch( function() { return RefreshService.lastUpdate(); }, function() { $scope.refresh(); }, true ); $scope.$watch('filter', function() { $scope.refreshVisibleNodes(); }, true); $scope.setSortBy = function(field) { if ($scope.sortBy === field) { $scope.reverse = !$scope.reverse; } $scope.sortBy = field; }; $scope.refreshVisibleNodes = function() { if ($scope._nodes) { $scope.nodes = $scope._nodes.filter(function(node) { return $scope.filter.matches(node); }); } else { $scope.nodes = undefined; } }; $scope.setNodes = function(nodes) { $scope._nodes = nodes; $scope.refreshVisibleNodes(); }; $scope.refresh = function() { NodesDataService.load( function(nodes) { $scope.setNodes(nodes); }, function(error) { $scope.setNodes(undefined); AlertService.error('Error while loading nodes data', error); } ); }; }] ); angular.module('cerebro').factory('NodesDataService', ['DataService', function(DataService) { this.load = function(success, error) { DataService.send('/nodes', {}, success, error); }; return this; } ]); angular.module('cerebro').controller('OverviewController', ['$scope', '$http', '$window', '$location', 'DataService', 'AlertService', 'ModalService', 'RefreshService', Loading Loading @@ -2027,6 +2107,40 @@ angular.module('cerebro').filter('startsWith', function() { }; }); angular.module('cerebro').filter('timeInterval', function() { var UNITS = ['yr', 'mo', 'd', 'h', 'min']; var UNIT_MEASURE = { yr: 31536000000, mo: 2678400000, wk: 604800000, d: 86400000, h: 3600000, min: 60000 }; function stringify(seconds) { var result = 'less than a minute'; for (var idx = 0; idx < UNITS.length; idx++) { var amount = Math.floor(seconds / UNIT_MEASURE[UNITS[idx]]); if (amount) { result = amount + UNITS[idx] + '.'; break; } } return result; } return function(seconds) { return stringify(seconds); }; }); function URLAutocomplete(mappings) { var PATHS = [ Loading Loading @@ -2168,6 +2282,52 @@ angular.module('cerebro').directive('ngPlainInclude', function() { }; }); angular.module('cerebro').directive('ngSortBy', function() { function updateSortingIcon(scope, elem, attrs) { var sorts = scope.sortBy === attrs.property; var sortIcon = elem.find('i'); sortIcon.removeClass('fa-sort-asc fa-sort-desc'); if (sorts) { if (scope.reverse) { sortIcon.addClass('fa-sort-desc'); } else { sortIcon.addClass('fa-sort-asc'); } } } function link(scope, elem, attrs) { scope.$watch( function() { return scope.sortBy; }, function() { updateSortingIcon(scope, elem, attrs); }); scope.$watch( function() { return scope.reverse; }, function() { updateSortingIcon(scope, elem, attrs); } ); } return { link: link, template: function(elem, attrs) { return '<a href="" target="_self" ng-click=setSortBy(\'' + attrs.property + '\')>' + attrs.text + '<i class="fa fa-fw fa-sort-asc"></i></a>'; } }; } ); angular.module('cerebro').factory('AceEditorService', function() { this.init = function(name) { Loading
public/navbar.html +1 −0 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ <div id="navbar" class="navbar-collapse collapse"> <ul class="nav navbar-nav" ng-show="host"> <li><a class="nav-item nav-link" ng-href="#overview?host={{host}}"><i class="fa fa-sitemap"></i> overview</a></li> <li><a class="nav-item nav-link" ng-href="#nodes?host={{host}}"><i class="fa fa-server"></i> nodes</a></li> <li><a class="nav-item nav-link" ng-href="#rest?host={{host}}"><i class="fa fa-edit"></i> rest</a></li> <li> <a target="_self" href="#" data-toggle="dropdown" class="dropdown-toggle"> Loading
public/nodes/index.html 0 → 100644 +126 −0 Original line number Diff line number Diff line <div class="row"> <div class="col-lg-3 col-md-4 col-sm-5 col-xs-12 form-group"> <input type="text" ng-model="filter.name" class="form-control form-control-sm" placeholder="filter nodes by name"> </div> <div class="col-lg-6 col-md-6 col-sm-6 col-xs-12 noselect"> <div class="checkbox"> <label class="checkbox-inline"> <input type="checkbox" ng-model="filter.master"> <i class="fa fa-star"></i> master </label> <label class="checkbox-inline"> <input type="checkbox" ng-model="filter.data"> <i class="fa fa-hdd-o"></i> data </label> <label class="checkbox-inline"> <input type="checkbox" ng-model="filter.client"> <i class="fa fa-search"></i> client </label> </div> </div> </div> <table class="table table-bordered"> <thead> <tr> <th> <ng-sort-by property="name" text="name"></ng-sort-by> </th> <th> <ng-sort-by property="cpu.load" text="load"></ng-sort-by> </th> <th> <ng-sort-by property="cpu.process" text="process cpu %"></ng-sort-by> </th> <th> <ng-sort-by property="heap.percent" text="heap usage %"></ng-sort-by> </th> <th> <ng-sort-by property="disk.percent" text="disk usage %"></ng-sort-by> </th> <th> <ng-sort-by property="uptime" text="uptime"></ng-sort-by> </th> </tr> </thead> <tbody> <tr ng-repeat="node in nodes | orderBy:sortBy:reverse track by $index"> <td> <div class="row"> <div class="col-lg-12"> <div class="node-badges title"> <div ng-show="node.master"> <i ng-show="node.current_master" class="fa fa-fw fa-star" title="current master"></i> <i ng-show="!node.current_master" class="fa fa-fw fa-star-o" title="master eligible"></i> </div> <div ng-show="node.data"> <i class="fa fa-fw fa-hdd-o" title="data node"></i> </div> <div ng-show="node.client"> <i class="fa fa-fw fa-search" title="client node"></i> </div> <div ng-show="node.ingest"> <i class="fa fa-fw fa-crop" title="ingest node"></i> </div> </div> <div class="node-info"> <div class="title"> <span> {{node.name}} </span> </div> <div> <small>{{node.host}}</small> </div> </div> </div> </div> <div class="node-labels"> <span class="label label-details">JVM: {{node.jvm}}</span> <span class="label label-details">ES: {{node.version}}</span> </div> </td> <td> <div> <span class="main-stat">{{node.cpu.load | number:2}}</span> </div> </td> <td> <div> <span class="main-stat">{{node.cpu.process}}%</span> <span class="detail-stat"> <div>os cpu: {{node.cpu.os}}%</div> </span> </div> </td> <td> <div> <span class="main-stat">{{node.heap.percent}}%</span> <span class="detail-stat"> <div>used: {{node.heap.used}}</div> <div>max: {{node.heap.max}}</div> </span> </div> </td> <td> <div ng-show="node.disk.percent"> <span class="main-stat">{{node.disk.percent}}%</span> <span class="detail-stat"> <div>available: {{node.disk.available | bytes}}</div> <div>total: {{node.disk.total | bytes}}</div> </span> </div> <div class="empty-stat" ng-hide="node.disk.percent"> - </div> </td> <td> <div> <span class="main-stat">{{node.uptime | timeInterval}}</span> </div> </td> </tr> </tbody> </table> No newline at end of file
src/app/app.routes.js +4 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,10 @@ angular.module('cerebro', ['ngRoute', 'ngAnimate', 'ui.bootstrap']) templateUrl: 'overview.html', controller: 'OverviewController' }) .when('/nodes', { templateUrl: 'nodes/index.html', controller: 'NodesController' }) .when('/connect', { templateUrl: 'connect.html', controller: 'ConnectController' Loading