Refactor javascript, always show table, add sorting

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

No files matched your search

+2 -2
View File
@@ -5,12 +5,12 @@ import (
) )
const ( const (
AssetsBuildDate = "Wed, 13 Apr 2016 20:30:55 GMT" AssetsBuildDate = "Thu, 14 Apr 2016 21:00:33 GMT"
) )
func Assets() map[string][]byte { func Assets() map[string][]byte {
var assets = make(map[string][]byte, 1) var assets = make(map[string][]byte, 1)
assets["index.html"], _ = base64.StdEncoding.DecodeString("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") assets["index.html"], _ = base64.StdEncoding.DecodeString("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 truncated
return assets return assets
} }
+237 -180
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>