Support for aggregate node view (#5902)

* Introduce sign-in banner

* Show/hide sign-in banner

* Improvements

* Fixed banner for white theme.

* Preliminary work on integrating console with agent

* Updated console

* Fetch js and css from cloud #431

* Minor

* Fixed for better console embedding

* Fixed a problem with the Makefile

* Minor

* Dynamically generate index fragment based on cloudBaseURL

* Relative path to console

* Added cache-busters

* Add Nodes view access

* Improved console/index.html

* Fixes in console/index

* Version bump

* Removed extraneous react import (thanks Nicolas)

* Use netdataNoBootstrap

* Use white theme

* Touch agent API call (updates lastseen timestamp)

* Revert to API version 1

* Move touch to init cloud

* Remove logs

* Updated js file href

* Nodes view on unsigned redirect

* Revert yellow color

* Remove comment

* Beta color update

* Move styles to CSS

* Cloud console link fix

* Remove cloudBaseUrl

* Use local dashboard.js, duh!

* Added GoogleTagManager

* Remove duplicate dashboard.js

* Analytics fix

Analytics fix
This commit is contained in:
George Moschovitis 2019-05-15 16:50:01 +03:00 committed by Chris Akritidis
parent 288787a54c
commit 4721ed4f14
8 changed files with 250 additions and 100 deletions

1
.gitignore vendored
View File

@ -130,6 +130,7 @@ cmake_install.cmake
.jetbrains* .jetbrains*
.DS_Store .DS_Store
webcopylocal*
contrib/debian/changelog contrib/debian/changelog

View File

@ -69,6 +69,17 @@ dist_web_DATA = \
version.txt \ version.txt \
$(NULL) $(NULL)
webconsoledir=$(webdir)/console
dist_webconsole_DATA = \
console/index.html \
$(NULL)
webstaticdir=$(webdir)/static/img
dist_webstatic_DATA = \
static/img/netdata-logomark.svg \
$(NULL)
weblibdir=$(webdir)/lib weblibdir=$(webdir)/lib
dist_weblib_DATA = \ dist_weblib_DATA = \
lib/bootstrap-3.3.7.min.js \ lib/bootstrap-3.3.7.min.js \

View File

@ -0,0 +1,72 @@
<!DOCTYPE html>
<html>
<head>
<title>Netdata Console</title>
<meta name="application-name" content="netdata" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<link rel="icon" href="/favicon.ico" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<!-- Google Tag Manager -->
<script>(function (w, d, s, l, i) {
w[l] = w[l] || []; w[l].push({
'gtm.start':
new Date().getTime(), event: 'gtm.js'
}); var f = d.getElementsByTagName(s)[0],
j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : ''; j.async = true; j.src =
'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'dataLayer', 'GTM-N6CBMJD');
googleTagManager = window.dataLayer || [];
</script>
<!-- End Google Tag Manager -->
<script>
function loadJSFile(filename) {
const s = document.createElement("script")
s.setAttribute("type", "text/javascript")
s.setAttribute("src", filename)
document.getElementsByTagName("head")[0].appendChild(s)
}
function loadCSSFile(filename) {
const l = document.createElement("link")
l.setAttribute("rel", "stylesheet")
l.setAttribute("type", "text/css")
l.setAttribute("href", filename)
document.getElementsByTagName("head")[0].appendChild(l)
}
var cloudBaseURL = localStorage.getItem("cloud.baseURL") || "https://netdata.cloud"
loadCSSFile(`${cloudBaseURL}/static/console/main.css?v=4`)
</script>
</head>
<body class="mdc-theme--background mdc-typography">
<!-- Google Tag Manager (noscript) -->
<noscript>
<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-N6CBMJD" height="0" width="0"
style="display:none;visibility:hidden"></iframe>
</noscript>
<!-- End Google Tag Manager (noscript) -->
<main id="app" class="console-app" data-host="agent"></main>
<script>
const main = document.getElementById("app")
main.setAttribute("data-static-base-url", cloudBaseURL)
main.setAttribute("data-api-base-url", cloudBaseURL)
main.setAttribute("data-api-base-url-v2", cloudBaseURL)
loadJSFile(`${cloudBaseURL}/static/console/main.js?v=4`)
</script>
<script>
var netdataTheme = "white"
var netdataNoBootstrap = true
</script>
<script type="text/javascript" src="/dashboard.js?v20190515"></script>
</body>
</html>

View File

@ -124,6 +124,7 @@
</div> </div>
<nav class="collapse navbar-collapse navbar-right" role="navigation"> <nav class="collapse navbar-collapse navbar-right" role="navigation">
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li title="Nodes view" data-toggle="tooltip" data-placement="bottom"><a onclick="openAuthenticatedUrl('console/index.html');" class="btn" target="_blank"><i class="fas fa-tv"></i>&nbsp;<span class="hidden-sm hidden-md">Nodes<sup class="beta"> beta</sup></span></a></li>
<li id="alarmsButton" title="check the health monitoring alarms and their log" data-toggle="tooltip" data-placement="bottom"><a href="#" class="btn" data-toggle="modal" data-target="#alarmsModal"><i class="fas fa-bell"></i>&nbsp;<span class="hidden-sm hidden-md">Alarms&nbsp;</span><span id="alarms_count_badge" class="badge"></span></a></li> <li id="alarmsButton" title="check the health monitoring alarms and their log" data-toggle="tooltip" data-placement="bottom"><a href="#" class="btn" data-toggle="modal" data-target="#alarmsModal"><i class="fas fa-bell"></i>&nbsp;<span class="hidden-sm hidden-md">Alarms&nbsp;</span><span id="alarms_count_badge" class="badge"></span></a></li>
<li title="change dashboard settings" data-toggle="tooltip" data-placement="bottom"><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fas fa-cog"></i>&nbsp;<span class="hidden-sm hidden-md">Settings</span></a></li> <li title="change dashboard settings" data-toggle="tooltip" data-placement="bottom"><a href="#" class="btn" data-toggle="modal" data-target="#optionsModal"><i class="fas fa-cog"></i>&nbsp;<span class="hidden-sm hidden-md">Settings</span></a></li>
<li title="check for netdata updates<br/>you should keep your netdata updated" data-toggle="tooltip" data-placement="bottom" class="hidden-sm" id="updateButton"><a href="#" class="btn" data-toggle="modal" data-target="#updateModal"><i class="fas fa-cloud-download-alt"></i> <span class="hidden-sm hidden-md">Update </span><span id="update_badge" class="badge"></span></a></li> <li title="check for netdata updates<br/>you should keep your netdata updated" data-toggle="tooltip" data-placement="bottom" class="hidden-sm" id="updateButton"><a href="#" class="btn" data-toggle="modal" data-target="#updateModal"><i class="fas fa-cloud-download-alt"></i> <span class="hidden-sm hidden-md">Update </span><span id="update_badge" class="badge"></span></a></li>

View File

@ -713,3 +713,7 @@ body.modal-open {
margin-left: 5px; margin-left: 5px;
color: #17CE8A; color: #17CE8A;
} }
.beta {
color:#FFCC00;
}

View File

@ -258,7 +258,7 @@ var urlOptions = {
$('.highlight-tooltip').tooltip({ $('.highlight-tooltip').tooltip({
html: true, html: true,
delay: {show: 500, hide: 0}, delay: { show: 500, hide: 0 },
container: 'body' container: 'body'
}); });
} else { } else {
@ -588,20 +588,20 @@ function renderMachines(machinesArray) {
const alternateUrlItems = ( const alternateUrlItems = (
`<div class="agent-alternate-urls agent-${machine.guid} collapsed"> `<div class="agent-alternate-urls agent-${machine.guid} collapsed">
${machine.alternate_urls.reduce((str, url) => { ${machine.alternate_urls.reduce((str, url) => {
if (url === maskedURL) { if (url === maskedURL) {
return str return str
} }
return str + ( return str + (
`<div class="agent-item agent-item--alternate"> `<div class="agent-item agent-item--alternate">
<div></div> <div></div>
<a href="${url}" title="${url}">${truncateString(url, 64)}</a> <a href="${url}" title="${url}">${truncateString(url, 64)}</a>
<a href="#" onclick="deleteRegistryModalHandler('${machine.guid}', '${machine.name}', '${url}'); return false;"> <a href="#" onclick="deleteRegistryModalHandler('${machine.guid}', '${machine.name}', '${url}'); return false;">
<i class="fas fa-trash" style="color: #777;"></i> <i class="fas fa-trash" style="color: #777;"></i>
</a> </a>
</div>` </div>`
) )
}, },
'' ''
)} )}
</div>` </div>`
@ -650,14 +650,14 @@ function renderMachines(machinesArray) {
html += `<div class="info-item">Demo netdata nodes</div>`; html += `<div class="info-item">Demo netdata nodes</div>`;
const demoServers = [ const demoServers = [
{url: "//london.netdata.rocks/default.html", title: "UK - London (DigitalOcean.com)"}, { url: "//london.netdata.rocks/default.html", title: "UK - London (DigitalOcean.com)" },
{url: "//newyork.netdata.rocks/default.html", title: "US - New York (DigitalOcean.com)"}, { url: "//newyork.netdata.rocks/default.html", title: "US - New York (DigitalOcean.com)" },
{url: "//sanfrancisco.netdata.rocks/default.html", title: "US - San Francisco (DigitalOcean.com)"}, { url: "//sanfrancisco.netdata.rocks/default.html", title: "US - San Francisco (DigitalOcean.com)" },
{url: "//atlanta.netdata.rocks/default.html", title: "US - Atlanta (CDN77.com)"}, { url: "//atlanta.netdata.rocks/default.html", title: "US - Atlanta (CDN77.com)" },
{url: "//frankfurt.netdata.rocks/default.html", title: "Germany - Frankfurt (DigitalOcean.com)"}, { url: "//frankfurt.netdata.rocks/default.html", title: "Germany - Frankfurt (DigitalOcean.com)" },
{url: "//toronto.netdata.rocks/default.html", title: "Canada - Toronto (DigitalOcean.com)"}, { url: "//toronto.netdata.rocks/default.html", title: "Canada - Toronto (DigitalOcean.com)" },
{url: "//singapore.netdata.rocks/default.html", title: "Japan - Singapore (DigitalOcean.com)"}, { url: "//singapore.netdata.rocks/default.html", title: "Japan - Singapore (DigitalOcean.com)" },
{url: "//bangalore.netdata.rocks/default.html", title: "India - Bangalore (DigitalOcean.com)"}, { url: "//bangalore.netdata.rocks/default.html", title: "India - Bangalore (DigitalOcean.com)" },
] ]
@ -704,6 +704,14 @@ function restrictMyNetdataMenu() {
</div>`); </div>`);
} }
function openAuthenticatedUrl(url) {
if (isSignedIn()) {
window.open(url);
} else {
window.open(`${NETDATA.registry.cloudBaseURL}/account/sign-in-agent?id=${NETDATA.registry.machine_guid}&name=${encodeURIComponent(NETDATA.registry.hostname)}&origin=${encodeURIComponent(window.location.origin + "/")}`);
}
}
function renderMyNetdataMenu(machinesArray) { function renderMyNetdataMenu(machinesArray) {
const el = document.getElementById('my-netdata-dropdown-content'); const el = document.getElementById('my-netdata-dropdown-content');
el.classList.add(`theme-${netdataTheme}`); el.classList.add(`theme-${netdataTheme}`);
@ -766,6 +774,11 @@ function renderMyNetdataMenu(machinesArray) {
if (!isSignedIn()) { if (!isSignedIn()) {
html += ( html += (
`<div class="agent-item"> `<div class="agent-item">
<i class="fas fa-tv"></i>
<a onClick="openAuthenticatedUrl('console/index.html');" target="_blank">Nodes<sup class="beta"> beta</sup></a>
<div></div>
</div>
<div class="agent-item">
<i class="fas fa-cog""></i> <i class="fas fa-cog""></i>
<a href="#" onclick="switchRegistryModalHandler(); return false;">Switch Identity</a> <a href="#" onclick="switchRegistryModalHandler(); return false;">Switch Identity</a>
<div></div> <div></div>
@ -779,6 +792,11 @@ function renderMyNetdataMenu(machinesArray) {
} else { } else {
html += ( html += (
`<div class="agent-item"> `<div class="agent-item">
<i class="fas fa-tv"></i>
<a onclick="openAuthenticatedUrl('console/index.html');" target="_blank">Nodes<sup class="beta"> beta</sup></a>
<div></div>
</div>
<div class="agent-item">
<i class="fas fa-sync"></i> <i class="fas fa-sync"></i>
<a href="#" onclick="showSyncModal(); return false">Synchronize with netdata.cloud</a> <a href="#" onclick="showSyncModal(); return false">Synchronize with netdata.cloud</a>
<div></div> <div></div>
@ -991,7 +1009,7 @@ function notifyForSwitchRegistry() {
} }
} }
var deleteRegistryGuid = null; var deleteRegistryGuid = null;
var deleteRegistryUrl = null; var deleteRegistryUrl = null;
function deleteRegistryModalHandler(guid, name, url) { function deleteRegistryModalHandler(guid, name, url) {
@ -1004,7 +1022,7 @@ function deleteRegistryModalHandler(guid, name, url) {
document.getElementById('deleteRegistryServerName2').innerHTML = name; document.getElementById('deleteRegistryServerName2').innerHTML = name;
document.getElementById('deleteRegistryServerURL').innerHTML = url; document.getElementById('deleteRegistryServerURL').innerHTML = url;
document.getElementById('deleteRegistryResponse').innerHTML = ''; document.getElementById('deleteRegistryResponse').innerHTML = '';
$('#deleteRegistryModal').modal('show'); $('#deleteRegistryModal').modal('show');
} }
@ -1027,7 +1045,7 @@ function notifyForDeleteRegistry() {
deleteRegistryUrl = null; deleteRegistryUrl = null;
$('#deleteRegistryModal').modal('hide'); $('#deleteRegistryModal').modal('hide');
NETDATA.registry.init(); NETDATA.registry.init();
}); });
}); });
} else { } else {
NETDATA.registry.delete(deleteRegistryUrl, function (result) { NETDATA.registry.delete(deleteRegistryUrl, function (result) {
@ -1038,7 +1056,7 @@ function notifyForDeleteRegistry() {
} else { } else {
responseEl.innerHTML = "<b>Sorry, this command was rejected by the registry server!</b>"; responseEl.innerHTML = "<b>Sorry, this command was rejected by the registry server!</b>";
} }
}); });
} }
} }
} }
@ -1119,7 +1137,7 @@ function scrollToId(hash) {
var offset = $('#' + hash).offset(); var offset = $('#' + hash).offset();
if (typeof offset !== 'undefined') { if (typeof offset !== 'undefined') {
//console.log('scrolling to ' + hash + ' at ' + offset.top.toString()); //console.log('scrolling to ' + hash + ' at ' + offset.top.toString());
$('html, body').animate({scrollTop: offset.top - 30}, 0); $('html, body').animate({ scrollTop: offset.top - 30 }, 0);
} }
} }
@ -1171,7 +1189,7 @@ var netdataDashboard = {
} }
if (typeof this.sparklines_registry[key] === 'undefined') { if (typeof this.sparklines_registry[key] === 'undefined') {
this.sparklines_registry[key] = {count: 1}; this.sparklines_registry[key] = { count: 1 };
} else { } else {
this.sparklines_registry[key].count++; this.sparklines_registry[key].count++;
} }
@ -1772,7 +1790,7 @@ function renderPage(menus, data) {
sidebar += '<li class="" style="padding-top:15px;"><a href="https://github.com/netdata/netdata/blob/master/docs/Add-more-charts-to-netdata.md#add-more-charts-to-netdata" target="_blank"><i class="fas fa-plus"></i> add more charts</a></li>'; sidebar += '<li class="" style="padding-top:15px;"><a href="https://github.com/netdata/netdata/blob/master/docs/Add-more-charts-to-netdata.md#add-more-charts-to-netdata" target="_blank"><i class="fas fa-plus"></i> add more charts</a></li>';
sidebar += '<li class=""><a href="https://github.com/netdata/netdata/tree/master/health#Health-monitoring" target="_blank"><i class="fas fa-plus"></i> add more alarms</a></li>'; sidebar += '<li class=""><a href="https://github.com/netdata/netdata/tree/master/health#Health-monitoring" target="_blank"><i class="fas fa-plus"></i> add more alarms</a></li>';
sidebar += '<li class="" style="margin:20px;color:#666;"><small>netdata on <b>' + data.hostname.toString() + '</b>, collects every ' + ((data.update_every === 1) ? 'second' : data.update_every.toString() + ' seconds') + ' <b>' + data.dimensions_count.toLocaleString() + '</b> metrics, presented as <b>' + data.charts_count.toLocaleString() + '</b> charts and monitored by <b>' + data.alarms_count.toLocaleString() + '</b> alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + NETDATA.seconds4human(data.update_every * data.history, {space: '&nbsp;'}) + ' of real-time history.<br/>&nbsp;<br/><b>netdata</b><br/>' + data.version.toString() + '</small></li>'; sidebar += '<li class="" style="margin:20px;color:#666;"><small>netdata on <b>' + data.hostname.toString() + '</b>, collects every ' + ((data.update_every === 1) ? 'second' : data.update_every.toString() + ' seconds') + ' <b>' + data.dimensions_count.toLocaleString() + '</b> metrics, presented as <b>' + data.charts_count.toLocaleString() + '</b> charts and monitored by <b>' + data.alarms_count.toLocaleString() + '</b> alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + NETDATA.seconds4human(data.update_every * data.history, { space: '&nbsp;' }) + ' of real-time history.<br/>&nbsp;<br/><b>netdata</b><br/>' + data.version.toString() + '</small></li>';
sidebar += '</ul>'; sidebar += '</ul>';
div.innerHTML = html; div.innerHTML = html;
document.getElementById('sidebar').innerHTML = sidebar; document.getElementById('sidebar').innerHTML = sidebar;
@ -1886,7 +1904,7 @@ function loadJs(url, callback) {
url: url, url: url,
cache: true, cache: true,
dataType: "script", dataType: "script",
xhrFields: {withCredentials: true} // required for the cookie xhrFields: { withCredentials: true } // required for the cookie
}) })
.fail(function () { .fail(function () {
alert('Cannot load required JS library: ' + url); alert('Cannot load required JS library: ' + url);
@ -1988,8 +2006,8 @@ function alarmsUpdateModal() {
if (data === null) { if (data === null) {
document.getElementById('alarms_active').innerHTML = document.getElementById('alarms_active').innerHTML =
document.getElementById('alarms_all').innerHTML = document.getElementById('alarms_all').innerHTML =
document.getElementById('alarms_log').innerHTML = document.getElementById('alarms_log').innerHTML =
'failed to load alarm data!'; 'failed to load alarm data!';
return; return;
} }
@ -2039,7 +2057,7 @@ function alarmsUpdateModal() {
return '<code>' + alarm.lookup_method + '</code> ' return '<code>' + alarm.lookup_method + '</code> '
+ dimensions + ', of chart <code>' + alarm.chart + '</code>' + dimensions + ', of chart <code>' + alarm.chart + '</code>'
+ ', starting <code>' + NETDATA.seconds4human(alarm.lookup_after + alarm.lookup_before, {space: '&nbsp;'}) + '</code> and up to <code>' + NETDATA.seconds4human(alarm.lookup_before, {space: '&nbsp;'}) + '</code>' + ', starting <code>' + NETDATA.seconds4human(alarm.lookup_after + alarm.lookup_before, { space: '&nbsp;' }) + '</code> and up to <code>' + NETDATA.seconds4human(alarm.lookup_before, { space: '&nbsp;' }) + '</code>'
+ ((alarm.lookup_options) ? (', with options <code>' + alarm.lookup_options.replace(/ /g, ',&nbsp;') + '</code>') : '') + ((alarm.lookup_options) ? (', with options <code>' + alarm.lookup_options.replace(/ /g, ',&nbsp;') + '</code>') : '')
+ '.'; + '.';
} }
@ -2115,9 +2133,9 @@ function alarmsUpdateModal() {
} }
html += '<tr><td width="10%" style="text-align:right">check&nbsp;every</td><td>' + NETDATA.seconds4human(alarm.update_every, { html += '<tr><td width="10%" style="text-align:right">check&nbsp;every</td><td>' + NETDATA.seconds4human(alarm.update_every, {
space: '&nbsp;', space: '&nbsp;',
negative_suffix: '' negative_suffix: ''
}) + '</td></tr>' }) + '</td></tr>'
+ ((has_alarm === true) ? ('<tr><td width="10%" style="text-align:right">execute</td><td><span style="font-family: monospace;">' + alarm.exec + '</span>' + delay + '</td></tr>') : '') + ((has_alarm === true) ? ('<tr><td width="10%" style="text-align:right">execute</td><td><span style="font-family: monospace;">' + alarm.exec + '</span>' + delay + '</td></tr>') : '')
+ '<tr><td width="10%" style="text-align:right">source</td><td><span style="font-family: monospace;">' + alarm.source + '</span></td></tr>' + '<tr><td width="10%" style="text-align:right">source</td><td><span style="font-family: monospace;">' + alarm.source + '</span></td></tr>'
+ '</table></td></tr>'; + '</table></td></tr>';
@ -2162,7 +2180,7 @@ function alarmsUpdateModal() {
// not found - this should never happen! // not found - this should never happen!
if (typeof chart === 'undefined') { if (typeof chart === 'undefined') {
console.log('WARNING: alarm ' + x + ' is linked to chart ' + alarm.chart + ', which is not found in the list of chart got from the server.'); console.log('WARNING: alarm ' + x + ' is linked to chart ' + alarm.chart + ', which is not found in the list of chart got from the server.');
chart = {priority: 9999999}; chart = { priority: 9999999 };
} }
else if (typeof chart.menu !== 'undefined' && typeof chart.submenu !== 'undefined') else if (typeof chart.menu !== 'undefined' && typeof chart.submenu !== 'undefined')
// the family based on the chart // the family based on the chart
@ -2299,16 +2317,16 @@ function alarmsUpdateModal() {
switch (row.status) { switch (row.status) {
case 'CRITICAL': case 'CRITICAL':
return {classes: 'danger'}; return { classes: 'danger' };
break; break;
case 'WARNING': case 'WARNING':
return {classes: 'warning'}; return { classes: 'warning' };
break; break;
case 'UNDEFINED': case 'UNDEFINED':
return {classes: 'info'}; return { classes: 'info' };
break; break;
case 'CLEAR': case 'CLEAR':
return {classes: 'success'}; return { classes: 'success' };
break; break;
} }
return {}; return {};
@ -2496,7 +2514,7 @@ function alarmsUpdateModal() {
formatter: function (value, row, index) { formatter: function (value, row, index) {
void (row); void (row);
void (index); void (index);
return NETDATA.seconds4human(value, {negative_suffix: '', space: ' ', now: 'no time'}); return NETDATA.seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' });
}, },
align: 'center', align: 'center',
valign: 'middle', valign: 'middle',
@ -2510,7 +2528,7 @@ function alarmsUpdateModal() {
formatter: function (value, row, index) { formatter: function (value, row, index) {
void (row); void (row);
void (index); void (index);
return NETDATA.seconds4human(value, {negative_suffix: '', space: ' ', now: 'no time'}); return NETDATA.seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' });
}, },
align: 'center', align: 'center',
valign: 'middle', valign: 'middle',
@ -2642,7 +2660,7 @@ function alarmsUpdateModal() {
void (row); void (row);
void (index); void (index);
return NETDATA.seconds4human(value, {negative_suffix: '', space: ' ', now: 'no time'}); return NETDATA.seconds4human(value, { negative_suffix: '', space: ' ', now: 'no time' });
}, },
align: 'center', align: 'center',
valign: 'middle', valign: 'middle',
@ -2846,32 +2864,32 @@ function versionsMatch(v1, v2) {
if (v1 == v2) { if (v1 == v2) {
return true; return true;
} else { } else {
let s1=v1.split('.'); let s1 = v1.split('.');
let s2=v2.split('.'); let s2 = v2.split('.');
// Check major version // Check major version
let n1 = parseInt(s1[0].substring(1,2),10); let n1 = parseInt(s1[0].substring(1, 2), 10);
let n2 = parseInt(s2[0].substring(1,2), 10); let n2 = parseInt(s2[0].substring(1, 2), 10);
if ( n1 < n2 ) return false; if (n1 < n2) return false;
else if ( n1 > n2 ) return true; else if (n1 > n2) return true;
// Check minor version // Check minor version
n1 = parseInt(s1[1],10); n1 = parseInt(s1[1], 10);
n2 = parseInt(s2[1],10); n2 = parseInt(s2[1], 10);
if ( n1 < n2 ) return false; if (n1 < n2) return false;
else if ( n1 > n2 ) return true; else if (n1 > n2) return true;
// Split patch: format could be e.g. 0-22-nightly // Split patch: format could be e.g. 0-22-nightly
s1=s1[2].split('-'); s1 = s1[2].split('-');
s2=s2[2].split('-'); s2 = s2[2].split('-');
n1 = parseInt(s1[0],10); n1 = parseInt(s1[0], 10);
n2 = parseInt(s2[0],10); n2 = parseInt(s2[0], 10);
if ( n1 < n2 ) return false; if (n1 < n2) return false;
else if ( n1 > n2 ) return true; else if (n1 > n2) return true;
n1 = (s1.length > 1) ? parseInt(s1[1],10) : 0; n1 = (s1.length > 1) ? parseInt(s1[1], 10) : 0;
n2 = (s2.length > 1) ? parseInt(s2[1],10) : 0; n2 = (s2.length > 1) ? parseInt(s2[1], 10) : 0;
if ( n1 < n2 ) return false; if (n1 < n2) return false;
else return true; else return true;
} }
} }
@ -3119,7 +3137,7 @@ var snapshotOptions = {
bytes_per_point_disk: 1.9, bytes_per_point_disk: 1.9,
compress: function (s) { compress: function (s) {
return btoa(pako.deflate(s, {to: 'string'})); return btoa(pako.deflate(s, { to: 'string' }));
}, },
compressed_length: function (s) { compressed_length: function (s) {
@ -3127,7 +3145,7 @@ var snapshotOptions = {
}, },
uncompress: function (s) { uncompress: function (s) {
return pako.inflate(atob(s), {to: 'string'}); return pako.inflate(atob(s), { to: 'string' });
} }
}, },
@ -3136,7 +3154,7 @@ var snapshotOptions = {
bytes_per_point_disk: 3.2, bytes_per_point_disk: 3.2,
compress: function (s) { compress: function (s) {
return pako.deflate(s, {to: 'string'}); return pako.deflate(s, { to: 'string' });
}, },
compressed_length: function (s) { compressed_length: function (s) {
@ -3144,7 +3162,7 @@ var snapshotOptions = {
}, },
uncompress: function (s) { uncompress: function (s) {
return pako.inflate(s, {to: 'string'}); return pako.inflate(s, { to: 'string' });
} }
}, },
@ -3937,7 +3955,7 @@ function enableTooltipsAndPopovers() {
animated: 'fade', animated: 'fade',
trigger: 'hover', trigger: 'hover',
html: true, html: true,
delay: {show: 500, hide: 0}, delay: { show: 500, hide: 0 },
container: 'body' container: 'body'
}); });
$('[data-toggle="popover"]').popover(); $('[data-toggle="popover"]').popover();
@ -4015,7 +4033,7 @@ function runOnceOnDashboardWithjQuery() {
// scroll to the position we had open before the modal // scroll to the position we had open before the modal
$('html, body') $('html, body')
.animate({scrollTop: scrollPos}, 0); .animate({ scrollTop: scrollPos }, 0);
// unpause netdata, if we paused it // unpause netdata, if we paused it
if (netdata_paused_on_modal === true) { if (netdata_paused_on_modal === true) {
@ -4181,8 +4199,8 @@ function runOnceOnDashboardWithjQuery() {
.on('hidden.bs.modal', function () { .on('hidden.bs.modal', function () {
document.getElementById('alarms_active').innerHTML = document.getElementById('alarms_active').innerHTML =
document.getElementById('alarms_all').innerHTML = document.getElementById('alarms_all').innerHTML =
document.getElementById('alarms_log').innerHTML = document.getElementById('alarms_log').innerHTML =
'loading...'; 'loading...';
}); });
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -4232,7 +4250,7 @@ function runOnceOnDashboardWithjQuery() {
if ($this.hasClass('less')) { if ($this.hasClass('less')) {
$this.removeClass('less'); $this.removeClass('less');
$this.html(config.moreText); $this.html(config.moreText);
$this.parent().prev().animate({'height': '0' + '%'}, 0, function () { $this.parent().prev().animate({ 'height': '0' + '%' }, 0, function () {
$this.parent().prev().prev().show(); $this.parent().prev().prev().show();
}).hide(0, function () { }).hide(0, function () {
config.onLess(); config.onLess();
@ -4240,7 +4258,7 @@ function runOnceOnDashboardWithjQuery() {
} else { } else {
$this.addClass('less'); $this.addClass('less');
$this.html(config.lessText); $this.html(config.lessText);
$this.parent().prev().animate({'height': '100' + '%'}, 0, function () { $this.parent().prev().animate({ 'height': '100' + '%' }, 0, function () {
$this.parent().prev().prev().hide(); $this.parent().prev().prev().hide();
}).show(0, function () { }).show(0, function () {
config.onMore(); config.onMore();
@ -4563,7 +4581,7 @@ function getCloudAccountAgents() {
if (!isSignedIn()) { if (!isSignedIn()) {
return []; return [];
} }
return fetch( return fetch(
`${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents`, `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents`,
{ {
@ -4573,7 +4591,7 @@ function getCloudAccountAgents() {
"Authorization": `Bearer ${cloudToken}` "Authorization": `Bearer ${cloudToken}`
} }
} }
).then((response) => { ).then((response) => {
if (!response.ok) { if (!response.ok) {
throw Error("Cannot fetch known accounts"); throw Error("Cannot fetch known accounts");
} }
@ -4599,6 +4617,36 @@ function getCloudAccountAgents() {
}); });
} }
/** Updates the lastAccessTime and accessCount properties of the agent for the account. */
function touchAgent() {
if (!isSignedIn()) {
return [];
}
const touchUrl = `${NETDATA.registry.cloudBaseURL}/api/v1/agents/${NETDATA.registry.machine_guid}/touch?account_id=${cloudAccountID}`;
return fetch(
touchUrl,
{
method: "post",
body: "",
mode: "cors",
headers: {
"Authorization": `Bearer ${cloudToken}`
}
}
).then((response) => {
if (!response.ok) {
throw Error("Cannot touch agent" + JSON.stringify(response));
}
return response.json();
}).then((payload) => {
}).catch(function (error) {
console.log(error);
return null;
});
}
// https://github.com/netdata/hub/issues/128 // https://github.com/netdata/hub/issues/128
function postCloudAccountAgents(agentsToSync) { function postCloudAccountAgents(agentsToSync) {
if (!isSignedIn()) { if (!isSignedIn()) {
@ -4622,7 +4670,7 @@ function postCloudAccountAgents(agentsToSync) {
"agents": agents, "agents": agents,
"merge": false, "merge": false,
}; };
return fetch( return fetch(
`${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents`, `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents`,
{ {
@ -4650,7 +4698,7 @@ function postCloudAccountAgents(agentsToSync) {
"url": a.urls[0], "url": a.urls[0],
"alternate_urls": a.urls "alternate_urls": a.urls
} }
}) })
}); });
} }
@ -4727,7 +4775,7 @@ function updateMyNetdataAfterFilterChange() {
if (options.hosts.length > 1) { if (options.hosts.length > 1) {
const streamedEl = document.getElementById("my-netdata-menu-streamed") const streamedEl = document.getElementById("my-netdata-menu-streamed")
streamedEl.innerHTML = renderStreamedHosts(options); streamedEl.innerHTML = renderStreamedHosts(options);
} }
} }
@ -4742,7 +4790,7 @@ function myNetdataFilterDidChange(e) {
const inputEl = e.target; const inputEl = e.target;
setTimeout(() => { setTimeout(() => {
myNetdataMenuFilterValue = inputEl.value; myNetdataMenuFilterValue = inputEl.value;
updateMyNetdataAfterFilterChange(); updateMyNetdataAfterFilterChange();
}, 1); }, 1);
} }
@ -4753,9 +4801,9 @@ function myNetdataFilterClearDidClick(e) {
const inputEl = document.getElementById("my-netdata-menu-filter-input"); const inputEl = document.getElementById("my-netdata-menu-filter-input");
inputEl.value = ""; inputEl.value = "";
myNetdataMenuFilterValue = ""; myNetdataMenuFilterValue = "";
updateMyNetdataAfterFilterChange(); updateMyNetdataAfterFilterChange();
inputEl.focus(); inputEl.focus();
} }
@ -4792,12 +4840,18 @@ function renderAccountUI() {
container.removeAttribute("title"); container.removeAttribute("title");
container.removeAttribute("data-original-title"); container.removeAttribute("data-original-title");
container.removeAttribute("data-placement"); container.removeAttribute("data-placement");
// <a href="/console/index.html#/charts/${NETDATA.registry.machine_guid}" target="_blank" class="btn">
container.innerHTML = ( container.innerHTML = (
`<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span id="amc-account-name"></span> <strong class="caret"></strong></a> `<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span id="amc-account-name"></span> <strong class="caret"></strong></a>
<ul id="cloud-menu" class="dropdown-menu scrollable-menu inpagemenu" role="menu"> <ul id="cloud-menu" class="dropdown-menu scrollable-menu inpagemenu" role="menu">
<li> <li>
<a href="#" class="btn" onclick="signOutDidClick(event); return false"> <a onclick="openAuthenticatedUrl('console/index.html');" target="_blank" class="btn">
<i class="fas fa-sign-out-alt"></i>&nbsp;&nbsp;<span class="hidden-sm hidden-md">Sign Out</span> <i class="fas fa-tv"></i>&nbsp;&nbsp;<span class="hidden-sm hidden-md">Nodes<sup class="beta"> beta</sup></span>
</a>
</li>
<li>
<a href="#" class="btn" onclick="signOutDidClick(event); return false">
<i class="fas fa-sign-out-alt"></i>&nbsp;&nbsp;<span class="hidden-sm hidden-md">Sign Out</span>
</a> </a>
</li> </li>
</ul>` </ul>`
@ -4841,7 +4895,7 @@ function handleSignInMessage(e) {
} }
function handleSignOutMessage(e) { function handleSignOutMessage(e) {
clearCloudVariables(); clearCloudVariables();
renderAccountUI(); renderAccountUI();
renderMyNetdataMenu(registryAgents); renderMyNetdataMenu(registryAgents);
} }
@ -4915,17 +4969,17 @@ function explicitlySyncAgents() {
$("#syncRegistryModal").modal("hide"); $("#syncRegistryModal").modal("hide");
const json = localStorage.getItem("cloud.sync"); const json = localStorage.getItem("cloud.sync");
const sync = json ? JSON.parse(json): {}; const sync = json ? JSON.parse(json) : {};
delete sync[cloudAccountID]; delete sync[cloudAccountID];
localStorage.setItem("cloud.sync", JSON.stringify(sync)); localStorage.setItem("cloud.sync", JSON.stringify(sync));
NETDATA.registry.init(); NETDATA.registry.init();
} }
function syncAgents(callback) { function syncAgents(callback) {
const json = localStorage.getItem("cloud.sync"); const json = localStorage.getItem("cloud.sync");
const sync = json ? JSON.parse(json): {}; const sync = json ? JSON.parse(json) : {};
const currentAgent = { const currentAgent = {
guid: NETDATA.registry.machine_guid, guid: NETDATA.registry.machine_guid,
name: NETDATA.registry.hostname, name: NETDATA.registry.hostname,
@ -4933,30 +4987,30 @@ function syncAgents(callback) {
alternate_urls: [NETDATA.serverDefault], alternate_urls: [NETDATA.serverDefault],
} }
const localAgents = sync[cloudAccountID] const localAgents = sync[cloudAccountID]
? [currentAgent] ? [currentAgent]
: registryAgents.concat([currentAgent]); : registryAgents.concat([currentAgent]);
console.log("Checking if sync is needed.", localAgents); console.log("Checking if sync is needed.", localAgents);
const agentsToSync = mergeAgents(cloudAgents, localAgents); const agentsToSync = mergeAgents(cloudAgents, localAgents);
if ((!sync[cloudAccountID]) || agentsToSync) { if ((!sync[cloudAccountID]) || agentsToSync) {
sync[cloudAccountID] = new Date().getTime(); sync[cloudAccountID] = new Date().getTime();
localStorage.setItem("cloud.sync", JSON.stringify(sync)); localStorage.setItem("cloud.sync", JSON.stringify(sync));
} }
if (agentsToSync) { if (agentsToSync) {
console.log("Synchronizing with netdata.cloud."); console.log("Synchronizing with netdata.cloud.");
postCloudAccountAgents(agentsToSync).then((agents) => { postCloudAccountAgents(agentsToSync).then((agents) => {
// TODO: clear syncTime on error! // TODO: clear syncTime on error!
cloudAgents = agents; cloudAgents = agents;
callback(cloudAgents); callback(cloudAgents);
}); });
return return
} }
callback(cloudAgents); callback(cloudAgents);
} }
@ -4995,6 +5049,7 @@ function initCloud() {
cloudSSOInit(); cloudSSOInit();
} }
touchAgent();
renderAccountUI(); renderAccountUI();
} }
@ -5004,7 +5059,7 @@ function netdataRegistryCallback(machinesArray) {
initCloud(); initCloud();
registryAgents = machinesArray; registryAgents = machinesArray;
if (isSignedIn()) { if (isSignedIn()) {
// We call getCloudAccountAgents() here because it requires that // We call getCloudAccountAgents() here because it requires that
@ -5015,17 +5070,17 @@ function netdataRegistryCallback(machinesArray) {
errorMyNetdataMenu(); errorMyNetdataMenu();
return; return;
} }
cloudAgents = agents; cloudAgents = agents;
syncAgents((agents) => { syncAgents((agents) => {
const agentsMap = {} const agentsMap = {}
for (const agent of agents) { for (const agent of agents) {
agentsMap[agent.guid] = agent; agentsMap[agent.guid] = agent;
} }
NETDATA.registry.machines = agentsMap; NETDATA.registry.machines = agentsMap;
NETDATA.registry.machines_array = agents; NETDATA.registry.machines_array = agents;
renderMyNetdataMenu(agents); renderMyNetdataMenu(agents);
}); });
}); });
} else { } else {
@ -5044,15 +5099,15 @@ function tryFastInitCloud() {
NETDATA.registry.cloudBaseURL = baseURL; NETDATA.registry.cloudBaseURL = baseURL;
NETDATA.registry.machine_guid = agentID; NETDATA.registry.machine_guid = agentID;
NETDATA.registry.isCloudEnabled = true; NETDATA.registry.isCloudEnabled = true;
initCloud(); initCloud();
} }
} }
function initializeApp() { function initializeApp() {
window.addEventListener("message", handleMessage, false); window.addEventListener("message", handleMessage, false);
// tryFastInitCloud(); // tryFastInitCloud();
} }
if (document.readyState === "complete") { if (document.readyState === "complete") {

View File

@ -0,0 +1,3 @@
<svg width="1723" height="1723" viewBox="0 0 1723 1723" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.628784 849.678C0.628784 473.909 235.042 153.621 563.766 30.7914C701.438 19.0613 843.892 50.2449 970.557 129.297C1052.47 180.42 1119.71 246.528 1170.96 321.982C1161.21 207.568 1122.97 96.4678 1058.94 0.187012C1220.56 38.587 1364.64 123.126 1476.91 239.343C1518.34 297.634 1548.55 365.545 1563.67 440.489C1578.54 514.244 1577.35 587.545 1562.5 656.661C1601.04 613.105 1632.22 563.24 1654.63 509.251C1698.41 613.852 1722.63 728.899 1722.63 849.678C1722.63 1331.55 1337.15 1722.19 861.629 1722.19C386.112 1722.19 0.628784 1331.55 0.628784 849.678ZM1178.87 1369.04C1286.71 1369.04 1374.13 1280.45 1374.13 1171.17C1374.13 1061.88 1286.71 973.293 1178.87 973.293C1071.03 973.293 983.603 1061.88 983.603 1171.17C983.603 1280.45 1071.03 1369.04 1178.87 1369.04Z" fill="#00C853"/>
</svg>

After

Width:  |  Height:  |  Size: 934 B

View File

@ -0,0 +1,3 @@
<svg width="1723" height="1723" viewBox="0 0 1723 1723" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.628784 849.678C0.628784 473.909 235.042 153.621 563.766 30.7914C701.438 19.0613 843.892 50.2449 970.557 129.297C1052.47 180.42 1119.71 246.528 1170.96 321.982C1161.21 207.568 1122.97 96.4678 1058.94 0.187012C1220.56 38.587 1364.64 123.126 1476.91 239.343C1518.34 297.634 1548.55 365.545 1563.67 440.489C1578.54 514.244 1577.35 587.545 1562.5 656.661C1601.04 613.105 1632.22 563.24 1654.63 509.251C1698.41 613.852 1722.63 728.899 1722.63 849.678C1722.63 1331.55 1337.15 1722.19 861.629 1722.19C386.112 1722.19 0.628784 1331.55 0.628784 849.678ZM1178.87 1369.04C1286.71 1369.04 1374.13 1280.45 1374.13 1171.17C1374.13 1061.88 1286.71 973.293 1178.87 973.293C1071.03 973.293 983.603 1061.88 983.603 1171.17C983.603 1280.45 1071.03 1369.04 1178.87 1369.04Z" fill="#00C853"/>
</svg>

After

Width:  |  Height:  |  Size: 934 B