From 8a7b72f7cd1ccd547a03eb4243294e741d661d3f Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 8 Feb 2019 08:30:37 +0100 Subject: Adding upstream version 1.12.0. Signed-off-by: Daniel Baumann --- web/gui/main.js | 955 ++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 788 insertions(+), 167 deletions(-) (limited to 'web/gui/main.js') diff --git a/web/gui/main.js b/web/gui/main.js index a04f406bd..b6478f6cf 100644 --- a/web/gui/main.js +++ b/web/gui/main.js @@ -1,5 +1,8 @@ // Main JavaScript file for the Netdata GUI. +// Codacy declarations +/* global NETDATA */ + // netdata snapshot data var netdataSnapshotData = null; @@ -453,7 +456,7 @@ function saveObjectToClient(data, filename) { saveTextToClient(JSON.stringify(data), filename); } -// -------------------------------------------------------------------- +// ----------------------------------------------------------------------------- // registry call back to render my-netdata menu function toggleExpandIcon(svgEl) { @@ -476,21 +479,8 @@ function toggleAgentItem(e, guid) { } } -// TODO: consider renaming to `truncateString` - -/// Enforces a maximum string length while retaining the prefix and the postfix of -/// the string. -function clipString(str, maxLength) { - if (str.length <= maxLength) { - return str; - } - - const spanLength = Math.floor((maxLength - 3) / 2); - return `${str.substring(0, spanLength)}...${str.substring(str.length - spanLength)}`; -} - -// When you stream metrics from netdata to netdata, the recieving netdata now -// has multiple host databases. It's own, and multiple mirrored. Mirrored databases +// When you stream metrics from netdata to netdata, the recieving netdata now +// has multiple host databases. It's own, and multiple mirrored. Mirrored databases // can be accessed with function renderStreamedHosts(options) { let html = `
Databases streamed to this agent
`; @@ -512,12 +502,22 @@ function renderStreamedHosts(options) { return naturalSortCompare(a.hostname, b.hostname); }); - for (const s of sorted) { + let displayedDatabases = false; + + for (var s of sorted) { let url, icon; const hostname = s.hostname; + if (myNetdataMenuFilterValue !== "") { + if (!hostname.includes(myNetdataMenuFilterValue)) { + continue; + } + } + + displayedDatabases = true; + if (hostname === master) { - url = `base${'/'}`; + url = `${base}/`; icon = 'home'; } else { url = `${base}/host/${hostname}/`; @@ -528,18 +528,33 @@ function renderStreamedHosts(options) { `` ) } + if (!displayedDatabases) { + html += ( + `
+ + no databases match the filter criteria. +
` + ) + } + return html; } function renderMachines(machinesArray) { - let html = `
My netdata agents
`; + // let html = isSignedIn() + // ? `
My nodes
` + // : `
My nodes
`; + + let html = `
My nodes
`; if (machinesArray === null) { let ret = loadLocalStorage("registryCallback"); @@ -550,6 +565,9 @@ function renderMachines(machinesArray) { } let found = false; + let displayedAgents = false; + + const maskedURL = NETDATA.registry.MASKED_DATA; if (machinesArray) { saveLocalStorage("registryCallback", JSON.stringify(machinesArray)); @@ -558,21 +576,34 @@ function renderMachines(machinesArray) { return naturalSortCompare(a.name, b.name); }); - for (const machine of machines) { + for (var machine of machines) { found = true; + if (myNetdataMenuFilterValue !== "") { + if (!machine.name.includes(myNetdataMenuFilterValue)) { + continue; + } + } + + displayedAgents = true; + const alternateUrlItems = ( `` @@ -581,8 +612,8 @@ function renderMachines(machinesArray) { html += ( `
- - ${machine.name} + + ${machine.name} @@ -591,25 +622,34 @@ function renderMachines(machinesArray) { ${alternateUrlItems}` ) } + + if (found && (!displayedAgents)) { + html += ( + `
+ + zero nodes are matching the filter value. +
` + ) + } } if (!found) { if (machines) { html += ( `
` ) } else { html += ( `` ) } html += `
`; - html += `
Demo netdata agents
`; + html += `
Demo netdata nodes
`; const demoServers = [ {url: "//london.netdata.rocks/default.html", title: "UK - London (DigitalOcean.com)"}, @@ -623,10 +663,10 @@ function renderMachines(machinesArray) { ] - for (const server of demoServers) { + for (var server of demoServers) { html += ( ` @@ -638,36 +678,111 @@ function renderMachines(machinesArray) { return html; } -// Populates the my-netdata menu. -function netdataRegistryCallback(machinesArray) { +function setMyNetdataMenu(html) { + const el = document.getElementById('my-netdata-dropdown-content') + el.innerHTML = html; +} + +function clearMyNetdataMenu() { + setMyNetdataMenu(`
+ + Loading, please wait... +
+
`); +} + +function errorMyNetdataMenu() { + setMyNetdataMenu(`
+ + Cannot load known netdata agents from netdata.cloud! +
+
`); +} + +function restrictMyNetdataMenu() { + setMyNetdataMenu(`
+ Please sign in to netdata.cloud to view your nodes! +
+
`); +} + +function renderMyNetdataMenu(machinesArray) { + const el = document.getElementById('my-netdata-dropdown-content'); + el.classList.add(`theme-${netdataTheme}`); + + if (!isSignedIn()) { + if (!NETDATA.registry.isRegistryEnabled()) { + restrictMyNetdataMenu(); + return; + } + } + + if (machinesArray == registryAgents) { + console.log("Rendering my-netdata menu from registry"); + } else { + console.log("Rendering my-netdata menu from netdata.cloud", machinesArray); + } + let html = ''; + if (isSignedIn()) { + html += ( + `
+ + +
+
` + ); + } + if (options.hosts.length > 1) { - html += renderStreamedHosts(options) + `
`; + html += `
${renderStreamedHosts(options)}

`; } - html += renderMachines(machinesArray); - - html += ( - `
-
- - Switch Identity -
-
-
- - What is this? -
-
` - ) + html += `
${renderMachines(machinesArray)}
`; + + if (!isSignedIn()) { + html += ( + `
+
+ + Switch Identity +
+
+
+ + What is this? +
+
` + ) + } else { + html += ( + `
+ +
+ + What is this? +
+
` + ) + } - const el = document.getElementById('my-netdata-dropdown-content') - el.classList.add(`theme-${netdataTheme}`); el.innerHTML = html; gotoServerInit(); -}; +} function isdemo() { if (this_is_demo !== null) { @@ -762,6 +877,7 @@ function gotoServerValidateUrl(id, guid, url) { } else { document.getElementById('gotoServerResponse').innerHTML += 'found it! It is at:
' + escapeUserInputHTML(url) + ''; document.location = verifyURL(finalURL); + $('#gotoServerModal').modal('hide'); } } } else { @@ -800,24 +916,29 @@ function gotoServerModalHandler(guid) { gotoServerValidateUrl(count++, guid, url); } - setTimeout(function () { - if (gotoServerStop === false) { - document.getElementById('gotoServerResponse').innerHTML = 'Added all the known URLs for this machine.'; - NETDATA.registry.search(guid, function (data) { - // console.log(data); - len = data.urls.length; - while (len--) { - var url = data.urls[len][1]; - // console.log(url); - if (typeof checked[url] === 'undefined') { - gotoServerValidateRemaining++; - checked[url] = true; - gotoServerValidateUrl(count++, guid, url); + if (!isSignedIn()) { + // When the registry is enabled, if the user's known URLs are not working + // we consult the registry to get additional URLs. + setTimeout(function () { + if (gotoServerStop === false) { + document.getElementById('gotoServerResponse').innerHTML = 'Added all the known URLs for this machine.'; + NETDATA.registry.search(guid, function (data) { + // console.log(data); + len = data.urls.length; + while (len--) { + var url = data.urls[len][1]; + // console.log(url); + if (typeof checked[url] === 'undefined') { + gotoServerValidateRemaining++; + checked[url] = true; + gotoServerValidateUrl(count++, guid, url); + } } - } - }); - } - }, 2000); + }); + } + }, 2000); + } + return false; } @@ -858,30 +979,55 @@ function notifyForSwitchRegistry() { } } +var deleteRegistryGuid = null; var deleteRegistryUrl = null; function deleteRegistryModalHandler(guid, name, url) { - void (guid); + // void (guid); + deleteRegistryGuid = guid; deleteRegistryUrl = url; + document.getElementById('deleteRegistryServerName').innerHTML = name; document.getElementById('deleteRegistryServerName2').innerHTML = name; document.getElementById('deleteRegistryServerURL').innerHTML = url; document.getElementById('deleteRegistryResponse').innerHTML = ''; + $('#deleteRegistryModal').modal('show'); } function notifyForDeleteRegistry() { + const responseEl = document.getElementById('deleteRegistryResponse'); + if (deleteRegistryUrl) { - NETDATA.registry.delete(deleteRegistryUrl, function (result) { - if (result !== null) { - deleteRegistryUrl = null; - $('#deleteRegistryModal').modal('hide'); - NETDATA.registry.init(); - } else { - document.getElementById('deleteRegistryResponse').innerHTML = "Sorry! this command was rejected by the registry server."; - } - }); + if (isSignedIn()) { + deleteCloudAgentURL(deleteRegistryGuid, deleteRegistryUrl) + .then((count) => { + if (!count) { + responseEl.innerHTML = "Sorry, this command was rejected by netdata.cloud!"; + return; + } + NETDATA.registry.delete(deleteRegistryUrl, function (result) { + if (result === null) { + console.log("Received error from registry", result); + } + + deleteRegistryUrl = null; + $('#deleteRegistryModal').modal('hide'); + NETDATA.registry.init(); + }); + }); + } else { + NETDATA.registry.delete(deleteRegistryUrl, function (result) { + if (result !== null) { + deleteRegistryUrl = null; + $('#deleteRegistryModal').modal('hide'); + NETDATA.registry.init(); + } else { + responseEl.innerHTML = "Sorry, this command was rejected by the registry server!"; + } + }); + } } } @@ -1165,6 +1311,7 @@ function enrichChartData(chart) { case 'ap': case 'net': case 'disk': + case 'powersupply': case 'statsd': chart.menu = tmp; break; @@ -1610,9 +1757,9 @@ function renderPage(menus, data) { html += mhead + shtml + '

'; } - sidebar += '
  • add more charts
  • '; + sidebar += '
  • add more charts
  • '; sidebar += '
  • add more alarms
  • '; - sidebar += '
  • netdata on ' + data.hostname.toString() + ', collects every ' + ((data.update_every === 1) ? 'second' : data.update_every.toString() + ' seconds') + ' ' + data.dimensions_count.toLocaleString() + ' metrics, presented as ' + data.charts_count.toLocaleString() + ' charts and monitored by ' + data.alarms_count.toLocaleString() + ' alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + NETDATA.seconds4human(data.update_every * data.history, {space: ' '}) + ' of real-time history.
     
    netdata
    v' + data.version.toString() + '
  • '; + sidebar += '
  • netdata on ' + data.hostname.toString() + ', collects every ' + ((data.update_every === 1) ? 'second' : data.update_every.toString() + ' seconds') + ' ' + data.dimensions_count.toLocaleString() + ' metrics, presented as ' + data.charts_count.toLocaleString() + ' charts and monitored by ' + data.alarms_count.toLocaleString() + ' alarms, using ' + Math.round(data.rrd_memory_bytes / 1024 / 1024).toLocaleString() + ' MB of memory for ' + NETDATA.seconds4human(data.update_every * data.history, {space: ' '}) + ' of real-time history.
     
    netdata
    ' + data.version.toString() + '
  • '; sidebar += ''; div.innerHTML = html; document.getElementById('sidebar').innerHTML = sidebar; @@ -1696,7 +1843,7 @@ function renderChartsAndMenu(data) { // propagate the descriptive subname given to QoS // to all the other submenus with the same name - for (m in menus) { + for (var m in menus) { if (!menus.hasOwnProperty(m)) { continue; } @@ -2560,7 +2707,7 @@ function initializeDynamicDashboardWithData(data) { } // update the dashboard hostname - document.getElementById('hostname').innerHTML = options.hostname + ((netdataSnapshotData !== null) ? ' (snap)' : '').toString(); + document.getElementById('hostname').innerHTML = options.hostname + ((netdataSnapshotData !== null) ? ' (snap)' : '').toString() + '  '; document.getElementById('hostname').href = NETDATA.serverDefault; document.getElementById('netdataVersion').innerHTML = options.version; @@ -2676,89 +2823,43 @@ function versionLog(msg) { document.getElementById('versionCheckLog').innerHTML = msg; } -function getNetdataCommitIdFromVersion() { - var s = options.version.split('-'); +// New way of checking for updates, based only on versions - if (s.length !== 3) { - return null; - } - if (s[2][0] === 'g') { - var v = s[2].split('_')[0].substring(1, 8); - if (v.length === 7) { - versionLog('Installed git commit id of netdata is ' + v); - document.getElementById('netdataCommitId').innerHTML = v; - return v; - } +function versionsMatch(v1, v2) { + if (v1 == v2) { + return true; + } else { + var s1=v1.split('-'); + var s2=v2.split('-'); + if (s1.length !== s2.length) return false; + if (s1.length === 4) s1.pop(); + if (s2.length === 4) s2.pop(); + return (s1.join('-') === s2.join('-')); } - return null; } -function getNetdataCommitId(force, callback) { - versionLog('Downloading installed git commit id from netdata...'); +function getGithubLatestVersion(callback) { + versionLog('Downloading latest version id from github...'); $.ajax({ - url: 'version.txt', - async: true, - cache: false, - xhrFields: {withCredentials: true} // required for the cookie - }) - .done(function (data) { - data = data.replace(/(\r\n|\n|\r| |\t)/gm, ""); - - var c = getNetdataCommitIdFromVersion(); - if (c !== null && data.length === 40 && data.substring(0, 7) !== c) { - versionLog('Installed files commit id and internal netdata git commit id do not match'); - data = c; - } - - if (data.length >= 7) { - versionLog('Installed git commit id of netdata is ' + data); - document.getElementById('netdataCommitId').innerHTML = data.substring(0, 7); - callback(data); - } - }) - .fail(function () { - versionLog('Failed to download installed git commit id from netdata!'); - - if (force === true) { - var c = getNetdataCommitIdFromVersion(); - if (c === null) { - versionLog('Cannot find the git commit id of netdata.'); - } - callback(c); - } else { - callback(null); - } - }); -} - -function getGithubLatestCommit(callback) { - versionLog('Downloading latest git commit id info from github...'); - - $.ajax({ - url: 'https://api.github.com/repos/netdata/netdata/commits', + url: 'https://api.github.com/repositories/10744183/contents/packaging/version?ref=master', async: true, cache: false }) .done(function (data) { - versionLog('Latest git commit id from github is ' + data[0].sha); - callback(data[0].sha); + data = atob(data.content.replace(/(\r\n|\n|\r| |\t)/gm, "")); + versionLog('Latest version from github is ' + data); + callback(data); }) .fail(function () { - versionLog('Failed to download installed git commit id from github!'); + versionLog('Failed to download the latest version id from github!'); callback(null); }); } -function checkForUpdate(force, callback) { - getNetdataCommitId(force, function (sha1) { - if (sha1 === null) { - callback(null, null); - } - - getGithubLatestCommit(function (sha2) { - callback(sha1, sha2); - }); +function checkForUpdateByVersion(force, callback) { + getGithubLatestVersion(function (sha2) { + callback(options.version, sha2); }); return null; @@ -2784,23 +2885,22 @@ function notifyForUpdate(force) { } } - checkForUpdate(force, function (sha1, sha2) { + checkForUpdateByVersion(force, function (sha1, sha2) { var save = false; if (sha1 === null) { save = false; - versionLog('

    Failed to get your netdata git commit id!

    You can always get the latest netdata from its github page.

    '); + versionLog('

    Failed to get your netdata version!

    You can always get the latest netdata from its github page.

    '); } else if (sha2 === null) { save = false; - versionLog('

    Failed to get the latest git commit id from github.

    You can always get the latest netdata from its github page.

    '); - } else if (sha1 === sha2) { + versionLog('

    Failed to get the latest netdata version github.

    You can always get the latest netdata from its github page.

    '); + } else if (versionsMatch(sha1, sha2)) { save = true; versionLog('

    You already have the latest netdata!

    No update yet?
    Probably, we need some motivation to keep going on!

    If you haven\'t already, give netdata a at its github page.

    '); } else { save = true; - var compare = 'https://github.com/netdata/netdata/compare/' + sha1.toString() + '...' + sha2.toString(); - - versionLog('

    New version of netdata available!

    Latest commit: ' + sha2.substring(0, 7).toString() + '

    Click here for the changes log since your installed version, and
    click here for directions on updating your netdata installation.

    We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.
    Keeping your netdata updated, is generally a good idea.

    '); + var compare = 'https://docs.netdata.cloud/changelog/'; + versionLog('

    New version of netdata available!

    Latest version: ' + sha2 + '

    Click here for the changes log and
    click here for directions on updating your netdata installation.

    We suggest to review the changes log for new features you may be interested, or important bug fixes you may need.
    Keeping your netdata updated is generally a good idea.

    '); document.getElementById('update_badge').innerHTML = '!'; } @@ -3968,6 +4068,7 @@ function runOnceOnDashboardWithjQuery() { }) .on('shown.bs.dropdown', function () { Ps.update(document.getElementById('my-netdata-dropdown-content')); + myNetdataMenuDidShow(); }) .on('hidden.bs.dropdown', function () { NETDATA.unpause(); @@ -4106,7 +4207,7 @@ function runOnceOnDashboardWithjQuery() { } } } - + if (inTag && content[i] === '>') { inTag = false; } @@ -4209,7 +4310,7 @@ function finalizePage() { })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga'); ga('create', 'UA-64295674-3', 'auto'); - ga('send', 'pageview'); + ga('send', 'pageview', '/demosite/' + window.location.host); }, 2000); } else { notifyForUpdate(); @@ -4341,3 +4442,523 @@ var selected_server_timezone = function (timezone, status) { // var netdataStarted = performance.now(); var netdataCallback = initializeDynamicDashboard; + +// ================================================================================================= +// netdata.cloud + +let registryAgents = []; + +let cloudAgents = []; + +let myNetdataMenuFilterValue = ""; + +let cloudAccountID = null; + +let cloudAccountName = null; + +let cloudToken = null; + +/// Enforces a maximum string length while retaining the prefix and the postfix of +/// the string. +function truncateString(str, maxLength) { + if (str.length <= maxLength) { + return str; + } + + const spanLength = Math.floor((maxLength - 3) / 2); + return `${str.substring(0, spanLength)}...${str.substring(str.length - spanLength)}`; +} + +// ------------------------------------------------------------------------------------------------- +// netdata.cloud API Client +// ------------------------------------------------------------------------------------------------- + +function isValidAgent(a) { + return a.urls != null && a.urls.length > 0; +} + +// https://github.com/netdata/hub/issues/146 +function getCloudAccountAgents() { + if (!isSignedIn()) { + return []; + } + + return fetch( + `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents`, + { + method: "GET", + mode: "cors", + headers: { + "Authorization": `Bearer ${cloudToken}` + } + } + ).then((response) => { + if (!response.ok) { + throw Error("Cannot fetch known accounts"); + } + return response.json(); + }).then((payload) => { + const agents = payload.result ? payload.result.agents : null; + + if (!agents) { + return []; + } + + return agents.filter((a) => isValidAgent(a)).map((a) => { + return { + "guid": a.id, + "name": a.name, + "url": a.urls[0], + "alternate_urls": a.urls + } + }) + }).catch(function (error) { + console.log(error); + return null; + }); +} + +// https://github.com/netdata/hub/issues/128 +function postCloudAccountAgents(agentsToSync) { + if (!isSignedIn()) { + return []; + } + + const maskedURL = NETDATA.registry.MASKED_DATA; + + const agents = agentsToSync.map((a) => { + const urls = a.alternate_urls.filter((url) => url != maskedURL); + + return { + "id": a.guid, + "name": a.name, + "urls": urls + } + }).filter((a) => isValidAgent(a)) + + const payload = { + "accountID": cloudAccountID, + "agents": agents, + "merge": false, + }; + + return fetch( + `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents`, + { + method: "POST", + mode: "cors", + headers: { + "Content-Type": "application/json; charset=utf-8", + "Authorization": `Bearer ${cloudToken}` + }, + body: JSON.stringify(payload) + } + ).then((response) => { + return response.json(); + }).then((payload) => { + const agents = payload.result ? payload.result.agents : null; + + if (!agents) { + return []; + } + + return agents.filter((a) => isValidAgent(a)).map((a) => { + return { + "guid": a.id, + "name": a.name, + "url": a.urls[0], + "alternate_urls": a.urls + } + }) + }); +} + +function deleteCloudAgentURL(agentID, url) { + if (!isSignedIn()) { + return []; + } + + return fetch( + `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents/${agentID}/url?value=${encodeURIComponent(url)}`, + { + method: "DELETE", + mode: "cors", + headers: { + "Content-Type": "application/json; charset=utf-8", + "Authorization": `Bearer ${cloudToken}` + }, + } + ).then((response) => { + return response.json(); + }).then((payload) => { + const count = payload.result ? payload.result.count : 0; + return count; + }); +} + +// ------------------------------------------------------------------------------------------------- + +function signInDidClick(e) { + e.preventDefault(); + e.stopPropagation(); + + if (!NETDATA.registry.isUsingGlobalRegistry()) { + // If user is using a private registry, request his consent for + // synchronizing with cloud. + showSignInModal(); + return; + } + + signIn(); +} + +function signOutDidClick(e) { + e.preventDefault(); + e.stopPropagation(); + signOut(); +} + +// ------------------------------------------------------------------------------------------------- + +function updateMyNetdataAfterFilterChange() { + const machinesEl = document.getElementById("my-netdata-menu-machines") + machinesEl.innerHTML = renderMachines(cloudAgents); + + if (options.hosts.length > 1) { + const streamedEl = document.getElementById("my-netdata-menu-streamed") + streamedEl.innerHTML = renderStreamedHosts(options); + } +} + +function myNetdataMenuDidShow() { + const filterEl = document.getElementById("my-netdata-menu-filter-input"); + if (filterEl) { + filterEl.focus(); + } +} + +function myNetdataFilterDidChange(e) { + const inputEl = e.target; + setTimeout(() => { + myNetdataMenuFilterValue = inputEl.value; + updateMyNetdataAfterFilterChange(); + }, 1); +} + +function myNetdataFilterClearDidClick(e) { + e.preventDefault(); + e.stopPropagation(); + + const inputEl = document.getElementById("my-netdata-menu-filter-input"); + inputEl.value = ""; + myNetdataMenuFilterValue = ""; + + updateMyNetdataAfterFilterChange(); + + inputEl.focus(); +} + +// ------------------------------------------------------------------------------------------------- + +function clearCloudVariables() { + cloudAccountID = null; + cloudAccountName = null; + cloudToken = null; +} + +function clearCloudLocalStorageItems() { + localStorage.removeItem("cloud.baseURL"); + localStorage.removeItem("cloud.agentID"); + localStorage.removeItem("cloud.sync"); +} + +function signIn() { + const url = `${NETDATA.registry.cloudBaseURL}/account/sign-in-agent?origin=${encodeURIComponent(window.location.origin + "/")}`; + window.open(url); +} + +function signOut() { + cloudSSOSignOut(); +} + +function renderAccountUI() { + if (!NETDATA.registry.isCloudEnabled) { + return + } + + const container = document.getElementById("account-menu-container"); + if (isSignedIn()) { + container.removeAttribute("title"); + container.removeAttribute("data-original-title"); + container.removeAttribute("data-placement"); + container.innerHTML = ( + ` + ` + ) + document.getElementById("amc-account-name").textContent = cloudAccountName; // Anti-XSS + } else { + container.setAttribute("data-original-title", "sign in"); + container.setAttribute("data-placement", "bottom"); + container.innerHTML = ( + ` +   + ` + ) + } +} + +function handleMessage(e) { + switch (e.data.type) { + case "sign-in": + handleSignInMessage(e); + break; + + case "sign-out": + handleSignOutMessage(e); + break; + + default: + return; + } +} + +function handleSignInMessage(e) { + localStorage.setItem("cloud.baseURL", NETDATA.registry.cloudBaseURL); + + cloudAccountID = e.data.accountID; + cloudAccountName = e.data.accountName; + cloudToken = e.data.token; + + netdataRegistryCallback(registryAgents); +} + +function handleSignOutMessage(e) { + clearCloudVariables(); + renderAccountUI(); + renderMyNetdataMenu(registryAgents); +} + +function isSignedIn() { + return cloudToken != null && cloudAccountID != null; +} + +function sortedArraysEqual(a, b) { + if (a.length != b.length) return false; + + for (var i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + + return true; +} + +// If merging is needed returns the merged agents set, otherwise returns null. +function mergeAgents(cloud, local) { + let dirty = false; + + const union = new Map(); + + for (const cagent of cloud) { + union.set(cagent.guid, cagent); + } + + for (const lagent of local) { + const cagent = union.get(lagent.guid); + if (cagent) { + for (const u of lagent.alternate_urls) { + if (u === NETDATA.registry.MASKED_DATA) { // TODO: temp until registry is updated. + continue; + } + + if (!cagent.alternate_urls.includes(u)) { + dirty = true; + cagent.alternate_urls.push(u); + } + } + } else { + dirty = true; + union.set(lagent.guid, lagent); + } + } + + if (dirty) { + return Array.from(union.values()); + } + + return null; +} + +function showSignInModal() { + document.getElementById("sim-registry").innerHTML = NETDATA.registry.server; + $("#signInModal").modal("show"); +} + +function explicitlySignIn() { + $("#signInModal").modal("hide"); + signIn(); +} + +function showSyncModal() { + document.getElementById("sync-registry-modal-registry").innerHTML = NETDATA.registry.server; + $("#syncRegistryModal").modal("show"); +} + +function explicitlySyncAgents() { + $("#syncRegistryModal").modal("hide"); + + const json = localStorage.getItem("cloud.sync"); + const sync = json ? JSON.parse(json): {}; + delete sync[cloudAccountID]; + localStorage.setItem("cloud.sync", JSON.stringify(sync)); + + NETDATA.registry.init(); +} + +function syncAgents(callback) { + const json = localStorage.getItem("cloud.sync"); + const sync = json ? JSON.parse(json): {}; + + const currentAgent = { + guid: NETDATA.registry.machine_guid, + name: NETDATA.registry.hostname, + url: NETDATA.serverDefault, + alternate_urls: [NETDATA.serverDefault], + } + + const localAgents = sync[cloudAccountID] + ? [currentAgent] + : registryAgents.concat([currentAgent]); + + console.log("Checking if sync is needed.", localAgents); + + const agentsToSync = mergeAgents(cloudAgents, localAgents); + + if ((!sync[cloudAccountID]) || agentsToSync) { + sync[cloudAccountID] = new Date().getTime(); + localStorage.setItem("cloud.sync", JSON.stringify(sync)); + } + + if (agentsToSync) { + console.log("Synchronizing with netdata.cloud."); + + postCloudAccountAgents(agentsToSync).then((agents) => { + // TODO: clear syncTime on error! + cloudAgents = agents; + callback(cloudAgents); + }); + + return + } + + callback(cloudAgents); +} + +let isCloudSSOInitialized = false; + +function cloudSSOInit() { + const iframeEl = document.getElementById("ssoifrm"); + const url = `${NETDATA.registry.cloudBaseURL}/account/sso-agent?id=${NETDATA.registry.machine_guid}`; + iframeEl.src = url; + isCloudSSOInitialized = true; +} + +function cloudSSOSignOut() { + const iframe = document.getElementById("ssoifrm"); + const url = `${NETDATA.registry.cloudBaseURL}/account/sign-out-agent`; + iframe.src = url; +} + +function initCloud() { + if (!NETDATA.registry.isCloudEnabled) { + clearCloudVariables(); + clearCloudLocalStorageItems(); + return; + } + + if (NETDATA.registry.cloudBaseURL != localStorage.getItem("cloud.baseURL")) { + clearCloudVariables(); + clearCloudLocalStorageItems(); + if (NETDATA.registry.cloudBaseURL) { + localStorage.setItem("cloud.baseURL", NETDATA.registry.cloudBaseURL); + } + } + + if (!isCloudSSOInitialized) { + cloudSSOInit(); + } + + renderAccountUI(); +} + +// This callback is called after NETDATA.registry is initialized. +function netdataRegistryCallback(machinesArray) { + localStorage.setItem("cloud.agentID", NETDATA.registry.machine_guid); + + initCloud(); + + registryAgents = machinesArray; + + if (isSignedIn()) { + // We call getCloudAccountAgents() here because it requires that + // NETDATA.registry is initialized. + clearMyNetdataMenu(); + getCloudAccountAgents().then((agents) => { + if (!agents) { + errorMyNetdataMenu(); + return; + } + cloudAgents = agents; + syncAgents((agents) => { + const agentsMap = {} + for (const agent of agents) { + agentsMap[agent.guid] = agent; + } + + NETDATA.registry.machines = agentsMap; + NETDATA.registry.machines_array = agents; + + renderMyNetdataMenu(agents); + }); + }); + } else { + renderMyNetdataMenu(machinesArray) + } +}; + +// If we know the cloudBaseURL and agentID from local storage render (eagerly) +// the account ui before receiving the definitive response from the web server. +// This improves the perceived performance. +function tryFastInitCloud() { + const baseURL = localStorage.getItem("cloud.baseURL"); + const agentID = localStorage.getItem("cloud.agentID"); + + if (baseURL && agentID) { + NETDATA.registry.cloudBaseURL = baseURL; + NETDATA.registry.machine_guid = agentID; + NETDATA.registry.isCloudEnabled = true; + + initCloud(); + } +} + +function initializeApp() { + window.addEventListener("message", handleMessage, false); + +// tryFastInitCloud(); +} + +if (document.readyState === "complete") { + initializeApp(); +} else { + document.addEventListener("readystatechange", () => { + if (document.readyState === "complete") { + initializeApp(); + } + }) +} -- cgit v1.2.3