Refactor javascript, always show table, add sorting

This commit is contained in:
Audrius Butkevicius 2016-04-14 22:01:25 +01:00
parent 50068b0b0f
commit dd364c962f
2 changed files with 239 additions and 182 deletions

File diff suppressed because one or more lines are too long

View File

@ -19,9 +19,13 @@
display: none; display: none;
} }
table { table {
font-size: 11px; font-size: 11px !important;
width: 100%; width: 100%;
border: 1px; border: 1px;
}
td {
padding: 0px !important;
} }
tfoot td { tfoot td {
font-weight: bold; font-weight: bold;
@ -32,57 +36,125 @@
<body class="ng-cloak"> <body class="ng-cloak">
<div class="container"> <div class="container">
<h1>Relay Pool Data</h2> <h1>Relay Pool Data</h2>
<div ng-if="!started" class="text-center"> <div ng-if="relays === undefined" class="text-center">
<img src="//cdnjs.cloudflare.com/ajax/libs/galleriffic/2.0.1/css/loader.gif"/> <img src="//cdnjs.cloudflare.com/ajax/libs/galleriffic/2.0.1/css/loader.gif"/>
<p>Please wait while we gather data</p> <p>Please wait while we gather data</p>
<p ng-repeat="entry in progress" class="ng-cloak">{{ entry.name }}... <span ng-if="entry.done">Done!</span></p>
</div> </div>
<div> <div>
<div ng-show="started" class="ng-hide"> <div ng-show="relays !== undefined" class="ng-hide">
<p> <p>
Currently {{ relays.length }} relays online ({{ totals.goMaxProcs }} cores in total). Currently {{ relays.length }} relays online ({{ totals.goMaxProcs }} cores in total).
</p> </p>
</div> </div>
<div id="map"></div> <!-- Can't hide the map, otherwise it freaks out --> <div id="map"></div> <!-- Can't hide the map, otherwise it freaks out -->
<p ng-show="started" class="ng-hide">The circle size represents how much bytes the relay transfered relative to other relays</p> <p>The circle size represents how much bytes the relay transfered relative to other relays</p>
</div> </div>
<div ng-show="started" class="ng-hide"> <div>
<table> <table class="table table-striped table-condensed table">
<thead> <thead>
<tr> <tr>
<th colspan="4"></th> <th rowspan="2">Address</td>
<th colspan="6"">Transfer rate in the last period (per second)</th> <th rowspan="2">
<th colspan="2"></th> <a ng-click="sortType = 'status.numActiveSessions || 0'; sortReverse = !sortReverse">
<tr/> Sessions
<span ng-show="sortType == 'status.numActiveSessions || 0' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'status.numActiveSessions || 0' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th rowspan="2">
<a ng-click="sortType = 'status.numConnections || 0'; sortReverse = !sortReverse">
Connections
<span ng-show="sortType == 'status.numConnections || 0' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'status.numConnections || 0' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th rowspan="2">
<a ng-click="sortType = 'status.bytesProxied || 0'; sortReverse = !sortReverse">
Data relayed
<span ng-show="sortType == 'status.bytesProxied || 0' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'status.bytesProxied || 0' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th colspan="6" class="text-center">Transfer rate in the last period</th>
<th rowspan="2">
<a ng-click="sortType = 'status.uptimeSeconds || 0'; sortReverse = !sortReverse">
Uptime hours
<span ng-show="sortType == 'status.uptimeSeconds || 0' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'status.uptimeSeconds || 0' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th rowspan="2">
<a ng-click="sortType = 'status.options[\'provided-by\'] || \'\''; sortReverse = !sortReverse">
Provided by
<span ng-show="sortType == 'status.options[\'provided-by\'] || \'\'' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'status.options[\'provided-by\'] || \'\'' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
</tr>
<tr> <tr>
<th>Address</th> <th>
<th>Active Sessions</th> <a ng-click="sortType = 'status.kbps10s1m5m15m30m60m[0] || 0'; sortReverse = !sortReverse">
<th>Connections</th> 10s
<th>Data proxied</th> <span ng-show="sortType == 'status.kbps10s1m5m15m30m60m[0] || 0' && !sortReverse" class="fa fa-caret-down"></span>
<th>10s</th> <span ng-show="sortType == 'status.kbps10s1m5m15m30m60m[0] || 0' && sortReverse" class="fa fa-caret-up"></span>
<th>1m</th> </a>
<th>5m</th> </th>
<th>15m</th> <th>
<th>30m</th> <a ng-click="sortType = 'status.kbps10s1m5m15m30m60m[1] || 0'; sortReverse = !sortReverse">
<th>60m</th> 1m
<th>Uptime</th> <span ng-show="sortType == 'status.kbps10s1m5m15m30m60m[1] || 0' && !sortReverse" class="fa fa-caret-down"></span>
<th>Provided by</th> <span ng-show="sortType == 'status.kbps10s1m5m15m30m60m[1] || 0' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th>
<a ng-click="sortType = 'status.kbps10s1m5m15m30m60m[2] || 0'; sortReverse = !sortReverse">
5m
<span ng-show="sortType == 'status.kbps10s1m5m15m30m60m[2] || 0' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'status.kbps10s1m5m15m30m60m[2] || 0' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th>
<a ng-click="sortType = 'status.kbps10s1m5m15m30m60m[3] || 0'; sortReverse = !sortReverse">
15m
<span ng-show="sortType == 'status.kbps10s1m5m15m30m60m[3] || 0' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'status.kbps10s1m5m15m30m60m[3] || 0' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th>
<a ng-click="sortType = 'status.kbps10s1m5m15m30m60m[4] || 0'; sortReverse = !sortReverse">
30m
<span ng-show="sortType == 'status.kbps10s1m5m15m30m60m[4] || 0' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'status.kbps10s1m5m15m30m60m[4] || 0' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
<th>
<a ng-click="sortType = 'status.kbps10s1m5m15m30m60m[5] || 0'; sortReverse = !sortReverse">
60m
<span ng-show="sortType == 'status.kbps10s1m5m15m30m60m[5] || 0' && !sortReverse" class="fa fa-caret-down"></span>
<span ng-show="sortType == 'status.kbps10s1m5m15m30m60m[5] || 0' && sortReverse" class="fa fa-caret-up"></span>
</a>
</th>
</tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="relay in relays"> <tr ng-repeat="relay in relays | orderBy:sortType:sortReverse ">
<td>{{ relay.url.split('/')[2] }}</td> <td>{{ relay.address }}</td>
<td>{{ status[relay.url].numActiveSessions }}</td> <td ng-if="relay.status === undefined" colspan="11" class="text-center">Looking up...</td>
<td>{{ status[relay.url].numConnections }}</td> <td ng-if-start="relay.status !== undefined">{{ relay.status.numActiveSessions }}</td>
<td>{{ status[relay.url].bytesProxied | bytes }}</td> <td>{{ relay.status.numConnections }}</td>
<td>{{ status[relay.url].kbps10s1m5m15m30m60m[0] * 128 | bytes }}/s</td> <td>{{ relay.status.bytesProxied | bytes }}</td>
<td>{{ status[relay.url].kbps10s1m5m15m30m60m[1] * 128 | bytes }}/s</td> <td>{{ relay.status.kbps10s1m5m15m30m60m[0] * 128 | bytes }}/s</td>
<td>{{ status[relay.url].kbps10s1m5m15m30m60m[2] * 128 | bytes }}/s</td> <td>{{ relay.status.kbps10s1m5m15m30m60m[1] * 128 | bytes }}/s</td>
<td>{{ status[relay.url].kbps10s1m5m15m30m60m[3] * 128 | bytes }}/s</td> <td>{{ relay.status.kbps10s1m5m15m30m60m[2] * 128 | bytes }}/s</td>
<td>{{ status[relay.url].kbps10s1m5m15m30m60m[4] * 128 | bytes }}/s</td> <td>{{ relay.status.kbps10s1m5m15m30m60m[3] * 128 | bytes }}/s</td>
<td>{{ status[relay.url].kbps10s1m5m15m30m60m[5] * 128 | bytes }}/s</td> <td>{{ relay.status.kbps10s1m5m15m30m60m[4] * 128 | bytes }}/s</td>
<td ng-if="status[relay.url].uptimeSeconds != undefined">{{ status[relay.url].uptimeSeconds/60/60 | number:0 }} hours</td> <td>{{ relay.status.kbps10s1m5m15m30m60m[5] * 128 | bytes }}/s</td>
<td ng-if="status[relay.url].uptimeSeconds == undefined"></td> <td ng-if="relay.status.uptimeSeconds != undefined">{{ relay.status.uptimeSeconds/60/60 | number:0 }}</td>
<td>{{ status[relay.url].options['provided-by'] || '' }}</td> <td ng-if="relay.status.uptimeSeconds == undefined"></td>
<td title="{{ relay.status.options['provided-by'] || '' }}" ng-if-end>
{{ relay.status.options['provided-by'] || '' | limitTo:50 }}
<span ng-if="(relay.status.options['provided-by'] || '').length > 50">&hellip;
</td>
</tr> </tr>
</tbody> </tbody>
<tfoot> <tfoot>
@ -98,7 +170,7 @@
<td>{{ totals.kbps10s1m5m15m30m60m[4] * 128 | bytes }}/s</td> <td>{{ totals.kbps10s1m5m15m30m60m[4] * 128 | bytes }}/s</td>
<td>{{ totals.kbps10s1m5m15m30m60m[5] * 128 | bytes }}/s</td> <td>{{ totals.kbps10s1m5m15m30m60m[5] * 128 | bytes }}/s</td>
<td>{{ totals.uptimeSeconds/60/60 | number:0 }} hours</td> <td>{{ totals.uptimeSeconds/60/60 | number:0 }} hours</td>
<td></td> <td>{{ relays.length }} relays</td>
</tr> </tr>
</tfoor> </tfoor>
</table> </table>
@ -145,11 +217,6 @@
} }
}) })
.controller('relayDataController', ['$scope', '$rootScope', '$http', '$q', '$compile', '$timeout', function($scope, $rootScope, $http, $q, $compile, $timeout) { .controller('relayDataController', ['$scope', '$rootScope', '$http', '$q', '$compile', '$timeout', function($scope, $rootScope, $http, $q, $compile, $timeout) {
$scope.started = false;
$scope.geoip = {};
$scope.status = {};
$scope.uri = {};
$scope.progress = [];
$scope.totals = { $scope.totals = {
bytesProxied: 0, bytesProxied: 0,
goMaxProcs: 0, goMaxProcs: 0,
@ -160,176 +227,166 @@
numProxies: 0, numProxies: 0,
uptimeSeconds: 0, uptimeSeconds: 0,
}; };
$scope.map = new google.maps.Map(document.getElementById('map'), {
zoom: 1,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
$scope.mapBounds = new google.maps.LatLngBounds();
$scope.tooltipTemplate = $('#infoTemplate').html();
$scope.usedLocations = {};
$scope.sortType = 'status.numActiveSessions || 0';
$scope.sortReverse = true;
var usedLocs = {};
function initProgress(name) {
$scope.progress.push({name: name, done: false});
}
function progressDone(name) {
angular.forEach($scope.progress, function(progress) {
if (progress.name == name) {
progress.done = true;
}
});
}
var map;
var template = $('#infoTemplate').html();
initProgress("Fetching relays");
$http.get("/endpoint").then(function(response) { $http.get("/endpoint").then(function(response) {
progressDone("Fetching relays");
$scope.relays = response.data.relays; $scope.relays = response.data.relays;
map = new google.maps.Map(document.getElementById('map'), {
zoom: 1,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var promises = []; var promises = [];
angular.forEach($scope.relays, function(relay) { angular.forEach($scope.relays, function(relay) {
var uri = document.createElement('a');
// HAX, otherwise doesn't work relay.uri = constructURI(relay.url);
uri.href = relay.url.replace('relay://', 'http://'); relay.address = relay.url.split('/')[2];
// Convert query string to object addMarkerToMap(relay);
uri.args = {};
angular.forEach(uri.search.replace(/^\?/, '').split('&'), function(query) {
var split = query.split('=');
uri.args[split[0]] = split[1];
})
$scope.uri[relay.url] = uri; promises.push(getRelayStatus(relay));
var resolveStatus = $q.defer();
initProgress("Getting relay status for " + uri.hostname);
// Normal timeout doesn't deal with relays which accept the TCP connection
// but don't respond (some firewalls do that), so deal with it this way.
var timeoutRequest = $q.defer();
$http.get("http://" + uri.hostname + (uri.args.statusAddr || ":22070") + "/status", { timeout: timeoutRequest.promise }).then(function (response) {
progressDone("Getting relay status for " + uri.hostname);
$scope.status[relay.url] = response.data;
angular.forEach($scope.totals, function(value, key) {
if (typeof $scope.totals[key] == 'number') {
$scope.totals[key] += response.data[key];
} else if (typeof $scope.totals[key] == 'object' && $scope.totals[key] instanceof Array) {
angular.forEach($scope.totals[key], function(value, index) {
$scope.totals[key][index] += response.data[key][index];
});
}
});
resolveStatus.resolve(response.data);
}, function() {
progressDone("Getting relay status for " + uri.hostname);
resolveStatus.resolve(response.data);
});
$timeout(function() {
timeoutRequest.resolve();
}, 5000);
promises.push(resolveStatus.promise);
}); });
// Can only add circles once we know the totals for transfers, which means
// we need to resolve all statuses.
$q.all(promises).then(function() { $q.all(promises).then(function() {
$scope.started = true;
var bounds = new google.maps.LatLngBounds();
angular.forEach($scope.relays, function(relay) { angular.forEach($scope.relays, function(relay) {
var scope = $rootScope.$new(true); if (relay.status) {
var loc = relay.location.latitude + "," + relay.location.longitude; addCircleToMap(relay);
// Deal with overlapping markers
while (loc in usedLocs) {
var locParts = loc.split(',');
locParts = [parseFloat(locParts[0]), parseFloat(locParts[1])];
locParts[Math.round(Math.random())] += 0.5 * (Math.random() >= 0.5 ? 1 : -1);
loc = locParts.join(',');
} }
});
});
usedLocs[loc] = true; $scope.map.fitBounds($scope.mapBounds);
if ($scope.relays.length == 1) {
$scope.map.setZoom(13);
}
});
function addMarkerToMap(relay) {
var loc = relay.location.latitude + "," + relay.location.longitude;
// Deal with overlapping markers
while (loc in $scope.usedLocations) {
var locParts = loc.split(','); var locParts = loc.split(',');
var position = new google.maps.LatLng(locParts[0], locParts[1]); locParts = [parseFloat(locParts[0]), parseFloat(locParts[1])];
locParts[Math.round(Math.random())] += 0.5 * (Math.random() >= 0.5 ? 1 : -1);
loc = locParts.join(',');
}
scope.status = $scope.status[relay.url]; $scope.usedLocations[loc] = true;
scope.relay = relay;
scope.uri = $scope.uri[relay.url];
var marker = new google.maps.Marker({ var locParts = loc.split(',');
position: position,
map: map,
title: relay.url,
});
if (scope.status) { relay.marker = new google.maps.Marker({
marker.circle = new google.maps.Circle({ map: $scope.map,
strokeColor: '#FF0000', position: new google.maps.LatLng(locParts[0], locParts[1]),
strokeOpacity: 0.8, title: relay.url,
strokeWeight: 2, });
fillColor: '#FF0000',
fillOpacity: 0.35, var scope = $rootScope.$new(true);
map: map, scope.relay = relay;
center: position,
radius: ((scope.status.bytesProxied * 100) / $scope.totals.bytesProxied) * 10000 relay.marker.info = new google.maps.InfoWindow({
content: $compile($scope.tooltipTemplate)(scope)[0],
});
relay.marker.addListener('mouseover', function() {
relay.marker.info.open($scope.map, relay.marker);
});
relay.marker.addListener('mouseout', function() {
relay.marker.info.close();
});
$scope.mapBounds.extend(relay.marker.position);
}
function addCircleToMap(relay) {
relay.marker.circle = new google.maps.Circle({
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35,
map: $scope.map,
center: relay.marker.position,
radius: ((relay.status.bytesProxied * 100) / $scope.totals.bytesProxied) * 10000
});
}
function getRelayStatus(relay) {
// Normal timeout doesn't deal with relays which accept the TCP connection
// but don't respond (some firewalls do that), so deal with it this way.
var timeoutRequest = $q.defer();
var resolveStatus = $q.defer();
$http.get("http://" + relay.uri.hostname + (relay.uri.args.statusAddr || ":22070") + "/status", { timeout: timeoutRequest.promise }).then(function (response) {
relay.status = response.data;
resolveStatus.resolve();
angular.forEach($scope.totals, function(value, key) {
if (typeof $scope.totals[key] == 'number') {
$scope.totals[key] += response.data[key];
} else if (typeof $scope.totals[key] == 'object' && $scope.totals[key] instanceof Array) {
angular.forEach($scope.totals[key], function(value, index) {
$scope.totals[key][index] += response.data[key][index];
}); });
} }
var content = $compile(template)(scope)[0];
marker.info = new google.maps.InfoWindow();
marker.info.setContent(content);
marker.addListener('mouseover', function() {
marker.info.open(map, marker);
});
marker.addListener('mouseout', function() {
marker.info.close();
});
bounds.extend(marker.position);
}); });
}, function() {
map.fitBounds(bounds); relay.status = null;
if ($scope.relays.length == 1) { resolveStatus.resolve();
map.setZoom(13);
}
$scope.started = true;
}); });
});
$timeout(function() {
timeoutRequest.resolve();
}, 5000);
return resolveStatus.promise;
}
function constructURI(url) {
var uri = document.createElement('a');
// HAX, otherwise doesn't work
uri.href = url.replace('relay://', 'http://');
// Convert query string to object
uri.args = {};
angular.forEach(uri.search.replace(/^\?/, '').split('&'), function(query) {
var split = query.split('=');
uri.args[split[0]] = split[1];
});
return uri;
}
}]); }]);
</script> </script>
<script type="text/template" id="infoTemplate"> <script type="text/template" id="infoTemplate">
<div> <div>
<p><b>{{ uri.hostname }}</b> <span ng-if="status.options['provided-by']">provided by <u>{{ status.options['provided-by'] }}</u></span></p> <p><b>{{ relay.uri.hostname }}</b> <span ng-if="relay.status.options['provided-by']">provided by <u>{{ relay.status.options['provided-by'] }}</u></span></p>
<div ng-if="status"> <div ng-if="relay.status">
<span ng-if="status.startTime">Start time: {{ status.startTime | date:"medium" }}</br></span> <span ng-if="relay.status.startTime">Start time: {{ relay.status.startTime | date:"medium" }}</br></span>
<span ng-if="status.bytesProxied != undefined">Proxied: {{ status.bytesProxied | bytes }}</br></span> <span ng-if="relay.status.bytesProxied != undefined">Proxied: {{ relay.status.bytesProxied | bytes }}</br></span>
<span ng-if="status.numActiveSessions != undefined">Sessions: {{ status.numActiveSessions }}</br></span> <span ng-if="relay.status.numActiveSessions != undefined">Sessions: {{ relay.status.numActiveSessions }}</br></span>
<span ng-if="status.numConnections != undefined">Clients: {{ status.numConnections }}</br></span> <span ng-if="relay.status.numConnections != undefined">Clients: {{ relay.status.numConnections }}</br></span>
<span ng-if="status.options.pools">Pools: {{ status.options.pools.join(', ') }}</br></span> <span ng-if="relay.status.options.pools">Pools: {{ relay.status.options.pools.join(', ') }}</br></span>
<span ng-if="status.options['global-rate'] != undefined"> <span ng-if="relay.status.options['global-rate'] != undefined">
<span ng-if="status.options['global-rate'] > 0">Global rate limit: {{ status.options['global-rate'] | bytes }}/s</span> <span ng-if="relay.status.options['global-rate'] > 0">Global rate limit: {{ relay.status.options['global-rate'] | bytes }}/s</span>
<span ng-if="status.options['global-rate'] == 0">Global rate limit: unlimited</span> <span ng-if="relay.status.options['global-rate'] == 0">Global rate limit: unlimited</span>
</br> </br>
</span> </span>
<span ng-if="status.options['per-session-rate'] != undefined"> <span ng-if="relay.status.options['per-session-rate'] != undefined">
<span ng-if="status.options['per-session-rate'] > 0">Session rate limit: {{ status.options['per-session-rate'] | bytes }}/s</span> <span ng-if="relay.status.options['per-session-rate'] > 0">Session rate limit: {{ relay.status.options['per-session-rate'] | bytes }}/s</span>
<span ng-if="status.options['per-session-rate'] == 0">Session rate limit: unlimited</span> <span ng-if="relay.status.options['per-session-rate'] == 0">Session rate limit: unlimited</span>
</br> </br>
</span> </span>
</div> </div>
<div ng-if="!status"> <div ng-if="!relay.status">
Data unavailable. Data unavailable.
<div> <div>
</div> </div>