mirror of
https://github.com/EDCD/EDDN.git
synced 2025-04-21 11:07:38 +03:00
Merge remote-tracking branch 'origin/EDDN-Monitor'
This commit is contained in:
commit
53dcc85d04
BIN
contrib/EDDN_Monitor.s3db
Normal file
BIN
contrib/EDDN_Monitor.s3db
Normal file
Binary file not shown.
27
contrib/monitor/css/eddn.css
Normal file
27
contrib/monitor/css/eddn.css
Normal file
@ -0,0 +1,27 @@
|
||||
body {
|
||||
padding-top: 70px;
|
||||
}
|
||||
|
||||
section.container {
|
||||
margin-bottom: 20px;
|
||||
|
||||
/* FIX ANCHORS */
|
||||
padding-top:70px;
|
||||
margin-top:-70px;
|
||||
}
|
||||
|
||||
.container h2 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.container .chart {
|
||||
height: 300px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
footer .navbar-default {
|
||||
border-width: 1px 0 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.highcharts-container{width:100% !important; height:100% !important;}
|
394
contrib/monitor/index.html
Normal file
394
contrib/monitor/index.html
Normal file
@ -0,0 +1,394 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>EDDN Status</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" />
|
||||
<link href="./css/eddn.css" rel="stylesheet" />
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
|
||||
<script src="./js/date.js"></script>
|
||||
</head>
|
||||
<body data-spy="scroll" data-target="#header-nav" data-offset="70">
|
||||
<header>
|
||||
<nav class="navbar navbar-default navbar-fixed-top">
|
||||
<div class="container">
|
||||
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#header-nav">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="">EDDN Status</a>
|
||||
</div>
|
||||
|
||||
<div class="collapse navbar-collapse" id="header-nav">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="#gateways">Gateways</a></li>
|
||||
<li><a href="#relays">Relays</a></li>
|
||||
<li><a href="#softwares">Softwares</a></li>
|
||||
<li><a href="#uploaders">Uploaders</a></li>
|
||||
<li><a href="#schemas">Schemas</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<section class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p>
|
||||
This page shows transaction information (messages sent/received) for key points of the EDDN, over a one-, five- and sixty-minute period.<br />
|
||||
Values automatically update every minute.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="gateways" class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h2>Gateways</h2>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="text-right form-inline">
|
||||
<select name="gateways" class="form-control"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50%;"></th>
|
||||
<th>1 min</th>
|
||||
<th>5 min</th>
|
||||
<th>60 min</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Messages received</th>
|
||||
<td class="inbound_1min stat">-</td>
|
||||
<td class="inbound_5min stat">-</td>
|
||||
<td class="inbound_60min stat">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Invalid messages</th>
|
||||
<td class="invalid_1min stat">-</td>
|
||||
<td class="invalid_5min stat">-</td>
|
||||
<td class="invalid_60min stat">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Messages passed to relay</th>
|
||||
<td class="outbound_1min stat">-</td>
|
||||
<td class="outbound_5min stat">-</td>
|
||||
<td class="outbound_60min stat">-</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12 charts">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<p class="text-muted small">
|
||||
EDDN version: <strong><span class="version">N/A</span></strong>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<p class="text-muted small">
|
||||
Uptime: <strong><span class="uptime">N/A</span></strong>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<p class="text-muted small">
|
||||
Last updated: <strong><span class="update_timestamp">N/A</span></strong>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="relays" class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h2>Relays</h2>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="text-right form-inline">
|
||||
<select name="relays" class="form-control"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50%;"></th>
|
||||
<th>1 min</th>
|
||||
<th>5 min</th>
|
||||
<th>60 min</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Messages received</th>
|
||||
<td class="inbound_1min stat">-</td>
|
||||
<td class="inbound_5min stat">-</td>
|
||||
<td class="inbound_60min stat">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Messages duplicate</th>
|
||||
<td class="duplicate_1min stat">-</td>
|
||||
<td class="duplicate_5min stat">-</td>
|
||||
<td class="duplicate_60min stat">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Messages passed to subscribers</th>
|
||||
<td class="outbound_1min stat">-</td>
|
||||
<td class="outbound_5min stat">-</td>
|
||||
<td class="outbound_60min stat">-</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12 charts">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<p class="text-muted small">
|
||||
EDDN version: <strong><span class="version">N/A</span></strong>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<p class="text-muted small">
|
||||
Uptime: <strong><span class="uptime">N/A</span></strong>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<p class="text-muted small">
|
||||
Last updated: <strong><span class="update_timestamp">N/A</span></strong>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="softwares" class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h2>Softwares</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 20px;"></th>
|
||||
<th style="width: 50%;"></th>
|
||||
<th>Today hits</th>
|
||||
<th>Yesterday hits</th>
|
||||
<th>Total hits</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="chart" style="height: 450px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-md-offset-8">
|
||||
<p class="text-muted small">
|
||||
Last updated: <strong><span class="update_timestamp">N/A</span></strong>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="uploaders" class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h2>Uploaders (Top 20)</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 20px;"></th>
|
||||
<th style="width: 50%;"></th>
|
||||
<th>Today hits</th>
|
||||
<th>Yesterday hits</th>
|
||||
<th>Total hits</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="chart" style="height: 450px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-md-offset-8">
|
||||
<p class="text-muted small">
|
||||
Last updated: <strong><span class="update_timestamp">N/A</span></strong>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="schemas" class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h2>Schemas</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-body">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7">
|
||||
<div class="table-responsive">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 20px;"></th>
|
||||
<th style="width: 50%;"></th>
|
||||
<th>Today hits</th>
|
||||
<th>Yesterday hits</th>
|
||||
<th>Total hits</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="chart" style="height: 450px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4 col-md-offset-8">
|
||||
<p class="text-muted small">
|
||||
Last updated: <strong><span class="update_timestamp">N/A</span></strong>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer>
|
||||
<nav class="navbar navbar-default">
|
||||
<div class="container">
|
||||
<p class="navbar-text">
|
||||
The <em>Elite: Dangerous Data Network</em> is volunteer-run and not affiliated with <a href="https://frontier.co.uk/" target="_blank">Frontier Developments</a>.
|
||||
|
|
||||
Charts by <a href="http://www.highcharts.com/" target="_blank">HighCharts</a>
|
||||
</p>
|
||||
</div>
|
||||
</nav>
|
||||
</footer>
|
||||
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
|
||||
<script src="http://code.highcharts.com/4.1.5/highcharts.js"></script>
|
||||
<script src="./js/eddn.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,10 +1,10 @@
|
||||
/**
|
||||
* Version: 1.0 Alpha-1
|
||||
* Build Date: 13-Nov-2007
|
||||
* Copyright (c) 2006-2007, Coolite Inc. (http://www.coolite.com/). All rights reserved.
|
||||
* License: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/.
|
||||
* Website: http://www.datejs.com/ or http://www.coolite.com/datejs/
|
||||
*/
|
||||
/**
|
||||
* Version: 1.0 Alpha-1
|
||||
* Build Date: 13-Nov-2007
|
||||
* Copyright (c) 2006-2007, Coolite Inc. (http://www.coolite.com/). All rights reserved.
|
||||
* License: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/.
|
||||
* Website: http://www.datejs.com/ or http://www.coolite.com/datejs/
|
||||
*/
|
||||
Date.CultureInfo={name:"en-US",englishName:"English (United States)",nativeName:"English (United States)",dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],abbreviatedDayNames:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],shortestDayNames:["Su","Mo","Tu","We","Th","Fr","Sa"],firstLetterDayNames:["S","M","T","W","T","F","S"],monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],abbreviatedMonthNames:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],amDesignator:"AM",pmDesignator:"PM",firstDayOfWeek:0,twoDigitYearMax:2029,dateElementOrder:"mdy",formatPatterns:{shortDate:"M/d/yyyy",longDate:"dddd, MMMM dd, yyyy",shortTime:"h:mm tt",longTime:"h:mm:ss tt",fullDateTime:"dddd, MMMM dd, yyyy h:mm:ss tt",sortableDateTime:"yyyy-MM-ddTHH:mm:ss",universalSortableDateTime:"yyyy-MM-dd HH:mm:ssZ",rfc1123:"ddd, dd MMM yyyy HH:mm:ss GMT",monthDay:"MMMM dd",yearMonth:"MMMM, yyyy"},regexPatterns:{jan:/^jan(uary)?/i,feb:/^feb(ruary)?/i,mar:/^mar(ch)?/i,apr:/^apr(il)?/i,may:/^may/i,jun:/^jun(e)?/i,jul:/^jul(y)?/i,aug:/^aug(ust)?/i,sep:/^sep(t(ember)?)?/i,oct:/^oct(ober)?/i,nov:/^nov(ember)?/i,dec:/^dec(ember)?/i,sun:/^su(n(day)?)?/i,mon:/^mo(n(day)?)?/i,tue:/^tu(e(s(day)?)?)?/i,wed:/^we(d(nesday)?)?/i,thu:/^th(u(r(s(day)?)?)?)?/i,fri:/^fr(i(day)?)?/i,sat:/^sa(t(urday)?)?/i,future:/^next/i,past:/^last|past|prev(ious)?/i,add:/^(\+|after|from)/i,subtract:/^(\-|before|ago)/i,yesterday:/^yesterday/i,today:/^t(oday)?/i,tomorrow:/^tomorrow/i,now:/^n(ow)?/i,millisecond:/^ms|milli(second)?s?/i,second:/^sec(ond)?s?/i,minute:/^min(ute)?s?/i,hour:/^h(ou)?rs?/i,week:/^w(ee)?k/i,month:/^m(o(nth)?s?)?/i,day:/^d(ays?)?/i,year:/^y((ea)?rs?)?/i,shortMeridian:/^(a|p)/i,longMeridian:/^(a\.?m?\.?|p\.?m?\.?)/i,timezone:/^((e(s|d)t|c(s|d)t|m(s|d)t|p(s|d)t)|((gmt)?\s*(\+|\-)\s*\d\d\d\d?)|gmt)/i,ordinalSuffix:/^\s*(st|nd|rd|th)/i,timeContext:/^\s*(\:|a|p)/i},abbreviatedTimeZoneStandard:{GMT:"-000",EST:"-0400",CST:"-0500",MST:"-0600",PST:"-0700"},abbreviatedTimeZoneDST:{GMT:"-000",EDT:"-0500",CDT:"-0600",MDT:"-0700",PDT:"-0800"}};
|
||||
Date.getMonthNumberFromName=function(name){var n=Date.CultureInfo.monthNames,m=Date.CultureInfo.abbreviatedMonthNames,s=name.toLowerCase();for(var i=0;i<n.length;i++){if(n[i].toLowerCase()==s||m[i].toLowerCase()==s){return i;}}
|
||||
return-1;};Date.getDayNumberFromName=function(name){var n=Date.CultureInfo.dayNames,m=Date.CultureInfo.abbreviatedDayNames,o=Date.CultureInfo.shortestDayNames,s=name.toLowerCase();for(var i=0;i<n.length;i++){if(n[i].toLowerCase()==s||m[i].toLowerCase()==s){return i;}}
|
||||
@ -101,4 +101,4 @@ return _.any.apply(null,rx);}else{return _get(fx);}};g._formats=g.formats(["yyyy
|
||||
return g._start.call({},s);};}());Date._parse=Date.parse;Date.parse=function(s){var r=null;if(!s){return null;}
|
||||
try{r=Date.Grammar.start.call({},s);}catch(e){return null;}
|
||||
return((r[1].length===0)?r[0]:null);};Date.getParseFunction=function(fx){var fn=Date.Grammar.formats(fx);return function(s){var r=null;try{r=fn.call({},s);}catch(e){return null;}
|
||||
return((r[1].length===0)?r[0]:null);};};Date.parseExact=function(s,fx){return Date.getParseFunction(fx)(s);};
|
||||
return((r[1].length===0)?r[0]:null);};};Date.parseExact=function(s,fx){return Date.getParseFunction(fx)(s);};
|
535
contrib/monitor/js/eddn.js
Normal file
535
contrib/monitor/js/eddn.js
Normal file
@ -0,0 +1,535 @@
|
||||
var updateInterval = 60000,
|
||||
|
||||
monitorEndPoint = 'http://eddn-monitor.ed-td.space:9091/',
|
||||
|
||||
gatewayBottlePort = 8080,
|
||||
relayBottlePort = 9090,
|
||||
|
||||
gateways = [
|
||||
'eddn-gateway.elite-markets.net',
|
||||
'eddn-gateway.ed-td.space'
|
||||
]; // Must find a way to bind them to monitor,
|
||||
|
||||
relays = [
|
||||
'eddn-relay.elite-markets.net',
|
||||
'eddn-relay.ed-td.space'
|
||||
]; // Must find a way to bind them to monitor
|
||||
|
||||
var stats = {
|
||||
'gateways' : {},
|
||||
'relays' : {}
|
||||
}; // Stats placeholder
|
||||
|
||||
formatNumber = function(num) {
|
||||
return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, "$1,")
|
||||
}
|
||||
|
||||
makeSlug = function(str) {
|
||||
var slugcontent_hyphens = str.replace(/\s/g,'-');
|
||||
var finishedslug = slugcontent_hyphens.replace(/[^a-zA-Z0-9\-]/g,'');
|
||||
return finishedslug.toLowerCase();
|
||||
}
|
||||
|
||||
secondsToDurationString = function(seconds) {
|
||||
var hours = Math.floor(seconds / 3600);
|
||||
var minutes = Math.floor((seconds - (hours * 3600)) / 60);
|
||||
var seconds = seconds - (hours * 3600) - (minutes * 60);
|
||||
var days = 0;
|
||||
|
||||
if (hours > 24) {
|
||||
days = Math.floor(hours / 24)
|
||||
hours = Math.floor((hours - days * 24) / 3600);
|
||||
}
|
||||
|
||||
if (hours < 10) {hours = "0" + hours;}
|
||||
if (minutes < 10) {minutes = "0" + minutes;}
|
||||
if (seconds < 10) {seconds = "0" + seconds;}
|
||||
|
||||
if (days > 0) {
|
||||
return days + "d " + hours + ":" + minutes + ":" + seconds;
|
||||
}
|
||||
else {
|
||||
return hours + ":" + minutes + ":" + seconds;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var doUpdateSoftwares = function()
|
||||
{
|
||||
var dToday = new Date(),
|
||||
dYesterday = new (function(d){ d.setDate(d.getDate()-1); return d})(new Date),
|
||||
|
||||
yesterday = dYesterday.getUTCFullYear() + '-' + ("0" + (dYesterday.getUTCMonth() + 1)).slice(-2) + '-' + ("0" + (dYesterday.getUTCDate())).slice(-2),
|
||||
today = dToday.getUTCFullYear() + '-' + ("0" + (dToday.getUTCMonth() + 1)).slice(-2) + '-' + ("0" + (dToday.getUTCDate())).slice(-2);
|
||||
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: monitorEndPoint + 'getSoftwares/?dateStart=' + yesterday + '&dateEnd = ' + today,
|
||||
success: function(softwares){
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: monitorEndPoint + 'getTotalSoftwares/',
|
||||
success: function(softwaresTotal){
|
||||
var chart = $('#softwares .chart').highcharts(),
|
||||
series = chart.get('softwares');
|
||||
|
||||
$('#softwares .table tbody').empty();
|
||||
|
||||
$.each(softwaresTotal, function(software, hits){
|
||||
$('#softwares .table tbody').append(
|
||||
newTr = $('<tr>').attr('data-name', software).on('mouseover', function(){
|
||||
chart.get('software-' + makeSlug(software)).setState('hover');
|
||||
chart.tooltip.refresh(chart.get('software-' + makeSlug(software)));
|
||||
}).on('mouseout', function(){
|
||||
chart.get('software-' + makeSlug(software)).setState('');
|
||||
chart.tooltip.hide();
|
||||
}).append(
|
||||
$('<td>').addClass('square')
|
||||
).append(
|
||||
$('<td>').html('<strong>' + software + '</strong>')
|
||||
)
|
||||
.append(
|
||||
$('<td>').addClass('stat today').html(formatNumber(softwares[today][software] || 0))
|
||||
)
|
||||
.append(
|
||||
$('<td>').addClass('stat yesterday').html(formatNumber(softwares[yesterday][software] || 0))
|
||||
)
|
||||
.append(
|
||||
$('<td>').addClass('stat total').html('<strong>' + formatNumber(hits) + '</strong>')
|
||||
)
|
||||
);
|
||||
|
||||
if(!chart.get('software-' + makeSlug(software)))
|
||||
series.addPoint({id: 'software-' + makeSlug(software), name: software, y: parseInt(hits)}, false);
|
||||
else
|
||||
chart.get('software-' + makeSlug(software)).update(parseInt(hits), false);
|
||||
|
||||
newTr.find('.square').css('background', chart.get('software-' + makeSlug(software)).color);
|
||||
});
|
||||
|
||||
chart.redraw();
|
||||
|
||||
$('#softwares').find(".stat").removeClass("warning").each(function() {
|
||||
if ($(this).html() == "0")
|
||||
$(this).addClass("warning");
|
||||
});
|
||||
|
||||
$('#softwares').find(".update_timestamp").html(d.toString("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var doUpdateUploaders = function()
|
||||
{
|
||||
var dToday = new Date(),
|
||||
dYesterday = new (function(d){ d.setDate(d.getDate()-1); return d})(new Date),
|
||||
|
||||
yesterday = dYesterday.getUTCFullYear() + '-' + ("0" + (dYesterday.getUTCMonth() + 1)).slice(-2) + '-' + ("0" + (dYesterday.getUTCDate())).slice(-2),
|
||||
today = dToday.getUTCFullYear() + '-' + ("0" + (dToday.getUTCMonth() + 1)).slice(-2) + '-' + ("0" + (dToday.getUTCDate())).slice(-2);
|
||||
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: monitorEndPoint + 'getUploaders/?dateStart=' + yesterday + '&dateEnd = ' + today,
|
||||
success: function(uploaders){
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: monitorEndPoint + 'getTotalUploaders/',
|
||||
success: function(uploadersTotal){
|
||||
var chart = $('#uploaders .chart').highcharts(),
|
||||
series = chart.get('uploaders');
|
||||
|
||||
$('#uploaders .table tbody').empty();
|
||||
|
||||
$.each(uploadersTotal, function(uploader, hits){
|
||||
if(uploader.length > 32)
|
||||
truncateUploader = jQuery.trim(uploader).substring(0, 32)/*.split(" ").slice(0, -1).join(" ")*/ + "..."
|
||||
else
|
||||
truncateUploader = uploader
|
||||
|
||||
$('#uploaders .table tbody').append(
|
||||
newTr = $('<tr>').attr('data-name', uploader).on('mouseover', function(){
|
||||
chart.get('uploader-' + makeSlug(uploader)).setState('hover');
|
||||
chart.tooltip.refresh(chart.get('uploader-' + makeSlug(uploader)));
|
||||
}).on('mouseout', function(){
|
||||
chart.get('uploader-' + makeSlug(uploader)).setState('');
|
||||
chart.tooltip.hide();
|
||||
}).append(
|
||||
$('<td>').addClass('square')
|
||||
).append(
|
||||
$('<td>').html('<strong>' + truncateUploader + '</strong>')
|
||||
)
|
||||
.append(
|
||||
$('<td>').addClass('stat today').html(formatNumber(uploaders[today][uploader] || 0))
|
||||
)
|
||||
.append(
|
||||
$('<td>').addClass('stat yesterday').html(formatNumber(uploaders[yesterday][uploader] || 0))
|
||||
)
|
||||
.append(
|
||||
$('<td>').addClass('stat total').html('<strong>' + formatNumber(hits) + '</strong>')
|
||||
)
|
||||
);
|
||||
|
||||
if(!chart.get('uploader-' + makeSlug(uploader)))
|
||||
series.addPoint({id: 'uploader-' + makeSlug(uploader), name: uploader, y: parseInt(hits)}, false);
|
||||
else
|
||||
chart.get('uploader-' + makeSlug(uploader)).update(parseInt(hits), false);
|
||||
|
||||
newTr.find('.square').css('background', chart.get('uploader-' + makeSlug(uploader)).color);
|
||||
});
|
||||
|
||||
chart.redraw();
|
||||
|
||||
$('#uploaders').find(".stat").removeClass("warning").each(function() {
|
||||
if ($(this).html() == "0")
|
||||
$(this).addClass("warning");
|
||||
});
|
||||
|
||||
$('#uploaders').find(".update_timestamp").html(d.toString("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var doUpdateSchemas = function()
|
||||
{
|
||||
var dToday = new Date(),
|
||||
dYesterday = new (function(d){ d.setDate(d.getDate()-1); return d})(new Date),
|
||||
|
||||
yesterday = dYesterday.getUTCFullYear() + '-' + ("0" + (dYesterday.getUTCMonth() + 1)).slice(-2) + '-' + ("0" + (dYesterday.getUTCDate())).slice(-2),
|
||||
today = dToday.getUTCFullYear() + '-' + ("0" + (dToday.getUTCMonth() + 1)).slice(-2) + '-' + ("0" + (dToday.getUTCDate())).slice(-2);
|
||||
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: monitorEndPoint + 'getSchemas/?dateStart=' + yesterday + '&dateEnd = ' + today,
|
||||
success: function(schemas){
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: monitorEndPoint + 'getTotalSchemas/',
|
||||
success: function(schemasTotal){
|
||||
var chart = $('#schemas .chart').highcharts(),
|
||||
series = chart.get('schemas');
|
||||
|
||||
$('#schemas .table tbody').empty();
|
||||
|
||||
$.each(schemasTotal, function(schema, hits){
|
||||
$('#schemas .table tbody').append(
|
||||
newTr = $('<tr>').attr('data-name', schema).on('mouseover', function(){
|
||||
chart.get('schema-' + makeSlug(schema)).setState('hover');
|
||||
chart.tooltip.refresh(chart.get('schema-' + makeSlug(schema)));
|
||||
}).on('mouseout', function(){
|
||||
chart.get('schema-' + makeSlug(schema)).setState('');
|
||||
chart.tooltip.hide();
|
||||
}).append(
|
||||
$('<td>').addClass('square')
|
||||
).append(
|
||||
$('<td>').html('<strong>' + schema + '</strong>')
|
||||
)
|
||||
.append(
|
||||
$('<td>').addClass('stat today').html(formatNumber(schemas[today][schema] || 0))
|
||||
)
|
||||
.append(
|
||||
$('<td>').addClass('stat yesterday').html(formatNumber(schemas[yesterday][schema] || 0))
|
||||
)
|
||||
.append(
|
||||
$('<td>').addClass('stat total').html('<strong>' + formatNumber(hits) + '</strong>')
|
||||
)
|
||||
);
|
||||
|
||||
if(!chart.get('schema-' + makeSlug(schema)))
|
||||
series.addPoint({id: 'schema-' + makeSlug(schema), name: schema, y: parseInt(hits)}, false);
|
||||
else
|
||||
chart.get('schema-' + makeSlug(schema)).update(parseInt(hits), false);
|
||||
|
||||
newTr.find('.square').css('background', chart.get('schema-' + makeSlug(schema)).color);
|
||||
});
|
||||
|
||||
chart.redraw();
|
||||
|
||||
$('#schemas').find(".stat").removeClass("warning").each(function() {
|
||||
if ($(this).html() == "0")
|
||||
$(this).addClass("warning");
|
||||
});
|
||||
|
||||
$('#schemas').find(".update_timestamp").html(d.toString("yyyy-MM-dd HH:mm:ss"));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var doUpdates = function(type){
|
||||
$("select[name=" + type + "] option").each(function(){
|
||||
var currentItem = $(this).html(),
|
||||
isSelected = $(this).is(':selected');
|
||||
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: $(this).val(),
|
||||
success: function(data){
|
||||
d = new Date();
|
||||
|
||||
stats[type][currentItem]['lastUpdate'] = d.toString("yyyy-MM-dd HH:mm:ss");
|
||||
stats[type][currentItem]['last'] = data;
|
||||
|
||||
if(isSelected)
|
||||
showStats(type, currentItem);
|
||||
|
||||
var chart = $("#" + type + " .chart[data-name='" + currentItem + "']").highcharts();
|
||||
|
||||
shift = chart.get('inbound').data.length > 60;
|
||||
chart.get('inbound').addPoint([d.getTime(), (data['inbound'] || {})['1min'] || 0], true, shift);
|
||||
|
||||
if(type == 'gateways')
|
||||
{
|
||||
shift = chart.get('invalid').data.length > 60;
|
||||
chart.get('invalid').addPoint([d.getTime(), (data['invalid'] || {})['1min'] || 0], true, shift);
|
||||
}
|
||||
|
||||
if(type == 'relays')
|
||||
{
|
||||
shift = chart.get('duplicate').data.length > 60;
|
||||
chart.get('duplicate').addPoint([d.getTime(), (data['duplicate'] || {})['1min'] || 0], true, shift);
|
||||
}
|
||||
|
||||
shift = chart.get('outbound').data.length > 60;
|
||||
chart.get('outbound').addPoint([d.getTime(), (data['outbound'] || {})['1min'] || 0], true, shift);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var showStats = function(type, currentItem){
|
||||
var el = $('#' + type),
|
||||
currentItemStats = stats[type][currentItem]['last'];
|
||||
|
||||
el.find(".inbound_1min").html((currentItemStats['inbound'] || {})['1min'] || 0);
|
||||
el.find(".inbound_5min").html((currentItemStats["inbound"] || {})['5min'] || 0);
|
||||
el.find(".inbound_60min").html((currentItemStats["inbound"] || {})['60min'] || 0);
|
||||
|
||||
if(type == 'gateways')
|
||||
{
|
||||
el.find(".invalid_1min").html((currentItemStats["invalid"] || {})['1min'] || 0);
|
||||
el.find(".invalid_5min").html((currentItemStats["invalid"] || {})['5min'] || 0);
|
||||
el.find(".invalid_60min").html((currentItemStats["invalid"] || {})['60min'] || 0);
|
||||
}
|
||||
|
||||
if(type == 'relays')
|
||||
{
|
||||
el.find(".duplicate_1min").html((currentItemStats["duplicate"] || {})['1min'] || 0);
|
||||
el.find(".duplicate_5min").html((currentItemStats["duplicate"] || {})['5min'] || 0);
|
||||
el.find(".duplicate_60min").html((currentItemStats["duplicate"] || {})['60min'] || 0);
|
||||
}
|
||||
|
||||
el.find(".outbound_1min").html((currentItemStats["outbound"] || {})['1min'] || 0);
|
||||
el.find(".outbound_5min").html((currentItemStats["outbound"] || {})['5min'] || 0);
|
||||
el.find(".outbound_60min").html((currentItemStats["outbound"] || {})['60min'] || 0);
|
||||
|
||||
el.find(".update_timestamp").html(stats[type][currentItem]['lastUpdate']);
|
||||
el.find(".version").html(currentItemStats['version'] || 'N/A');
|
||||
|
||||
if (currentItemStats['uptime'])
|
||||
el.find(".uptime").html(secondsToDurationString(currentItemStats['uptime']));
|
||||
else
|
||||
el.find(".uptime").html('N/A');
|
||||
|
||||
el.find(".stat").removeClass("warning").each(function() {
|
||||
if ($(this).html() == "0")
|
||||
$(this).addClass("warning");
|
||||
});
|
||||
|
||||
el.find(".chart").hide();
|
||||
el.find(".chart[data-name='" + currentItem + "']").show();
|
||||
$(window).trigger('resize'); // Fix wrong size in chart
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Launch monitoring
|
||||
*/
|
||||
var start = function(){
|
||||
Highcharts.setOptions({global: {useUTC: false}});
|
||||
|
||||
// Grab gateways
|
||||
gateways = gateways.sort();
|
||||
$.each(gateways, function(k, gateway){
|
||||
gateway = gateway.replace('tcp://', '');
|
||||
gateway = gateway.replace(':8500', '');
|
||||
|
||||
$("select[name=gateways]").append($('<option>', {
|
||||
value: 'http://' + gateway + ':' + gatewayBottlePort + '/stats/',
|
||||
text : gateway
|
||||
}));
|
||||
|
||||
$('#gateways .charts').append(
|
||||
$('<div>').addClass('chart')
|
||||
.css('width', '100%')
|
||||
.attr('data-name', gateway)
|
||||
);
|
||||
|
||||
$("#gateways .chart[data-name='" + gateway + "']").highcharts({
|
||||
chart: {
|
||||
type: 'spline', animation: Highcharts.svg
|
||||
},
|
||||
title: { text: '', style: {display: 'none'} },
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
tickPixelInterval: 150
|
||||
},
|
||||
yAxis: {
|
||||
title: {text: ''},
|
||||
plotLines: [{value: 0, width: 1, color: '#808080'}],
|
||||
min: 0
|
||||
},
|
||||
tooltip: { enabled: false },
|
||||
credits: { enabled: false },
|
||||
exporting: { enabled: false },
|
||||
series: [
|
||||
{id: 'inbound', data: [], name: 'Messages received', zIndex: 300},
|
||||
{id: 'invalid', data: [], name: 'Invalid messages', zIndex: 1},
|
||||
{id: 'outbound', data: [], name: 'Messages passed to relay', zIndex: 200}
|
||||
]
|
||||
}).hide();
|
||||
|
||||
stats['gateways'][gateway] = {};
|
||||
});
|
||||
|
||||
doUpdates('gateways');
|
||||
setInterval(function(){
|
||||
doUpdates('gateways');
|
||||
}, updateInterval);
|
||||
|
||||
// Grab relays
|
||||
relays = relays.sort();
|
||||
$.each(relays, function(k, relay){
|
||||
$("select[name=relays]").append($('<option>', {
|
||||
value: 'http://' + relay + ':' + relayBottlePort + '/stats/',
|
||||
text : relay
|
||||
}));
|
||||
|
||||
$('#relays .charts').append(
|
||||
$('<div>').addClass('chart')
|
||||
.css('width', '100%')
|
||||
.attr('data-name', relay)
|
||||
);
|
||||
|
||||
$("#relays .chart[data-name='" + relay + "']").highcharts({
|
||||
chart: {
|
||||
type: 'spline', animation: Highcharts.svg,
|
||||
events: {
|
||||
load: function(){ setTimeout(function(){$(window).trigger('resize');}, 250); }
|
||||
},
|
||||
marginRight: 10
|
||||
},
|
||||
title: { text: '', style: {display: 'none'} },
|
||||
xAxis: {
|
||||
type: 'datetime',
|
||||
tickPixelInterval: 150
|
||||
},
|
||||
yAxis: {
|
||||
title: {text: ''},
|
||||
plotLines: [{value: 0, width: 1, color: '#808080'}],
|
||||
min: 0
|
||||
},
|
||||
tooltip: { enabled: false },
|
||||
credits: { enabled: false },
|
||||
exporting: { enabled: false },
|
||||
series: [
|
||||
{id: 'inbound', data: [], name: 'Messages received', zIndex: 300},
|
||||
{id: 'duplicate', data: [], name: 'Messages duplicate', zIndex: 1},
|
||||
{id: 'outbound', data: [], name: 'Messages passed to subscribers', zIndex: 200}
|
||||
]
|
||||
}).hide();
|
||||
|
||||
stats['relays'][relay] = {};
|
||||
});
|
||||
|
||||
doUpdates('relays');
|
||||
setInterval(function(){
|
||||
doUpdates('relays');
|
||||
}, updateInterval);
|
||||
|
||||
// Grab software from monitor
|
||||
$('#softwares .chart').highcharts({
|
||||
chart: {
|
||||
type: 'pie', animation: Highcharts.svg
|
||||
},
|
||||
title: { text: '', style: {display: 'none'} },
|
||||
credits: { enabled: false },
|
||||
tooltip: { headerFormat: '', pointFormat: '{point.name}: <b>{point.percentage:.1f}%</b>' },
|
||||
legend: { enabled: false },
|
||||
plotOptions: {pie: {allowPointSelect: false,dataLabels: { enabled: false }}},
|
||||
series: [{
|
||||
id: 'softwares',
|
||||
type: 'pie',
|
||||
data: []
|
||||
}]
|
||||
});
|
||||
|
||||
doUpdateSoftwares();
|
||||
setInterval(function(){
|
||||
doUpdateSoftwares();
|
||||
}, updateInterval);
|
||||
|
||||
// Grab uploader from monitor
|
||||
$('#uploaders .chart').highcharts({
|
||||
chart: {
|
||||
type: 'pie', animation: Highcharts.svg
|
||||
},
|
||||
title: { text: '', style: {display: 'none'} },
|
||||
credits: { enabled: false },
|
||||
tooltip: { headerFormat: '', pointFormat: '{point.name}: <b>{point.percentage:.1f}%</b>' },
|
||||
legend: { enabled: false },
|
||||
plotOptions: {pie: {allowPointSelect: false,dataLabels: { enabled: false }}},
|
||||
series: [{
|
||||
id: 'uploaders',
|
||||
type: 'pie',
|
||||
data: []
|
||||
}]
|
||||
});
|
||||
|
||||
doUpdateUploaders();
|
||||
setInterval(function(){
|
||||
doUpdateUploaders();
|
||||
}, updateInterval);
|
||||
|
||||
// Grab schema from monitor
|
||||
$('#schemas .chart').highcharts({
|
||||
chart: {
|
||||
type: 'pie', animation: Highcharts.svg
|
||||
},
|
||||
title: { text: '', style: {display: 'none'} },
|
||||
credits: { enabled: false },
|
||||
tooltip: { headerFormat: '', pointFormat: '{point.name}: <b>{point.percentage:.1f}%</b>' },
|
||||
legend: { enabled: false },
|
||||
plotOptions: {pie: {allowPointSelect: false,dataLabels: { enabled: false }}},
|
||||
series: [{
|
||||
id: 'schemas',
|
||||
type: 'pie',
|
||||
data: []
|
||||
}]
|
||||
});
|
||||
|
||||
doUpdateSchemas();
|
||||
setInterval(function(){
|
||||
doUpdateSchemas();
|
||||
}, updateInterval);
|
||||
|
||||
// Attach events
|
||||
$("select[name=gateways]").change(function(){
|
||||
showStats('gateways', $(this).find('option:selected').html());
|
||||
});
|
||||
$("select[name=relays]").change(function(){
|
||||
showStats('relays', $(this).find('option:selected').html());
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
start();
|
||||
});
|
5
contrib/status/css/bootstrap.min.css
vendored
5
contrib/status/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
7
contrib/status/js/bootstrap.min.js
vendored
7
contrib/status/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,95 +0,0 @@
|
||||
secondsToDurationString = function(seconds) {
|
||||
var hours = Math.floor(seconds / 3600);
|
||||
var minutes = Math.floor((seconds - (hours * 3600)) / 60);
|
||||
var seconds = seconds - (hours * 3600) - (minutes * 60);
|
||||
var days = 0;
|
||||
|
||||
if (hours > 24) {
|
||||
days = Math.floor(hours / 24)
|
||||
hours = Math.floor((seconds - days * 24) / 3600);
|
||||
}
|
||||
|
||||
if (hours < 10) {hours = "0" + hours;}
|
||||
if (minutes < 10) {minutes = "0" + minutes;}
|
||||
if (seconds < 10) {seconds = "0" + seconds;}
|
||||
|
||||
if (days > 0) {
|
||||
return days + "d " + hours + ":" + minutes + ":" + seconds;
|
||||
}
|
||||
else {
|
||||
return hours + ":" + minutes + ":" + seconds;
|
||||
}
|
||||
}
|
||||
|
||||
displayGatewayStats = function(stats) {
|
||||
$("#gateway_inbound_1min").html((stats["inbound"] || {})["1min"] || 0);
|
||||
$("#gateway_inbound_5min").html((stats["inbound"] || {})["5min"] || 0);
|
||||
$("#gateway_inbound_60min").html((stats["inbound"] || {})["60min"] || 0);
|
||||
|
||||
$("#gateway_invalid_1min").html((stats["invalid"] || {})["1min"] || 0);
|
||||
$("#gateway_invalid_5min").html((stats["invalid"] || {})["5min"] || 0);
|
||||
$("#gateway_invalid_60min").html((stats["invalid"] || {})["60min"] || 0);
|
||||
|
||||
$("#gateway_outbound_1min").html((stats["outbound"] || {})["1min"] || 0);
|
||||
$("#gateway_outbound_5min").html((stats["outbound"] || {})["5min"] || 0);
|
||||
$("#gateway_outbound_60min").html((stats["outbound"] || {})["60min"] || 0);
|
||||
d = new Date();
|
||||
$("#update_gateway_timestamp").html(d.toString("yyyy-MM-dd HH:mm:ss"));
|
||||
$("#gateway_version").html(stats['version'] || "N/A");
|
||||
if (stats['uptime']) {
|
||||
$("#gateway_uptime").html(secondsToDurationString(stats['uptime']));
|
||||
}
|
||||
formatStats();
|
||||
}
|
||||
|
||||
displayRelayStats = function(stats) {
|
||||
$("#relay_inbound_1min").html((stats["inbound"] || {})["1min"] || 0);
|
||||
$("#relay_inbound_5min").html((stats["inbound"] || {})["5min"] || 0);
|
||||
$("#relay_inbound_60min").html((stats["inbound"] || {})["60min"] || 0);
|
||||
|
||||
$("#relay_outbound_1min").html((stats["outbound"] || {})["1min"] || 0);
|
||||
$("#relay_outbound_5min").html((stats["outbound"] || {})["5min"] || 0);
|
||||
$("#relay_outbound_60min").html((stats["outbound"] || {})["60min"] || 0);
|
||||
d = new Date();
|
||||
$("#update_relay_timestamp").html(d.toString("yyyy-MM-dd HH:mm:ss"));
|
||||
$("#relay_version").html(stats['version'] || "N/A");
|
||||
if (stats['uptime']) {
|
||||
$("#relay_uptime").html(secondsToDurationString(stats['uptime']));
|
||||
}
|
||||
formatStats();
|
||||
}
|
||||
|
||||
formatStats = function() {
|
||||
$(".stat").each(function() {
|
||||
if ($(this).html() == "0") {
|
||||
$(this).addClass("warning");
|
||||
}
|
||||
else {
|
||||
$(this).removeClass("warning");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
doUpdate = function(url, success, failure) {
|
||||
$.ajax({
|
||||
dataType: "json",
|
||||
url: url,
|
||||
success: success,
|
||||
failure: failure
|
||||
});
|
||||
}
|
||||
|
||||
getGatewayUrl = function() {
|
||||
return $("#gateway").val();
|
||||
}
|
||||
|
||||
getRelayUrl = function() {
|
||||
return $("#relay").val();
|
||||
}
|
||||
|
||||
$("#gateway").change(function() {doUpdate(getGatewayUrl(), displayGatewayStats);})
|
||||
$("#relay").change(function() {doUpdate(getRelayUrl(), displayRelayStats);})
|
||||
|
||||
doUpdate(getGatewayUrl(), displayGatewayStats);
|
||||
doUpdate(getRelayUrl(), displayRelayStats);
|
||||
setInterval(function() {doUpdate(getGatewayUrl(), displayGatewayStats); doUpdate(getRelayUrl(), displayRelayStats)}, 60000);
|
4
contrib/status/js/jquery.min.js
vendored
4
contrib/status/js/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,109 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>EDDN Status</title>
|
||||
|
||||
<!-- Bootstrap -->
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="css/eddn.css" rel="stylesheet">
|
||||
|
||||
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/date.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h1>EDDN Status</h1>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>This page shows transaction information (messages sent/received) for key points of the EDDN, over a one-, five- and sixty-minute period. Values automatically update every minute.</p>
|
||||
<h2>Gateway</h2>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="form-inline">
|
||||
<select id="gateway" class="form-control">
|
||||
<option value="http://eddn-gateway.elite-markets.net:8080/stats/">eddn-gateway.elite-markets.net</option>
|
||||
</select>
|
||||
</td>
|
||||
<th>1 min</th>
|
||||
<th>5 min</th>
|
||||
<th>60 min</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Messages received</th>
|
||||
<td id="gateway_inbound_1min" class="stat">-</td>
|
||||
<td id="gateway_inbound_5min" class="stat">-</td>
|
||||
<td id="gateway_inbound_60min" class="stat">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Invalid messages</th>
|
||||
<td id="gateway_invalid_1min" class="stat">-</td>
|
||||
<td id="gateway_invalid_5min" class="stat">-</td>
|
||||
<td id="gateway_invalid_60min" class="stat">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Messages passed to relay</th>
|
||||
<td id="gateway_outbound_1min" class="stat">-</td>
|
||||
<td id="gateway_outbound_5min" class="stat">-</td>
|
||||
<td id="gateway_outbound_60min" class="stat">-</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-muted small">EDDN version <span id="gateway_version">N/A</span>. Uptime: <span id="gateway_uptime">N/A</span>. Last updated <span id="update_gateway_timestamp">N/A</span></p>
|
||||
<h2>Relay</h2>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<td class="form-inline">
|
||||
<select id="relay" class="form-control">
|
||||
<option value="http://eddn-relay.elite-markets.net:9090/stats/">eddn-relay.elite-markets.net</option>
|
||||
</select>
|
||||
</td>
|
||||
<th>1 min</th>
|
||||
<th>5 min</th>
|
||||
<th>60 min</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Messages received</th>
|
||||
<td id="relay_inbound_1min" class="stat">-</td>
|
||||
<td id="relay_inbound_5min" class="stat">-</td>
|
||||
<td id="relay_inbound_60min" class="stat">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Messages passed to subscribers</th>
|
||||
<td id="relay_outbound_1min" class="stat">-</td>
|
||||
<td id="relay_outbound_5min" class="stat">-</td>
|
||||
<td id="relay_outbound_60min" class="stat">-</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p class="text-muted small">EDDN version <span id="relay_version">N/A</span>. Uptime: <span id="relay_uptime">N/A</span>. Last updated <span id="update_relay_timestamp">N/A</span></p>
|
||||
</div>
|
||||
<div class="panel-footer">
|
||||
<p>The <em>Elite: Dangerous Data Network</em> is volunteer-run and not affiliated with Frontier Developments.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
|
||||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
<script src="js/eddn.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -103,6 +103,9 @@ while (true)
|
||||
try
|
||||
{
|
||||
$subscriber->connect($relayEDDN);
|
||||
echoLog('Connect to ' . $relayEDDN);
|
||||
echoLog('');
|
||||
echoLog('');
|
||||
|
||||
while (true)
|
||||
{
|
||||
@ -111,6 +114,9 @@ while (true)
|
||||
if ($message === false)
|
||||
{
|
||||
$subscriber->disconnect($relayEDDN);
|
||||
echoLog('Disconnect from ' . $relayEDDN);
|
||||
echoLog('');
|
||||
echoLog('');
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -52,13 +52,14 @@
|
||||
|
||||
class EDDN
|
||||
{
|
||||
static private $_debug = true;
|
||||
private static $_debug = true;
|
||||
|
||||
static private $_gateways = array(
|
||||
'http://eddn-gateway.elite-markets.net:8080/upload/'
|
||||
private static $_gateways = array(
|
||||
'http://eddn-gateway.elite-markets.net:8080/upload/',
|
||||
'http://eddn-gateway.ed-td.space:8080/upload/'
|
||||
);
|
||||
|
||||
static private $_schemas = array(
|
||||
private static $_schemas = array(
|
||||
'commodity-v1' => array(
|
||||
'production' => 'http://schemas.elite-markets.net/eddn/commodity/1',
|
||||
'test' => 'http://schemas.elite-markets.net/eddn/commodity/1/test'
|
||||
@ -176,6 +177,8 @@ class EDDN
|
||||
{
|
||||
return $this->_uploaderID;
|
||||
}
|
||||
|
||||
|
||||
public function setSoftwareName($value)
|
||||
{
|
||||
$this->_softwareName = $value;
|
||||
@ -185,6 +188,8 @@ class EDDN
|
||||
{
|
||||
return $this->_softwareName;
|
||||
}
|
||||
|
||||
|
||||
public function setSoftwareVersion($value)
|
||||
{
|
||||
$this->_softwareVersion = $value;
|
||||
@ -194,4 +199,15 @@ class EDDN
|
||||
{
|
||||
return $this->_softwareVersion;
|
||||
}
|
||||
|
||||
|
||||
public function setDebug($value)
|
||||
{
|
||||
self::$_debug = $value;
|
||||
}
|
||||
|
||||
public function getDebug()
|
||||
{
|
||||
return self::$_debug;
|
||||
}
|
||||
}
|
3
examples/Python 2.7/Client_Complete.bat
Normal file
3
examples/Python 2.7/Client_Complete.bat
Normal file
@ -0,0 +1,3 @@
|
||||
"C:\Python27\python.exe" "%~dp0\Client_Complete.py"
|
||||
|
||||
pause
|
@ -1,5 +1,5 @@
|
||||
import zlib
|
||||
import zmq.green as zmq
|
||||
import zmq
|
||||
import simplejson
|
||||
import sys, os, datetime, time
|
||||
|
||||
@ -96,12 +96,18 @@ def main():
|
||||
while True:
|
||||
try:
|
||||
subscriber.connect(__relayEDDN)
|
||||
echoLog('Connect to ' + __relayEDDN)
|
||||
echoLog('')
|
||||
echoLog('')
|
||||
|
||||
while True:
|
||||
__message = subscriber.recv()
|
||||
|
||||
if __message == False:
|
||||
subscriber.disconnect(__relayEDDN)
|
||||
echoLog('Disconnect from ' + __relayEDDN)
|
||||
echoLog('')
|
||||
echoLog('')
|
||||
break
|
||||
|
||||
__message = zlib.decompress(__message)
|
||||
@ -112,8 +118,8 @@ def main():
|
||||
# Handle commodity v1
|
||||
if __json['$schemaRef'] == 'http://schemas.elite-markets.net/eddn/commodity/1' + ('/test' if (__debugEDDN == True) else ''):
|
||||
echoLogJSON(__message)
|
||||
echoLog('Receiving commodity-v1 message...');
|
||||
echoLog(' - Converting to v2...');
|
||||
echoLog('Receiving commodity-v1 message...')
|
||||
echoLog(' - Converting to v2...')
|
||||
|
||||
__temp = {}
|
||||
__temp['$schemaRef'] = 'http://schemas.elite-markets.net/eddn/commodity/2' + ('/test' if (__debugEDDN == True) else '')
|
3
examples/Python 2.7/Client_Simple.bat
Normal file
3
examples/Python 2.7/Client_Simple.bat
Normal file
@ -0,0 +1,3 @@
|
||||
"C:\Python27\python.exe" "%~dp0\Client_Simple.py"
|
||||
|
||||
pause
|
@ -1,5 +1,5 @@
|
||||
import zlib
|
||||
import zmq.green as zmq
|
||||
import zmq
|
||||
import simplejson
|
||||
import sys
|
||||
|
3
examples/Python 3.4/Client_Complete.bat
Normal file
3
examples/Python 3.4/Client_Complete.bat
Normal file
@ -0,0 +1,3 @@
|
||||
"C:\Python34\python.exe" "%~dp0\Client_Complete.py"
|
||||
|
||||
pause
|
218
examples/Python 3.4/Client_Complete.py
Normal file
218
examples/Python 3.4/Client_Complete.py
Normal file
@ -0,0 +1,218 @@
|
||||
import zlib
|
||||
import zmq
|
||||
import simplejson
|
||||
import sys, os, datetime, time
|
||||
|
||||
"""
|
||||
" Configuration
|
||||
"""
|
||||
__relayEDDN = 'tcp://eddn-relay.elite-markets.net:9500'
|
||||
__timeoutEDDN = 600000
|
||||
|
||||
# Set False to listen to production stream
|
||||
__debugEDDN = True;
|
||||
|
||||
# Set to False if you do not want verbose logging
|
||||
__logVerboseFile = os.path.dirname(__file__) + '/Logs_Verbose_EDDN_%DATE%.htm'
|
||||
#__logVerboseFile = False
|
||||
|
||||
# Set to False if you do not want JSON logging
|
||||
__logJSONFile = os.path.dirname(__file__) + '/Logs_JSON_EDDN_%DATE%.log'
|
||||
#__logJSONFile = False
|
||||
|
||||
# A sample list of authorised softwares
|
||||
__authorisedSoftwares = [
|
||||
"EDCE",
|
||||
"ED-TD.SPACE",
|
||||
"EliteOCR",
|
||||
"Maddavo's Market Share",
|
||||
"RegulatedNoise",
|
||||
"RegulatedNoise__DJ"
|
||||
]
|
||||
|
||||
# Used this to excludes yourself for example has you don't want to handle your own messages ^^
|
||||
__excludedSoftwares = [
|
||||
'My Awesome Market Uploader'
|
||||
]
|
||||
|
||||
|
||||
|
||||
"""
|
||||
" Start
|
||||
"""
|
||||
def date(__format):
|
||||
d = datetime.datetime.utcnow()
|
||||
return d.strftime(__format)
|
||||
|
||||
|
||||
__oldTime = False
|
||||
def echoLog(__str):
|
||||
global __oldTime, __logVerboseFile
|
||||
|
||||
if __logVerboseFile != False:
|
||||
__logVerboseFileParsed = __logVerboseFile.replace('%DATE%', str(date('%Y-%m-%d')))
|
||||
|
||||
if __logVerboseFile != False and not os.path.exists(__logVerboseFileParsed):
|
||||
f = open(__logVerboseFileParsed, 'w')
|
||||
f.write('<style type="text/css">html { white-space: pre; font-family: Courier New,Courier,Lucida Sans Typewriter,Lucida Typewriter,monospace; }</style>')
|
||||
f.close()
|
||||
|
||||
if (__oldTime == False) or (__oldTime != date('%H:%M:%S')):
|
||||
__oldTime = date('%H:%M:%S')
|
||||
__str = str(__oldTime) + ' | ' + str(__str)
|
||||
else:
|
||||
__str = ' ' + ' | ' + str(__str)
|
||||
|
||||
print (__str)
|
||||
sys.stdout.flush()
|
||||
|
||||
if __logVerboseFile != False:
|
||||
f = open(__logVerboseFileParsed, 'a')
|
||||
f.write(__str + '\n')
|
||||
f.close()
|
||||
|
||||
|
||||
def echoLogJSON(__json):
|
||||
global __logJSONFile
|
||||
|
||||
if __logJSONFile != False:
|
||||
__logJSONFileParsed = __logJSONFile.replace('%DATE%', str(date('%Y-%m-%d')))
|
||||
|
||||
f = open(__logJSONFileParsed, 'a')
|
||||
f.write(str(__json) + '\n')
|
||||
f.close()
|
||||
|
||||
|
||||
def main():
|
||||
echoLog('Starting EDDN Subscriber')
|
||||
echoLog('')
|
||||
|
||||
context = zmq.Context()
|
||||
subscriber = context.socket(zmq.SUB)
|
||||
|
||||
subscriber.setsockopt(zmq.SUBSCRIBE, b"")
|
||||
subscriber.setsockopt(zmq.RCVTIMEO, __timeoutEDDN)
|
||||
|
||||
while True:
|
||||
try:
|
||||
subscriber.connect(__relayEDDN)
|
||||
echoLog('Connect to ' + __relayEDDN)
|
||||
echoLog('')
|
||||
echoLog('')
|
||||
|
||||
while True:
|
||||
__message = subscriber.recv()
|
||||
|
||||
if __message == False:
|
||||
subscriber.disconnect(__relayEDDN)
|
||||
echoLog('Disconnect from ' + __relayEDDN)
|
||||
echoLog('')
|
||||
echoLog('')
|
||||
break
|
||||
|
||||
__message = zlib.decompress(__message)
|
||||
__json = simplejson.loads(__message)
|
||||
__converted = False
|
||||
|
||||
|
||||
# Handle commodity v1
|
||||
if __json['$schemaRef'] == 'http://schemas.elite-markets.net/eddn/commodity/1' + ('/test' if (__debugEDDN == True) else ''):
|
||||
echoLogJSON(__message)
|
||||
echoLog('Receiving commodity-v1 message...')
|
||||
echoLog(' - Converting to v2...')
|
||||
|
||||
__temp = {}
|
||||
__temp['$schemaRef'] = 'http://schemas.elite-markets.net/eddn/commodity/2' + ('/test' if (__debugEDDN == True) else '')
|
||||
__temp['header'] = __json['header']
|
||||
|
||||
__temp['message'] = {}
|
||||
__temp['message']['systemName'] = __json['message']['systemName']
|
||||
__temp['message']['stationName'] = __json['message']['stationName']
|
||||
__temp['message']['timestamp'] = __json['message']['timestamp']
|
||||
|
||||
__temp['message']['commodities'] = []
|
||||
|
||||
__commodity = {}
|
||||
|
||||
if 'itemName' in __json['message']:
|
||||
__commodity['name'] = __json['message']['itemName']
|
||||
|
||||
if 'buyPrice' in __json['message']:
|
||||
__commodity['buyPrice'] = __json['message']['buyPrice']
|
||||
if 'stationStock' in __json['message']:
|
||||
__commodity['supply'] = __json['message']['stationStock']
|
||||
if 'supplyLevel' in __json['message']:
|
||||
__commodity['supplyLevel'] = __json['message']['supplyLevel']
|
||||
|
||||
if 'sellPrice' in __json['message']:
|
||||
__commodity['sellPrice'] = __json['message']['sellPrice']
|
||||
if 'demand' in __json['message']:
|
||||
__commodity['demand'] = __json['message']['demand']
|
||||
if'demandLevel' in __json['message']:
|
||||
__commodity['demandLevel'] = __json['message']['demandLevel']
|
||||
|
||||
__temp['message']['commodities'].append(__commodity)
|
||||
__json = __temp
|
||||
del __temp, __commodity
|
||||
|
||||
__converted = True
|
||||
|
||||
# Handle commodity v2
|
||||
if __json['$schemaRef'] == 'http://schemas.elite-markets.net/eddn/commodity/2' + ('/test' if (__debugEDDN == True) else ''):
|
||||
if __converted == False:
|
||||
echoLogJSON(__message)
|
||||
echoLog('Receiving commodity-v2 message...')
|
||||
|
||||
__authorised = False
|
||||
__excluded = False
|
||||
|
||||
if __json['header']['softwareName'] in __authorisedSoftwares:
|
||||
__authorised = True
|
||||
if __json['header']['softwareName'] in __excludedSoftwares:
|
||||
__excluded = True
|
||||
|
||||
echoLog(' - Software: ' + __json['header']['softwareName'] + ' / ' + __json['header']['softwareVersion'])
|
||||
echoLog(' - ' + 'AUTHORISED' if (__authorised == True) else
|
||||
('EXCLUDED' if (__excluded == True) else 'UNAUTHORISED')
|
||||
)
|
||||
|
||||
if __authorised == True and __excluded == False:
|
||||
# Do what you want with the data...
|
||||
# Have fun !
|
||||
|
||||
# For example
|
||||
echoLog(' - Timestamp: ' + __json['message']['timestamp'])
|
||||
echoLog(' - Uploader ID: ' + __json['header']['uploaderID'])
|
||||
echoLog(' - System Name: ' + __json['message']['systemName'])
|
||||
echoLog(' - Station Name: ' + __json['message']['stationName'])
|
||||
|
||||
for __commodity in __json['message']['commodities']:
|
||||
echoLog(' - Name: ' + __commodity['name'])
|
||||
echoLog(' - Buy Price: ' + str(__commodity['buyPrice']))
|
||||
echoLog(' - Supply: ' + str(__commodity['supply'])
|
||||
+ ((' (' + __commodity['supplyLevel'] + ')') if 'supplyLevel' in __commodity else '')
|
||||
)
|
||||
echoLog(' - Sell Price: ' + str(__commodity['sellPrice']))
|
||||
echoLog(' - Demand: ' + str(__commodity['demand'])
|
||||
+ ((' (' + __commodity['demandLevel'] + ')') if 'demandLevel' in __commodity else '')
|
||||
)
|
||||
# End example
|
||||
|
||||
del __authorised, __excluded
|
||||
|
||||
echoLog('')
|
||||
echoLog('')
|
||||
|
||||
del __converted
|
||||
|
||||
|
||||
except zmq.ZMQError as e:
|
||||
echoLog('')
|
||||
echoLog('ZMQSocketException: ' + str(e))
|
||||
echoLog('')
|
||||
time.sleep(10)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
3
examples/Python 3.4/Client_Simple.bat
Normal file
3
examples/Python 3.4/Client_Simple.bat
Normal file
@ -0,0 +1,3 @@
|
||||
"C:\Python34\python.exe" "%~dp0\Client_Simple.py"
|
||||
|
||||
pause
|
50
examples/Python 3.4/Client_Simple.py
Normal file
50
examples/Python 3.4/Client_Simple.py
Normal file
@ -0,0 +1,50 @@
|
||||
import zlib
|
||||
import zmq
|
||||
import simplejson
|
||||
import sys
|
||||
|
||||
"""
|
||||
" Configuration
|
||||
"""
|
||||
__relayEDDN = 'tcp://eddn-relay.elite-markets.net:9500'
|
||||
__timeoutEDDN = 600000
|
||||
|
||||
|
||||
|
||||
"""
|
||||
" Start
|
||||
"""
|
||||
def main():
|
||||
context = zmq.Context()
|
||||
subscriber = context.socket(zmq.SUB)
|
||||
|
||||
subscriber.setsockopt(zmq.SUBSCRIBE, b"")
|
||||
subscriber.setsockopt(zmq.RCVTIMEO, __timeoutEDDN)
|
||||
|
||||
while True:
|
||||
try:
|
||||
subscriber.connect(__relayEDDN)
|
||||
|
||||
while True:
|
||||
__message = subscriber.recv()
|
||||
|
||||
if __message == False:
|
||||
subscriber.disconnect(__relayEDDN)
|
||||
break
|
||||
|
||||
__message = zlib.decompress(__message)
|
||||
__json = simplejson.loads(__message)
|
||||
|
||||
|
||||
print (__json)
|
||||
sys.stdout.flush()
|
||||
|
||||
except zmq.ZMQError as e:
|
||||
print ('ZMQSocketException: ' + str(e))
|
||||
sys.stdout.flush()
|
||||
time.sleep(10)
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,3 +0,0 @@
|
||||
"python" %~dp0\Client_Complete.py
|
||||
|
||||
pause
|
@ -1,3 +0,0 @@
|
||||
"python" %~dp0\Client_Simple.py
|
||||
|
||||
pause
|
@ -10,6 +10,7 @@
|
||||
"header" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : true,
|
||||
"required": [ "uploaderID", "softwareName", "softwareVersion" ],
|
||||
"properties" : {
|
||||
"uploaderID" : { "type": "string" },
|
||||
"softwareName" : { "type": "string" },
|
||||
|
3
setup.py
3
setup.py
@ -3,7 +3,7 @@ import re
|
||||
import glob
|
||||
|
||||
|
||||
VERSIONFILE = "src/eddn/_version.py"
|
||||
VERSIONFILE = "src/eddn/_Conf/Version.py"
|
||||
verstr = "unknown"
|
||||
try:
|
||||
verstrline = open(VERSIONFILE, "rt").read()
|
||||
@ -34,6 +34,7 @@ setup(
|
||||
'console_scripts': [
|
||||
'eddn-gateway = eddn.Gateway:main',
|
||||
'eddn-relay = eddn.Relay:main',
|
||||
'eddn-monitor = eddn.Monitor:main',
|
||||
],
|
||||
}
|
||||
)
|
||||
|
@ -14,7 +14,7 @@ from datetime import datetime
|
||||
import os
|
||||
|
||||
from eddn._Conf.Settings import Settings, loadConfig
|
||||
from eddn.Validator import Validator, ValidationSeverity
|
||||
from eddn._Core.Validator import Validator, ValidationSeverity
|
||||
|
||||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
@ -29,7 +29,7 @@ sender = context.socket(zmq.PUB)
|
||||
validator = Validator()
|
||||
|
||||
# This import must be done post-monkey-patching!
|
||||
from eddn.StatsCollector import StatsCollector
|
||||
from eddn._Core.StatsCollector import StatsCollector
|
||||
statsCollector = StatsCollector()
|
||||
statsCollector.start()
|
||||
|
||||
@ -42,11 +42,10 @@ def configure():
|
||||
sender.bind(binding)
|
||||
|
||||
for schemaRef, schemaFile in Settings.GATEWAY_JSON_SCHEMAS.iteritems():
|
||||
#filename = resource_filename(Requirement.parse("eddn"), schemaFile)
|
||||
validator.addSchemaResource(schemaRef, os.path.dirname(__file__) + '/' + schemaFile)
|
||||
|
||||
|
||||
def push_message(string_message):
|
||||
def push_message(string_message, topic):
|
||||
"""
|
||||
Spawned as a greenlet to push messages (strings) through ZeroMQ.
|
||||
This is a dumb method that just pushes strings; it assumes you've already validated
|
||||
@ -54,9 +53,9 @@ def push_message(string_message):
|
||||
"""
|
||||
|
||||
# Push a zlib compressed JSON representation of the message to
|
||||
# announcers.
|
||||
# announcers with schema as topic
|
||||
compressed_msg = zlib.compress(string_message)
|
||||
sender.send(compressed_msg)
|
||||
sender.send("%s |-| %s" % (topic, compressed_msg))
|
||||
statsCollector.tally("outbound")
|
||||
|
||||
|
||||
@ -143,7 +142,7 @@ def parse_and_error_handle(data):
|
||||
|
||||
# Sends the parsed MarketOrderList or MarketHistoryList to the Announcers
|
||||
# as compressed JSON.
|
||||
gevent.spawn(push_message, simplejson.dumps(parsed_message))
|
||||
gevent.spawn(push_message, simplejson.dumps(parsed_message), parsed_message['$schemaRef'])
|
||||
logger.info("Accepted %s upload from %s" % (
|
||||
parsed_message, get_remote_address()
|
||||
))
|
||||
|
251
src/eddn/Monitor.py
Normal file
251
src/eddn/Monitor.py
Normal file
@ -0,0 +1,251 @@
|
||||
"""
|
||||
Monitor sit below gateways, or another relay, and simply parse what it receives over SUB.
|
||||
"""
|
||||
from threading import Thread
|
||||
import zlib
|
||||
import gevent
|
||||
import simplejson
|
||||
import sqlite3
|
||||
import datetime
|
||||
import collections
|
||||
import zmq.green as zmq
|
||||
from bottle import get, request, response, run as bottle_run
|
||||
from eddn._Conf.Settings import Settings, loadConfig
|
||||
|
||||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
|
||||
if Settings.RELAY_DUPLICATE_MAX_MINUTES:
|
||||
from eddn._Core.DuplicateMessages import DuplicateMessages
|
||||
duplicateMessages = DuplicateMessages()
|
||||
duplicateMessages.start()
|
||||
|
||||
def date(__format):
|
||||
d = datetime.datetime.utcnow()
|
||||
return d.strftime(__format)
|
||||
|
||||
|
||||
@get('/getTotalSoftwares/')
|
||||
def getTotalSoftwares():
|
||||
response.set_header("Access-Control-Allow-Origin", "*")
|
||||
db = sqlite3.connect(Settings.MONITOR_DB)
|
||||
softwares = collections.OrderedDict()
|
||||
|
||||
maxDays = request.GET.get('maxDays', '31').strip()
|
||||
maxDays = int(maxDays) -1;
|
||||
|
||||
query = """SELECT name, SUM(hits) AS total, MAX(dateStats) AS maxDate
|
||||
FROM softwares
|
||||
GROUP BY name
|
||||
HAVING maxDate >= DATE('now', '""" + '-' + str(maxDays) + """ day')
|
||||
ORDER BY total DESC"""
|
||||
results = db.execute(query)
|
||||
|
||||
for row in results:
|
||||
softwares[str(row[0])] = str(row[1])
|
||||
|
||||
db.close()
|
||||
|
||||
return simplejson.dumps(softwares)
|
||||
|
||||
@get('/getSoftwares/')
|
||||
def getSoftwares():
|
||||
response.set_header("Access-Control-Allow-Origin", "*")
|
||||
db = sqlite3.connect(Settings.MONITOR_DB)
|
||||
softwares = collections.OrderedDict()
|
||||
|
||||
dateStart = request.GET.get('dateStart', str(date('%Y-%m-%d'))).strip()
|
||||
dateEnd = request.GET.get('dateEnd', str(date('%Y-%m-%d'))).strip()
|
||||
|
||||
query = """SELECT *
|
||||
FROM softwares
|
||||
WHERE dateStats BETWEEN ? AND ?
|
||||
ORDER BY hits DESC, dateStats ASC"""
|
||||
results = db.execute(query, (dateStart, dateEnd))
|
||||
|
||||
for row in results:
|
||||
if not str(row[2]) in softwares.keys():
|
||||
softwares[str(row[2])] = collections.OrderedDict()
|
||||
|
||||
softwares[str(row[2])][str(row[0])] = str(row[1])
|
||||
|
||||
db.close()
|
||||
|
||||
return simplejson.dumps(softwares)
|
||||
|
||||
@get('/getTotalUploaders/')
|
||||
def getTotalUploaders():
|
||||
response.set_header("Access-Control-Allow-Origin", "*")
|
||||
db = sqlite3.connect(Settings.MONITOR_DB)
|
||||
uploaders = collections.OrderedDict()
|
||||
|
||||
limit = request.GET.get('limit', '20').strip()
|
||||
|
||||
query = """SELECT name, SUM(hits) AS total
|
||||
FROM uploaders
|
||||
GROUP BY name
|
||||
ORDER BY total DESC
|
||||
LIMIT """ + limit
|
||||
results = db.execute(query)
|
||||
|
||||
for row in results:
|
||||
uploaders[str(row[0])] = str(row[1])
|
||||
|
||||
db.close()
|
||||
|
||||
return simplejson.dumps(uploaders)
|
||||
|
||||
@get('/getUploaders/')
|
||||
def getUploaders():
|
||||
response.set_header("Access-Control-Allow-Origin", "*")
|
||||
db = sqlite3.connect(Settings.MONITOR_DB)
|
||||
uploaders = collections.OrderedDict()
|
||||
|
||||
dateStart = request.GET.get('dateStart', str(date('%Y-%m-%d'))).strip()
|
||||
dateEnd = request.GET.get('dateEnd', str(date('%Y-%m-%d'))).strip()
|
||||
|
||||
query = """SELECT *
|
||||
FROM uploaders
|
||||
WHERE dateStats BETWEEN ? AND ?
|
||||
ORDER BY hits DESC, dateStats ASC"""
|
||||
results = db.execute(query, (dateStart, dateEnd))
|
||||
|
||||
for row in results:
|
||||
if not str(row[2]) in uploaders.keys():
|
||||
uploaders[str(row[2])] = collections.OrderedDict()
|
||||
|
||||
uploaders[str(row[2])][str(row[0])] = str(row[1])
|
||||
|
||||
db.close()
|
||||
|
||||
return simplejson.dumps(uploaders)
|
||||
|
||||
@get('/getTotalSchemas/')
|
||||
def getTotalSchemas():
|
||||
response.set_header("Access-Control-Allow-Origin", "*")
|
||||
db = sqlite3.connect(Settings.MONITOR_DB)
|
||||
schemas = collections.OrderedDict()
|
||||
|
||||
query = """SELECT name, SUM(hits) AS total
|
||||
FROM schemas
|
||||
GROUP BY name
|
||||
ORDER BY total DESC"""
|
||||
results = db.execute(query)
|
||||
|
||||
for row in results:
|
||||
schemas[str(row[0])] = str(row[1])
|
||||
|
||||
db.close()
|
||||
|
||||
return simplejson.dumps(schemas)
|
||||
|
||||
@get('/getSchemas/')
|
||||
def getSchemas():
|
||||
response.set_header("Access-Control-Allow-Origin", "*")
|
||||
db = sqlite3.connect(Settings.MONITOR_DB)
|
||||
schemas = collections.OrderedDict()
|
||||
|
||||
dateStart = request.GET.get('dateStart', str(date('%Y-%m-%d'))).strip()
|
||||
dateEnd = request.GET.get('dateEnd', str(date('%Y-%m-%d'))).strip()
|
||||
|
||||
query = """SELECT *
|
||||
FROM schemas
|
||||
WHERE dateStats BETWEEN ? AND ?
|
||||
ORDER BY hits DESC, dateStats ASC"""
|
||||
results = db.execute(query, (dateStart, dateEnd))
|
||||
|
||||
for row in results:
|
||||
if not str(row[2]) in schemas.keys():
|
||||
schemas[str(row[2])] = collections.OrderedDict()
|
||||
|
||||
schemas[str(row[2])][str(row[0])] = str(row[1])
|
||||
|
||||
db.close()
|
||||
|
||||
return simplejson.dumps(schemas)
|
||||
|
||||
|
||||
|
||||
class Monitor(Thread):
|
||||
|
||||
def run(self):
|
||||
context = zmq.Context()
|
||||
|
||||
receiver = context.socket(zmq.SUB)
|
||||
receiver.setsockopt(zmq.SUBSCRIBE, '')
|
||||
|
||||
for binding in Settings.MONITOR_RECEIVER_BINDINGS:
|
||||
receiver.connect(binding)
|
||||
|
||||
def monitor_worker(message):
|
||||
db = sqlite3.connect(Settings.MONITOR_DB)
|
||||
|
||||
# Separate topic from message
|
||||
message = message.split(' |-| ')
|
||||
|
||||
# Handle gateway not sending topic
|
||||
if len(message) > 1:
|
||||
message = message[1]
|
||||
else:
|
||||
message = message[0]
|
||||
|
||||
|
||||
if Settings.RELAY_DUPLICATE_MAX_MINUTES:
|
||||
if duplicateMessages.isDuplicated(message):
|
||||
schemaID = 'DUPLICATE MESSAGE'
|
||||
|
||||
c = db.cursor()
|
||||
c.execute('UPDATE schemas SET hits = hits + 1 WHERE `name` = ? AND `dateStats` = DATE("now", "utc")', (schemaID, ))
|
||||
c.execute('INSERT OR IGNORE INTO schemas (name, dateStats) VALUES (?, DATE("now", "utc"))', (schemaID, ))
|
||||
db.commit()
|
||||
|
||||
return
|
||||
|
||||
if Settings.MONITOR_DECOMPRESS_MESSAGES:
|
||||
message = zlib.decompress(message)
|
||||
|
||||
json = simplejson.loads(message)
|
||||
|
||||
# Update software count
|
||||
softwareID = json['header']['softwareName'] + ' | ' + json['header']['softwareVersion']
|
||||
|
||||
c = db.cursor()
|
||||
c.execute('UPDATE softwares SET hits = hits + 1 WHERE `name` = ? AND `dateStats` = DATE("now", "utc")', (softwareID, ))
|
||||
c.execute('INSERT OR IGNORE INTO softwares (name, dateStats) VALUES (?, DATE("now", "utc"))', (softwareID, ))
|
||||
db.commit()
|
||||
|
||||
|
||||
# Update uploader count
|
||||
uploaderID = json['header']['uploaderID']
|
||||
|
||||
if uploaderID: # Don't get empty uploaderID
|
||||
c = db.cursor()
|
||||
c.execute('UPDATE uploaders SET hits = hits + 1 WHERE `name` = ? AND `dateStats` = DATE("now", "utc")', (uploaderID, ))
|
||||
c.execute('INSERT OR IGNORE INTO uploaders (name, dateStats) VALUES (?, DATE("now", "utc"))', (uploaderID, ))
|
||||
db.commit()
|
||||
|
||||
|
||||
# Update schemas count
|
||||
schemaID = json['$schemaRef']
|
||||
|
||||
c = db.cursor()
|
||||
c.execute('UPDATE schemas SET hits = hits + 1 WHERE `name` = ? AND `dateStats` = DATE("now", "utc")', (schemaID, ))
|
||||
c.execute('INSERT OR IGNORE INTO schemas (name, dateStats) VALUES (?, DATE("now", "utc"))', (schemaID, ))
|
||||
db.commit()
|
||||
|
||||
db.close()
|
||||
|
||||
while True:
|
||||
inboundMessage = receiver.recv()
|
||||
gevent.spawn(monitor_worker, inboundMessage)
|
||||
|
||||
|
||||
def main():
|
||||
loadConfig()
|
||||
m = Monitor()
|
||||
m.start()
|
||||
bottle_run(host='0.0.0.0', port=9091, server='gevent')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -13,16 +13,21 @@ import gevent
|
||||
import simplejson
|
||||
import zmq.green as zmq
|
||||
from bottle import get, response, run as bottle_run
|
||||
from eddn.conf.Settings import Settings, loadConfig
|
||||
from eddn._Conf.Settings import Settings, loadConfig
|
||||
|
||||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
|
||||
from eddn.StatsCollector import StatsCollector
|
||||
from eddn._Core.StatsCollector import StatsCollector
|
||||
|
||||
statsCollector = StatsCollector()
|
||||
statsCollector.start()
|
||||
|
||||
if Settings.RELAY_DUPLICATE_MAX_MINUTES:
|
||||
from eddn._Core.DuplicateMessages import DuplicateMessages
|
||||
duplicateMessages = DuplicateMessages()
|
||||
duplicateMessages.start()
|
||||
|
||||
|
||||
@get('/stats/')
|
||||
def stats():
|
||||
@ -42,12 +47,23 @@ class Relay(Thread):
|
||||
context = zmq.Context()
|
||||
|
||||
receiver = context.socket(zmq.SUB)
|
||||
receiver.setsockopt(zmq.SUBSCRIBE, '')
|
||||
|
||||
# Filters on topics or not...
|
||||
if Settings.RELAY_RECEIVE_ONLY_GATEWAY_EXTRA_JSON == True:
|
||||
for schemaRef, schemaFile in Settings.GATEWAY_JSON_SCHEMAS.iteritems():
|
||||
receiver.setsockopt(zmq.SUBSCRIBE, schemaRef)
|
||||
for schemaRef, schemaFile in Settings.RELAY_EXTRA_JSON_SCHEMAS.iteritems():
|
||||
receiver.setsockopt(zmq.SUBSCRIBE, schemaRef)
|
||||
else:
|
||||
receiver.setsockopt(zmq.SUBSCRIBE, '')
|
||||
|
||||
|
||||
for binding in Settings.RELAY_RECEIVER_BINDINGS:
|
||||
# Relays bind upstream to an Announcer, or another Relay.
|
||||
receiver.connect(binding)
|
||||
|
||||
sender = context.socket(zmq.PUB)
|
||||
|
||||
for binding in Settings.RELAY_SENDER_BINDINGS:
|
||||
# End users, or other relays, may attach here.
|
||||
sender.bind(binding)
|
||||
@ -58,9 +74,20 @@ class Relay(Thread):
|
||||
to any subscribers.
|
||||
:param str message: A JSON string to re-broadcast.
|
||||
"""
|
||||
# if is_message_duped(message):
|
||||
# We've already seen this message recently. Discard it.
|
||||
# return
|
||||
# Separate topic from message
|
||||
message = message.split(' |-| ')
|
||||
|
||||
# Handle gateway not sending topic
|
||||
if len(message) > 1:
|
||||
message = message[1]
|
||||
else:
|
||||
message = message[0]
|
||||
|
||||
if Settings.RELAY_DUPLICATE_MAX_MINUTES:
|
||||
if duplicateMessages.isDuplicated(message):
|
||||
# We've already seen this message recently. Discard it.
|
||||
statsCollector.tally("duplicate")
|
||||
return
|
||||
|
||||
if Settings.RELAY_DECOMPRESS_MESSAGES:
|
||||
message = zlib.decompress(message)
|
||||
|
@ -6,7 +6,7 @@ Created on 15 Nov 2014
|
||||
|
||||
import argparse
|
||||
import simplejson
|
||||
from eddn._version import __version__ as version
|
||||
from eddn._Conf.Version import __version__ as version
|
||||
|
||||
|
||||
class _Settings(object):
|
||||
@ -17,11 +17,22 @@ class _Settings(object):
|
||||
# Relay settings
|
||||
###############################################################################
|
||||
|
||||
RELAY_RECEIVER_BINDINGS = ["tcp://localhost:8500"]
|
||||
#RELAY_RECEIVER_BINDINGS = ["tcp://localhost:8500"]
|
||||
RELAY_RECEIVER_BINDINGS = ["tcp://eddn-gateway.elite-markets.net:8500", "tcp://eddn-gateway.ed-td.space:8500"]
|
||||
|
||||
RELAY_SENDER_BINDINGS = ["tcp://*:9500"]
|
||||
|
||||
RELAY_DECOMPRESS_MESSAGES = False
|
||||
|
||||
# If set to False, no deduplicate is made
|
||||
RELAY_DUPLICATE_MAX_MINUTES = 15
|
||||
|
||||
# If set to false, don't listen to topic and accept all incoming messages
|
||||
RELAY_RECEIVE_ONLY_GATEWAY_EXTRA_JSON = False
|
||||
|
||||
RELAY_EXTRA_JSON_SCHEMAS = {
|
||||
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Gateway settings
|
||||
@ -36,10 +47,23 @@ class _Settings(object):
|
||||
GATEWAY_JSON_SCHEMAS = {
|
||||
"http://schemas.elite-markets.net/eddn/commodity/1": "schemas/commodity-v0.1.json",
|
||||
"http://schemas.elite-markets.net/eddn/commodity/1/test": "schemas/commodity-v0.1.json",
|
||||
"http://schemas.elite-markets.net/eddn/commodity/2": "schemas/commodity-v2.0-draft.json",
|
||||
"http://schemas.elite-markets.net/eddn/commodity/2/test": "schemas/commodity-v2.0-draft.json"
|
||||
|
||||
"http://schemas.elite-markets.net/eddn/commodity/2": "schemas/commodity-v2.0.json",
|
||||
"http://schemas.elite-markets.net/eddn/commodity/2/test": "schemas/commodity-v2.0.json"
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Monitor settings
|
||||
###############################################################################
|
||||
|
||||
MONITOR_RECEIVER_BINDINGS = ["tcp://eddn-gateway.elite-markets.net:8500", "tcp://eddn-gateway.ed-td.space:8500"]
|
||||
|
||||
MONITOR_DB = "/home/EDDN_Monitor.s3db"
|
||||
|
||||
MONITOR_DECOMPRESS_MESSAGES = True
|
||||
|
||||
|
||||
|
||||
def loadFrom(self, fileName):
|
||||
f = open(fileName, 'r')
|
||||
conf = simplejson.load(f)
|
1
src/eddn/_Conf/Version.py
Normal file
1
src/eddn/_Conf/Version.py
Normal file
@ -0,0 +1 @@
|
||||
__version__ = "0.4"
|
49
src/eddn/_Core/DuplicateMessages.py
Normal file
49
src/eddn/_Core/DuplicateMessages.py
Normal file
@ -0,0 +1,49 @@
|
||||
from datetime import datetime, timedelta
|
||||
from threading import Lock, Thread
|
||||
from time import sleep
|
||||
import hashlib
|
||||
import zlib
|
||||
import simplejson
|
||||
from eddn._Conf.Settings import Settings, loadConfig
|
||||
|
||||
|
||||
class DuplicateMessages(Thread):
|
||||
max_minutes = Settings.RELAY_DUPLICATE_MAX_MINUTES
|
||||
|
||||
caches = {}
|
||||
|
||||
lock = Lock()
|
||||
|
||||
def __init__(self):
|
||||
super(DuplicateMessages, self).__init__()
|
||||
self.daemon = True
|
||||
|
||||
def run(self):
|
||||
while True:
|
||||
sleep(60)
|
||||
with self.lock:
|
||||
maxTime = datetime.utcnow()
|
||||
|
||||
for key in self.caches.keys():
|
||||
if self.caches[key] + timedelta(minutes=self.max_minutes) < maxTime:
|
||||
del self.caches[key]
|
||||
|
||||
def isDuplicated(self, message):
|
||||
with self.lock:
|
||||
message = zlib.decompress(message)
|
||||
message = simplejson.loads(message)
|
||||
|
||||
if message['header']['gatewayTimestamp']:
|
||||
del message['header']['gatewayTimestamp'] # Prevent dupe with new timestamp ^^
|
||||
if message['message']['timestamp']:
|
||||
del message['message']['timestamp'] # Prevent dupe with new timestamp ^^
|
||||
|
||||
message = simplejson.dumps(message)
|
||||
key = hashlib.sha256(message).hexdigest()
|
||||
|
||||
if key not in self.caches:
|
||||
self.caches[key] = datetime.utcnow()
|
||||
return False
|
||||
else:
|
||||
self.caches[key] = datetime.utcnow()
|
||||
return True
|
0
src/eddn/_Core/__init__.py
Normal file
0
src/eddn/_Core/__init__.py
Normal file
@ -1 +0,0 @@
|
||||
__version__ = "0.3"
|
Loading…
x
Reference in New Issue
Block a user