453
saveTextToClient(JSON.stringify(data), filename);
454
}
455
456
-// --------------------------------------------------------------------
456
+// -----------------------------------------------------------------------------
457
// registry call back to render my-netdata menu
458
459
function toggleExpandIcon(svgEl) {
476
}
477
}
478
479
-// TODO: consider renaming to `truncateString`
480
-
481
-/// Enforces a maximum string length while retaining the prefix and the postfix of
482
-/// the string.
483
-function clipString(str, maxLength) {
484
- if (str.length <= maxLength) {
485
- return str;
486
- }
487
-
488
- const spanLength = Math.floor((maxLength - 3) / 2);
489
- return `${str.substring(0, spanLength)}...${str.substring(str.length - spanLength)}`;
490
-}
491
-
479
// When you stream metrics from netdata to netdata, the recieving netdata now
480
// has multiple host databases. It's own, and multiple mirrored. Mirrored databases
481
// can be accessed with <http://localhost:19999/host/NAME/>
499
return naturalSortCompare(a.hostname, b.hostname);
500
});
501
502
+ let displayedDatabases = false;
503
+
504
for (var s of sorted) {
505
let url, icon;
506
const hostname = s.hostname;
507
508
+ if (myNetdataMenuFilterValue !== "") {
509
+ if (!hostname.includes(myNetdataMenuFilterValue)) {
510
+ continue;
511
+ }
512
+ }
513
+
514
+ displayedDatabases = true;
515
+
516
if (hostname === master) {
517
url = `${base}/`;
518
icon = 'home';
534
)
535
}
536
537
+ if (!displayedDatabases) {
538
+ html += (
539
+ `<div class="info-item">
540
+ <i class="fas fa-filter"></i>
541
+ <span style="margin-left: 8px">no databases match the filter criteria.<span>
542
+ </div>`
543
+ )
544
+ }
545
+
546
return html;
547
}
548
549
function renderMachines(machinesArray) {
544
- let html = `<div class="info-item">My netdata agents</div>`;
550
+ // let html = isSignedIn()
551
+ // ? `<div class="info-item">My nodes</div>`
552
+ // : `<div class="info-item">My nodes</div>`;
553
+
554
+ let html = `<div class="info-item">My nodes</div>`;
555
556
if (machinesArray === null) {
557
let ret = loadLocalStorage("registryCallback");
562
}
563
564
let found = false;
565
+ let displayedAgents = false;
566
+
567
+ const maskedURL = NETDATA.registry.MASKED_DATA;
568
569
if (machinesArray) {
570
saveLocalStorage("registryCallback", JSON.stringify(machinesArray));
576
for (var machine of machines) {
577
found = true;
578
579
+ if (myNetdataMenuFilterValue !== "") {
580
+ if (!machine.name.includes(myNetdataMenuFilterValue)) {
581
+ continue;
582
+ }
583
+ }
584
+
585
+ displayedAgents = true;
586
+
587
const alternateUrlItems = (
588
`<div class="agent-alternate-urls agent-${machine.guid} collapsed">
568
- ${machine.alternate_urls.reduce(
569
- (str, url) => str + (
570
- `<div class="agent-item agent-item--alternate">
571
- <div></div>
572
- <a href="${url}" title="${url}">${clipString(url, 64)}</a>
573
- <a href="#" onclick="deleteRegistryModalHandler('${machine.guid}', '${machine.name}', '${url}'); return false;">
574
- <i class="fas fa-trash" style="color: #777;"></i>
575
- </a>
576
- </div>`
577
- ),
589
+ ${machine.alternate_urls.reduce((str, url) => {
590
+ if (url === maskedURL) {
591
+ return str
592
+ }
593
+
594
+ return str + (
595
+ `<div class="agent-item agent-item--alternate">
596
+ <div></div>
597
+ <a href="${url}" title="${url}">${truncateString(url, 64)}</a>
598
+ <a href="#" onclick="deleteRegistryModalHandler('${machine.guid}', '${machine.name}', '${url}'); return false;">
599
+ <i class="fas fa-trash" style="color: #777;"></i>
600
+ </a>
601
+ </div>`
602
+ )
603
+ },
604
''
605
)}
606
</div>`
619
${alternateUrlItems}`
620
)
621
}
622
+
623
+ if (found && (!displayedAgents)) {
624
+ html += (
625
+ `<div class="info-item">
626
+ <i class="fas fa-filter"></i>
627
+ <span style="margin-left: 8px">zero nodes are matching the filter value.<span>
628
+ </div>`
629
+ )
630
+ }
631
}
632
633
if (!found) {
634
if (machines) {
635
html += (
636
`<div class="info-item">
602
- <a href="https://github.com/netdata/netdata/tree/master/registry#netdata-registry" target="_blank">Your netdata server list is empty</a>
637
+ <a href="https://github.com/netdata/netdata/tree/master/registry#netdata-registry" target="_blank">Your nodes list is empty</a>
638
</div>`
639
)
640
} else {
646
}
647
648
html += `<hr />`;
614
- html += `<div class="info-item">Demo netdata agents</div>`;
649
+ html += `<div class="info-item">Demo netdata nodes</div>`;
650
651
const demoServers = [
652
{url: "//london.netdata.rocks/default.html", title: "UK - London (DigitalOcean.com)"},
663
for (var server of demoServers) {
664
html += (
665
`<div class="agent-item">
631
- <i class="fas fa-chart-bar" color: #fff"></i>
666
+ <i class="fas fa-chart-bar" style="color: #fff"></i>
667
<a href="${server.url}">${server.title}</a>
668
<div></div>
669
</div>
675
return html;
676
}
677
643
-// Populates the my-netdata menu.
644
-function netdataRegistryCallback(machinesArray) {
678
+function setMyNetdataMenu(html) {
679
+ const el = document.getElementById('my-netdata-dropdown-content')
680
+ el.innerHTML = html;
681
+}
682
+
683
+function clearMyNetdataMenu() {
684
+ setMyNetdataMenu(`<div class="agent-item" style="white-space: nowrap">
685
+ <i class="fas fa-hourglass-half"></i>
686
+ Loading, please wait...
687
+ <div></div>
688
+ </div>`);
689
+}
690
+
691
+function errorMyNetdataMenu() {
692
+ setMyNetdataMenu(`<div class="agent-item" style="white-space: nowrap">
693
+ <i class="fas fa-exclamation-triangle" style="color: red"></i>
694
+ Cannot load known netdata agents from netdata.cloud!
695
+ <div></div>
696
+ </div>`);
697
+}
698
+
699
+function restrictMyNetdataMenu() {
700
+ setMyNetdataMenu(`<div class="info-item" style="white-space: nowrap">
701
+ <span>Please <a href="#" onclick="signInDidClick(event); return false">sign in to netdata.cloud</a> to view your nodes!</span>
702
+ <div></div>
703
+ </div>`);
704
+}
705
+
706
+function renderMyNetdataMenu(machinesArray) {
707
+ const el = document.getElementById('my-netdata-dropdown-content');
708
+ el.classList.add(`theme-${netdataTheme}`);
709
+
710
+ if (!isSignedIn()) {
711
+ if (!NETDATA.registry.isRegistryEnabled()) {
712
+ restrictMyNetdataMenu();
713
+ return;
714
+ }
715
+ }
716
+
717
+ if (machinesArray == registryAgents) {
718
+ console.log("Rendering my-netdata menu from registry");
719
+ } else {
720
+ console.log("Rendering my-netdata menu from netdata.cloud", machinesArray);
721
+ }
722
+
723
let html = '';
724
725
+ if (isSignedIn()) {
726
+ html += (
727
+ `<div class="filter-control">
728
+ <input
729
+ id="my-netdata-menu-filter-input"
730
+ type="text"
731
+ placeholder="filter nodes..."
732
+ autocomplete="off"
733
+ value="${myNetdataMenuFilterValue}"
734
+ onkeydown="myNetdataFilterDidChange(event)"
735
+ />
736
+ <span class="filter-control__clear" onclick="myNetdataFilterClearDidClick(event)"><i class="fas fa-times"></i><span>
737
+ </div>
738
+ <hr />`
739
+ );
740
+ }
741
+
742
if (options.hosts.length > 1) {
648
- html += renderStreamedHosts(options) + `<hr />`;
743
+ html += `<div id="my-netdata-menu-streamed">${renderStreamedHosts(options)}</div><hr />`;
744
}
745
651
- html += renderMachines(machinesArray);
652
-
653
- html += (
654
- `<hr />
655
- <div class="agent-item">
656
- <i class="fas fa-cog""></i>
657
- <a href="#" onclick="switchRegistryModalHandler(); return false;">Switch Identity</a>
658
- <div></div>
659
- </div>
660
- <div class="agent-item">
661
- <i class="fas fa-question-circle""></i>
662
- <a href="https://github.com/netdata/netdata/tree/master/registry#netdata-registry" target="_blank">What is this?</a>
663
- <div></div>
664
- </div>`
665
- )
746
+ html += `<div id="my-netdata-menu-machines">${renderMachines(machinesArray)}</div>`;
747
+
748
+ if (!isSignedIn()) {
749
+ html += (
750
+ `<hr />
751
+ <div class="agent-item">
752
+ <i class="fas fa-cog""></i>
753
+ <a href="#" onclick="switchRegistryModalHandler(); return false;">Switch Identity</a>
754
+ <div></div>
755
+ </div>
756
+ <div class="agent-item">
757
+ <i class="fas fa-question-circle""></i>
758
+ <a href="https://github.com/netdata/netdata/tree/master/registry#netdata-registry" target="_blank">What is this?</a>
759
+ <div></div>
760
+ </div>`
761
+ )
762
+ } else {
763
+ html += (
764
+ `<hr />
765
+ <div class="agent-item">
766
+ <i class="fas fa-sync"></i>
767
+ <a href="#" onclick="showSyncModal(); return false">Synchronize with netdata.cloud</a>
768
+ <div></div>
769
+ </div>
770
+ <div class="agent-item">
771
+ <i class="fas fa-question-circle""></i>
772
+ <a href="https://netdata.cloud/about" target="_blank">What is this?</a>
773
+ <div></div>
774
+ </div>`
775
+ )
776
+ }
777
667
- const el = document.getElementById('my-netdata-dropdown-content')
668
- el.classList.add(`theme-${netdataTheme}`);
778
el.innerHTML = html;
779
780
gotoServerInit();
672
-};
781
+}
782
783
function isdemo() {
784
if (this_is_demo !== null) {
873
} else {
874
document.getElementById('gotoServerResponse').innerHTML += 'found it! It is at:<br/><small>' + escapeUserInputHTML(url) + '</small>';
875
document.location = verifyURL(finalURL);
876
+ $('#gotoServerModal').modal('hide');
877
}
878
}
879
} else {
912
gotoServerValidateUrl(count++, guid, url);
913
}
914
805
- setTimeout(function () {
806
- if (gotoServerStop === false) {
807
- document.getElementById('gotoServerResponse').innerHTML = '<b>Added all the known URLs for this machine.</b>';
808
- NETDATA.registry.search(guid, function (data) {
809
- // console.log(data);
810
- len = data.urls.length;
811
- while (len--) {
812
- var url = data.urls[len][1];
813
- // console.log(url);
814
- if (typeof checked[url] === 'undefined') {
815
- gotoServerValidateRemaining++;
816
- checked[url] = true;
817
- gotoServerValidateUrl(count++, guid, url);
915
+ if (!isSignedIn()) {
916
+ // When the registry is enabled, if the user's known URLs are not working
917
+ // we consult the registry to get additional URLs.
918
+ setTimeout(function () {
919
+ if (gotoServerStop === false) {
920
+ document.getElementById('gotoServerResponse').innerHTML = '<b>Added all the known URLs for this machine.</b>';
921
+ NETDATA.registry.search(guid, function (data) {
922
+ // console.log(data);
923
+ len = data.urls.length;
924
+ while (len--) {
925
+ var url = data.urls[len][1];
926
+ // console.log(url);
927
+ if (typeof checked[url] === 'undefined') {
928
+ gotoServerValidateRemaining++;
929
+ checked[url] = true;
930
+ gotoServerValidateUrl(count++, guid, url);
931
+ }
932
}
819
- }
820
- });
821
- }
822
- }, 2000);
933
+ });
934
+ }
935
+ }, 2000);
936
+ }
937
+
938
return false;
939
}
940
975
}
976
}
977
978
+var deleteRegistryGuid = null;
979
var deleteRegistryUrl = null;
980
981
function deleteRegistryModalHandler(guid, name, url) {
866
- void (guid);
982
+ // void (guid);
983
984
+ deleteRegistryGuid = guid;
985
deleteRegistryUrl = url;
986
+
987
document.getElementById('deleteRegistryServerName').innerHTML = name;
988
document.getElementById('deleteRegistryServerName2').innerHTML = name;
989
document.getElementById('deleteRegistryServerURL').innerHTML = url;
990
document.getElementById('deleteRegistryResponse').innerHTML = '';
991
+
992
$('#deleteRegistryModal').modal('show');
993
}
994
995
function notifyForDeleteRegistry() {
996
+ const responseEl = document.getElementById('deleteRegistryResponse');
997
+
998
if (deleteRegistryUrl) {
878
- NETDATA.registry.delete(deleteRegistryUrl, function (result) {
879
- if (result !== null) {
880
- deleteRegistryUrl = null;
881
- $('#deleteRegistryModal').modal('hide');
882
- NETDATA.registry.init();
883
- } else {
884
- document.getElementById('deleteRegistryResponse').innerHTML = "<b>Sorry! this command was rejected by the registry server.</b>";
885
- }
886
- });
999
+ if (isSignedIn()) {
1000
+ deleteCloudAgentURL(deleteRegistryGuid, deleteRegistryUrl)
1001
+ .then((count) => {
1002
+ if (!count) {
1003
+ responseEl.innerHTML = "<b>Sorry, this command was rejected by netdata.cloud!</b>";
1004
+ return;
1005
+ }
1006
+ NETDATA.registry.delete(deleteRegistryUrl, function (result) {
1007
+ if (result === null) {
1008
+ console.log("Received error from registry", result);
1009
+ }
1010
+
1011
+ deleteRegistryUrl = null;
1012
+ $('#deleteRegistryModal').modal('hide');
1013
+ NETDATA.registry.init();
1014
+ });
1015
+ });
1016
+ } else {
1017
+ NETDATA.registry.delete(deleteRegistryUrl, function (result) {
1018
+ if (result !== null) {
1019
+ deleteRegistryUrl = null;
1020
+ $('#deleteRegistryModal').modal('hide');
1021
+ NETDATA.registry.init();
1022
+ } else {
1023
+ responseEl.innerHTML = "<b>Sorry, this command was rejected by the registry server!</b>";
1024
+ }
1025
+ });
1026
+ }
1027
}
1028
}
1029
2703
}
2704
2705
// update the dashboard hostname
2566
- document.getElementById('hostname').innerHTML = options.hostname + ((netdataSnapshotData !== null) ? ' (snap)' : '').toString();
2706
+ document.getElementById('hostname').innerHTML = options.hostname + ((netdataSnapshotData !== null) ? ' (snap)' : '').toString() + ' <strong class="caret">';
2707
document.getElementById('hostname').href = NETDATA.serverDefault;
2708
document.getElementById('netdataVersion').innerHTML = options.version;
2709
4484
// var netdataStarted = performance.now();
4485
4486
var netdataCallback = initializeDynamicDashboard;
4487
+
4488
+// =================================================================================================
4489
+// netdata.cloud
4490
+
4491
+let registryAgents = [];
4492
+
4493
+let cloudAgents = [];
4494
+
4495
+let myNetdataMenuFilterValue = "";
4496
+
4497
+let cloudAccountID = null;
4498
+
4499
+let cloudAccountName = null;
4500
+
4501
+let cloudToken = null;
4502
+
4503
+/// Enforces a maximum string length while retaining the prefix and the postfix of
4504
+/// the string.
4505
+function truncateString(str, maxLength) {
4506
+ if (str.length <= maxLength) {
4507
+ return str;
4508
+ }
4509
+
4510
+ const spanLength = Math.floor((maxLength - 3) / 2);
4511
+ return `${str.substring(0, spanLength)}...${str.substring(str.length - spanLength)}`;
4512
+}
4513
+
4514
+// -------------------------------------------------------------------------------------------------
4515
+// netdata.cloud API Client
4516
+// -------------------------------------------------------------------------------------------------
4517
+
4518
+function isValidAgent(a) {
4519
+ return a.urls != null && a.urls.length > 0;
4520
+}
4521
+
4522
+// https://github.com/netdata/hub/issues/146
4523
+function getCloudAccountAgents() {
4524
+ if (!isSignedIn()) {
4525
+ return [];
4526
+ }
4527
+
4528
+ return fetch(
4529
+ `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents`,
4530
+ {
4531
+ method: "GET",
4532
+ mode: "cors",
4533
+ headers: {
4534
+ "Authorization": `Bearer ${cloudToken}`
4535
+ }
4536
+ }
4537
+ ).then((response) => {
4538
+ if (!response.ok) {
4539
+ throw Error("Cannot fetch known accounts");
4540
+ }
4541
+ return response.json();
4542
+ }).then((payload) => {
4543
+ const agents = payload.result ? payload.result.agents : null;
4544
+
4545
+ if (!agents) {
4546
+ return [];
4547
+ }
4548
+
4549
+ return agents.filter((a) => isValidAgent(a)).map((a) => {
4550
+ return {
4551
+ "guid": a.id,
4552
+ "name": a.name,
4553
+ "url": a.urls[0],
4554
+ "alternate_urls": a.urls
4555
+ }
4556
+ })
4557
+ }).catch(function (error) {
4558
+ console.log(error);
4559
+ return null;
4560
+ });
4561
+}
4562
+
4563
+// https://github.com/netdata/hub/issues/128
4564
+function postCloudAccountAgents(agentsToSync) {
4565
+ if (!isSignedIn()) {
4566
+ return [];
4567
+ }
4568
+
4569
+ const maskedURL = NETDATA.registry.MASKED_DATA;
4570
+
4571
+ const agents = agentsToSync.map((a) => {
4572
+ const urls = a.alternate_urls.filter((url) => url != maskedURL);
4573
+
4574
+ return {
4575
+ "id": a.guid,
4576
+ "name": a.name,
4577
+ "urls": urls
4578
+ }
4579
+ }).filter((a) => isValidAgent(a))
4580
+
4581
+ const payload = {
4582
+ "accountID": cloudAccountID,
4583
+ "agents": agents,
4584
+ "merge": false,
4585
+ };
4586
+
4587
+ return fetch(
4588
+ `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents`,
4589
+ {
4590
+ method: "POST",
4591
+ mode: "cors",
4592
+ headers: {
4593
+ "Content-Type": "application/json; charset=utf-8",
4594
+ "Authorization": `Bearer ${cloudToken}`
4595
+ },
4596
+ body: JSON.stringify(payload)
4597
+ }
4598
+ ).then((response) => {
4599
+ return response.json();
4600
+ }).then((payload) => {
4601
+ const agents = payload.result ? payload.result.agents : null;
4602
+
4603
+ if (!agents) {
4604
+ return [];
4605
+ }
4606
+
4607
+ return agents.filter((a) => isValidAgent(a)).map((a) => {
4608
+ return {
4609
+ "guid": a.id,
4610
+ "name": a.name,
4611
+ "url": a.urls[0],
4612
+ "alternate_urls": a.urls
4613
+ }
4614
+ })
4615
+ });
4616
+}
4617
+
4618
+function deleteCloudAgentURL(agentID, url) {
4619
+ if (!isSignedIn()) {
4620
+ return [];
4621
+ }
4622
+
4623
+ return fetch(
4624
+ `${NETDATA.registry.cloudBaseURL}/api/v1/accounts/${cloudAccountID}/agents/${agentID}/url?value=${encodeURIComponent(url)}`,
4625
+ {
4626
+ method: "DELETE",
4627
+ mode: "cors",
4628
+ headers: {
4629
+ "Content-Type": "application/json; charset=utf-8",
4630
+ "Authorization": `Bearer ${cloudToken}`
4631
+ },
4632
+ }
4633
+ ).then((response) => {
4634
+ return response.json();
4635
+ }).then((payload) => {
4636
+ const count = payload.result ? payload.result.count : 0;
4637
+ return count;
4638
+ });
4639
+}
4640
+
4641
+// -------------------------------------------------------------------------------------------------
4642
+
4643
+function signInDidClick(e) {
4644
+ e.preventDefault();
4645
+ e.stopPropagation();
4646
+
4647
+ if (!NETDATA.registry.isUsingGlobalRegistry()) {
4648
+ // If user is using a private registry, request his consent for
4649
+ // synchronizing with cloud.
4650
+ showSignInModal();
4651
+ return;
4652
+ }
4653
+
4654
+ signIn();
4655
+}
4656
+
4657
+function signOutDidClick(e) {
4658
+ e.preventDefault();
4659
+ e.stopPropagation();
4660
+ signOut();
4661
+}
4662
+
4663
+// -------------------------------------------------------------------------------------------------
4664
+
4665
+function updateMyNetdataAfterFilterChange() {
4666
+ const machinesEl = document.getElementById("my-netdata-menu-machines")
4667
+ machinesEl.innerHTML = renderMachines(cloudAgents);
4668
+
4669
+ if (options.hosts.length > 1) {
4670
+ const streamedEl = document.getElementById("my-netdata-menu-streamed")
4671
+ streamedEl.innerHTML = renderStreamedHosts(options);
4672
+ }
4673
+}
4674
+
4675
+function myNetdataFilterDidChange(e) {
4676
+ const inputEl = e.target;
4677
+ setTimeout(() => {
4678
+ myNetdataMenuFilterValue = inputEl.value;
4679
+ updateMyNetdataAfterFilterChange();
4680
+ }, 1);
4681
+}
4682
+
4683
+function myNetdataFilterClearDidClick(e) {
4684
+ e.preventDefault();
4685
+ e.stopPropagation();
4686
+
4687
+ const inputEl = document.getElementById("my-netdata-menu-filter-input");
4688
+ inputEl.value = "";
4689
+ myNetdataMenuFilterValue = "";
4690
+
4691
+ updateMyNetdataAfterFilterChange();
4692
+
4693
+ inputEl.focus();
4694
+}
4695
+
4696
+// -------------------------------------------------------------------------------------------------
4697
+
4698
+function clearCloudVariables() {
4699
+ cloudAccountID = null;
4700
+ cloudAccountName = null;
4701
+ cloudToken = null;
4702
+}
4703
+
4704
+function clearCloudLocalStorageItems() {
4705
+ localStorage.removeItem("cloud.baseURL");
4706
+ localStorage.removeItem("cloud.agentID");
4707
+ localStorage.removeItem("cloud.sync");
4708
+}
4709
+
4710
+function signIn() {
4711
+ const url = `${NETDATA.registry.cloudBaseURL}/account/sign-in-agent?origin=${encodeURIComponent(window.location.origin + "/")}`;
4712
+ window.open(url);
4713
+}
4714
+
4715
+function signOut() {
4716
+ cloudSSOSignOut();
4717
+}
4718
+
4719
+function renderAccountUI() {
4720
+ if (!NETDATA.registry.isCloudEnabled) {
4721
+ return
4722
+ }
4723
+
4724
+ const container = document.getElementById("account-menu-container");
4725
+ if (isSignedIn()) {
4726
+ container.removeAttribute("title");
4727
+ container.removeAttribute("data-original-title");
4728
+ container.removeAttribute("data-placement");
4729
+ container.innerHTML = (
4730
+ `<a href="#" class="dropdown-toggle" data-toggle="dropdown"><span id="amc-account-name"></span> <strong class="caret"></strong></a>
4731
+ <ul id="cloud-menu" class="dropdown-menu scrollable-menu inpagemenu" role="menu">
4732
+ <li>
4733
+ <a href="#" class="btn" onclick="signOutDidClick(event); return false">
4734
+ <i class="fas fa-sign-out-alt"></i> <span class="hidden-sm hidden-md">Sign Out</span>
4735
+ </a>
4736
+ </li>
4737
+ </ul>`
4738
+ )
4739
+ document.getElementById("amc-account-name").textContent = cloudAccountName; // Anti-XSS
4740
+ } else {
4741
+ container.setAttribute("data-original-title", "sign in");
4742
+ container.setAttribute("data-placement", "bottom");
4743
+ container.innerHTML = (
4744
+ `<a href="#" class="btn" onclick="signInDidClick(event); return false">
4745
+ <i class="fas fa-sign-in-alt"></i> <span class="hidden-sm hidden-md">Sign In</span>
4746
+ </a>`
4747
+ )
4748
+ }
4749
+}
4750
+
4751
+function handleMessage(e) {
4752
+ switch (e.data.type) {
4753
+ case "sign-in":
4754
+ handleSignInMessage(e);
4755
+ break;
4756
+
4757
+ case "sign-out":
4758
+ handleSignOutMessage(e);
4759
+ break;
4760
+
4761
+ default:
4762
+ console.log("Unknown message", e);
4763
+ }
4764
+}
4765
+
4766
+function handleSignInMessage(e) {
4767
+ localStorage.setItem("cloud.baseURL", NETDATA.registry.cloudBaseURL);
4768
+
4769
+ cloudAccountID = e.data.accountID;
4770
+ cloudAccountName = e.data.accountName;
4771
+ cloudToken = e.data.token;
4772
+
4773
+ netdataRegistryCallback(registryAgents);
4774
+}
4775
+
4776
+function handleSignOutMessage(e) {
4777
+ clearCloudVariables();
4778
+ renderAccountUI();
4779
+ renderMyNetdataMenu(registryAgents);
4780
+}
4781
+
4782
+function isSignedIn() {
4783
+ return cloudToken != null && cloudAccountID != null;
4784
+}
4785
+
4786
+function sortedArraysEqual(a, b) {
4787
+ if (a.length != b.length) return false;
4788
+
4789
+ for (var i = 0; i < a.length; ++i) {
4790
+ if (a[i] !== b[i]) return false;
4791
+ }
4792
+
4793
+ return true;
4794
+}
4795
+
4796
+// If merging is needed returns the merged agents set, otherwise returns null.
4797
+function mergeAgents(cloud, local) {
4798
+ let dirty = false;
4799
+
4800
+ const union = new Map();
4801
+
4802
+ for (const cagent of cloud) {
4803
+ union.set(cagent.guid, cagent);
4804
+ }
4805
+
4806
+ for (const lagent of local) {
4807
+ const cagent = union.get(lagent.guid);
4808
+ if (cagent) {
4809
+ for (const u of lagent.alternate_urls) {
4810
+ if (u === NETDATA.registry.MASKED_DATA) { // TODO: temp until registry is updated.
4811
+ continue;
4812
+ }
4813
+
4814
+ if (!cagent.alternate_urls.includes(u)) {
4815
+ dirty = true;
4816
+ cagent.alternate_urls.push(u);
4817
+ }
4818
+ }
4819
+ } else {
4820
+ dirty = true;
4821
+ union.set(lagent.guid, lagent);
4822
+ }
4823
+ }
4824
+
4825
+ if (dirty) {
4826
+ return Array.from(union.values());
4827
+ }
4828
+
4829
+ return null;
4830
+}
4831
+
4832
+function showSignInModal() {
4833
+ document.getElementById("sim-registry").innerHTML = NETDATA.registry.server;
4834
+ $("#signInModal").modal("show");
4835
+}
4836
+
4837
+function explicitlySignIn() {
4838
+ $("#signInModal").modal("hide");
4839
+ signIn();
4840
+}
4841
+
4842
+function showSyncModal() {
4843
+ document.getElementById("sync-registry-modal-registry").innerHTML = NETDATA.registry.server;
4844
+ $("#syncRegistryModal").modal("show");
4845
+}
4846
+
4847
+function explicitlySyncAgents() {
4848
+ $("#syncRegistryModal").modal("hide");
4849
+
4850
+ const json = localStorage.getItem("cloud.sync");
4851
+ const sync = json ? JSON.parse(json): {};
4852
+ delete sync[cloudAccountID];
4853
+ localStorage.setItem("cloud.sync", JSON.stringify(sync));
4854
+
4855
+ NETDATA.registry.init();
4856
+}
4857
+
4858
+function syncAgents(callback) {
4859
+ const json = localStorage.getItem("cloud.sync");
4860
+ const sync = json ? JSON.parse(json): {};
4861
+
4862
+ const currentAgent = {
4863
+ guid: NETDATA.registry.machine_guid,
4864
+ name: NETDATA.registry.hostname,
4865
+ url: NETDATA.serverDefault,
4866
+ alternate_urls: [NETDATA.serverDefault],
4867
+ }
4868
+
4869
+ const localAgents = sync[cloudAccountID]
4870
+ ? [currentAgent]
4871
+ : registryAgents.concat([currentAgent]);
4872
+
4873
+ console.log("Checking if sync is needed.", localAgents);
4874
+
4875
+ const agentsToSync = mergeAgents(cloudAgents, localAgents);
4876
+
4877
+ if ((!sync[cloudAccountID]) || agentsToSync) {
4878
+ sync[cloudAccountID] = new Date().getTime();
4879
+ localStorage.setItem("cloud.sync", JSON.stringify(sync));
4880
+ }
4881
+
4882
+ if (agentsToSync) {
4883
+ console.log("Synchronizing with netdata.cloud.");
4884
+
4885
+ postCloudAccountAgents(agentsToSync).then((agents) => {
4886
+ // TODO: clear syncTime on error!
4887
+ cloudAgents = agents;
4888
+ callback(cloudAgents);
4889
+ });
4890
+
4891
+ return
4892
+ }
4893
+
4894
+ callback(cloudAgents);
4895
+}
4896
+
4897
+let isCloudSSOInitialized = false;
4898
+
4899
+function cloudSSOInit() {
4900
+ const iframeEl = document.getElementById("ssoifrm");
4901
+ const url = `${NETDATA.registry.cloudBaseURL}/account/sso-agent?id=${NETDATA.registry.machine_guid}`;
4902
+ iframeEl.src = url;
4903
+ isCloudSSOInitialized = true;
4904
+}
4905
+
4906
+function cloudSSOSignOut() {
4907
+ const iframe = document.getElementById("ssoifrm");
4908
+ const url = `${NETDATA.registry.cloudBaseURL}/account/sign-out-agent`;
4909
+ iframe.src = url;
4910
+}
4911
+
4912
+function initCloud() {
4913
+ if (!NETDATA.registry.isCloudEnabled) {
4914
+ clearCloudVariables();
4915
+ clearCloudLocalStorageItems();
4916
+ return;
4917
+ }
4918
+
4919
+ if (NETDATA.registry.cloudBaseURL != localStorage.getItem("cloud.baseURL")) {
4920
+ clearCloudVariables();
4921
+ clearCloudLocalStorageItems();
4922
+ if (NETDATA.registry.cloudBaseURL) {
4923
+ localStorage.setItem("cloud.baseURL", NETDATA.registry.cloudBaseURL);
4924
+ }
4925
+ }
4926
+
4927
+ if (!isCloudSSOInitialized) {
4928
+ cloudSSOInit();
4929
+ }
4930
+
4931
+ renderAccountUI();
4932
+}
4933
+
4934
+// This callback is called after NETDATA.registry is initialized.
4935
+function netdataRegistryCallback(machinesArray) {
4936
+ localStorage.setItem("cloud.agentID", NETDATA.registry.machine_guid);
4937
+
4938
+ initCloud();
4939
+
4940
+ registryAgents = machinesArray;
4941
+
4942
+ if (isSignedIn()) {
4943
+ // We call getCloudAccountAgents() here because it requires that
4944
+ // NETDATA.registry is initialized.
4945
+ clearMyNetdataMenu();
4946
+ getCloudAccountAgents().then((agents) => {
4947
+ if (!agents) {
4948
+ errorMyNetdataMenu();
4949
+ return;
4950
+ }
4951
+ cloudAgents = agents;
4952
+ syncAgents((agents) => {
4953
+ const agentsMap = {}
4954
+ for (const agent of agents) {
4955
+ agentsMap[agent.guid] = agent;
4956
+ }
4957
+
4958
+ NETDATA.registry.machines = agentsMap;
4959
+ NETDATA.registry.machines_array = agents;
4960
+
4961
+ renderMyNetdataMenu(agents);
4962
+ });
4963
+ });
4964
+ } else {
4965
+ renderMyNetdataMenu(machinesArray)
4966
+ }
4967
+};
4968
+
4969
+// If we know the cloudBaseURL and agentID from local storage render (eagerly)
4970
+// the account ui before receiving the definitive response from the web server.
4971
+// This improves the perceived performance.
4972
+function tryFastInitCloud() {
4973
+ const baseURL = localStorage.getItem("cloud.baseURL");
4974
+ const agentID = localStorage.getItem("cloud.agentID");
4975
+
4976
+ if (baseURL && agentID) {
4977
+ NETDATA.registry.cloudBaseURL = baseURL;
4978
+ NETDATA.registry.machine_guid = agentID;
4979
+ NETDATA.registry.isCloudEnabled = true;
4980
+
4981
+ initCloud();
4982
+ }
4983
+}
4984
+
4985
+function initializeApp() {
4986
+ window.addEventListener("message", handleMessage, false);
4987
+
4988
+// tryFastInitCloud();
4989
+}
4990
+
4991
+if (document.readyState === "complete") {
4992
+ initializeApp();
4993
+} else {
4994
+ document.addEventListener("readystatechange", () => {
4995
+ if (document.readyState === "complete") {
4996
+ initializeApp();
4997
+ }
4998
+ })
4999
+}