Added agent console to mobile app.
Ylian Saint-Hilaire committed
Feb 2, 2021 at 15:48 UTC
34b8a7eed5b926fff28e0a9b057f5883ddc71a5e
1 file changed
+323
-1
views/default-mobile.handlebars
+323
-1
@@ -471,6 +471,77 @@
471
border: 12px solid;
472
border-color: transparent #F0ECCD #F0ECCD transparent;
473
}
474
+
475
+ #p15statetext {
476
+ padding: 4px;
477
+ height: 15px;
478
+ }
479
+
480
+ #p15agentConsole {
481
+ background: black;
482
+ margin: 0;
483
+ padding: 0;
484
+ color: lightgray;
485
+ width: 100%;
486
+ position: relative;
487
+ }
488
+
489
+ #p15coreName {
490
+ padding: 4px;
491
+ display: inline-block;
492
+ }
493
+
494
+ #p15agentConsoleText {
495
+ position:absolute;
496
+ margin: 0;
497
+ padding: 0;
498
+ top: 0;
499
+ bottom: 0;
500
+ left:0;
501
+ right: 0;
502
+ overflow-y: scroll;
503
+ overflow-x: auto;
504
+ }
505
+
506
+ .areaHead {
507
+ padding-left: 4px;
508
+ padding-top: 2px;
509
+ padding-bottom: 2px;
510
+ background: #C0C0C0;
511
+ }
512
+
513
+ .night .areaHead {
514
+ color: #CCC;
515
+ background: #333;
516
+ }
517
+
518
+ .areaFoot {
519
+ padding-top: 2px;
520
+ padding-bottom: 2px;
521
+ background: #C0C0C0;
522
+ }
523
+
524
+ .night .areaFoot {
525
+ color: #CCC;
526
+ background: #333;
527
+ }
528
+
529
+ .toright2 {
530
+ float: right;
531
+ text-align: right;
532
+ padding-right: 4px;
533
+ }
534
+
535
+ #consoleTable {
536
+ width: 100%;
537
+ height: 100%;
538
+ padding: 0px;
539
+ margin-top: 0px;
540
+ }
541
+
542
+ .night #consoleTable {
543
+ color: black;
544
+ }
545
</style>
546
</head>
547
<body id="body" onload="if (typeof(startup) !== 'undefined') startup();" style="overflow-y:hidden;margin:0;padding:0;border:0;color:black;font-size:13px;font-family:\'Trebuchet MS\', Arial, Helvetica, sans-serif">
@@ -798,6 +869,43 @@
869
<div id=p10details style="overflow-y:scroll;position:absolute;top:55px;bottom:0px;width:100%">
870
<div id=p10detailshtml style="margin-left:-3px"></div>
871
</div>
872
+ <div id=p10console style="overflow:hidden;position:absolute;top:55px;bottom:0px;width:100%">
873
+ <table id="consoleTable" cellpadding=0 cellspacing=0>
874
+ <tr style="height:28px">
875
+ <td class="areaHead">
876
+ <div class="toright2">
877
+ <div id=p15coreName></div>
878
+ <input type=button id=p15uploadCore value="Agent Action" onclick=p15uploadCore(event) />
879
+ </div>
880
+ <div id="p15statetext"></div>
881
+ </td>
882
+ </tr>
883
+ <tr>
884
+ <td id=p15agentConsole style="position:relative">
885
+ <pre id=p15agentConsoleText></pre>
886
+ </td>
887
+ </tr>
888
+ <tr style="height:28px">
889
+ <td class="areaFoot">
890
+ <table style="width:100%">
891
+ <tr>
892
+ <td style="width:99%">
893
+ <input id=p15consoleText style=width:100%;box-sizing:border-box onkeyup=p15consoleSend(event) />
894
+ </td>
895
+ <td id="p15outputselecttd">
896
+ <select id=p15outputselect onchange="setupConsole()">
897
+ <option id="p15outputselect1" value=1>Agent</option>
898
+ <option id="p15outputselect3" value=3>Push</option>
899
+ <option id="p15outputselect2" value=2>MQTT</option>
900
+ </select>
901
+ </td>
902
+ <td style="width:1%"><input id="id_p15consoleClear" type="button" class="bottombutton" value="Clear" onclick="p15consoleClear()" /></td>
903
+ </tr>
904
+ </table>
905
+ </td>
906
+ </tr>
907
+ </table>
908
+ </div>
909
</div>
910
<div id=p20 style="display:none;position:absolute;bottom:0;top:0;width:100%">
911
<table cellspacing=0 style="margin:0;padding:0;border-spacing:0;border:0;position:absolute;top:0">
@@ -1242,7 +1350,8 @@
1350
var index = -1;
1351
if (nodes != null) { for (var i in nodes) { if (nodes[i]._id == message.nodeid) { index = i; break; } } }
1352
if (index != -1) {
1245
- if (message.type == 'notify') { // This is a notification message.
1353
+ if (message.type == 'console') { p15consoleReceive(nodes[index], message.value, message.source); } // This is a console message.
1354
+ else if (message.type == 'notify') { // This is a notification message.
1355
var n = getstore('notifications', 0);
1356
if (((n & 8) == 0) && (message.amtMessage != null)) { break; } // Intel AMT desktop & terminal messages should be ignored.
1357
var n = { text: message.value, title: message.title, icon: message.icon, titleid: message.titleid, msgid: message.msgid, args: message.args };
@@ -2903,6 +3012,7 @@
3012
// Show node last 7 days timeline
3013
//drawDeviceTimeline();
3014
setupFiles();
3015
+ if (meshrights & 16) { setupConsole(); }
3016
3017
// Show bottom buttons
3018
x = '<div style=float:right;font-size:x-small;margin-right:10px>';
@@ -2973,6 +3083,7 @@
3083
QV('p10desktop', currentDevicePanel == 1); // Show if we have remote control rights or desktop view only rights
3084
QV('p10files', currentDevicePanel == 2);
3085
QV('p10details', currentDevicePanel == 3);
3086
+ QV('p10console', currentDevicePanel == 4);
3087
var menus = [];
3088
if (currentDevicePanel != 0) { menus.push({ n: "General", f: 'setupDeviceMenu(0)' }); }
3089
@@ -2984,6 +3095,7 @@
3095
3096
if ((currentDevicePanel != 2) && (currentNode != null) && (meshrights & 8) && ((meshrights == 0xFFFFFFFF) || ((meshrights & 1024) == 0)) && ((currentNode.mtype == 2) && (currentNode.agent.caps & 4))) { menus.push({ n: "Files", f: 'setupDeviceMenu(2)' }); }
3097
if ((currentDevicePanel != 3) && (currentNode != null)) { menus.push({ n: "Details", f: 'setupDeviceMenu(3)' }); }
3098
+ if ((currentDevicePanel != 4) && (currentNode != null) && (meshrights & 0x00000010)) { menus.push({ n: "Console", f: 'setupDeviceMenu(4)' }); }
3099
updateFooterMenu(menus);
3100
}
3101
@@ -4517,6 +4629,216 @@
4629
}
4630
}
4631
4632
+
4633
+ //
4634
+ // CONSOLE
4635
+ //
4636
+
4637
+ /*
4638
+ function agentConsoleHandleKeys(e) {
4639
+ if ((e.ctrlKey) || (e.altKey)) { return true; }
4640
+ var processed = 0, box = Q('p15consoleText');
4641
+ if (e.key) {
4642
+ if (e.keyCode == 13 && consoleFocus == 0) { p15consoleSend(e); processed = 1; }
4643
+ else if (e.keyCode == 8 && consoleFocus == 0) { var x = box.value; box.value = x.substring(0, x.length - 1); processed = 1; }
4644
+ else if (e.keyCode == 27) { box.value = ''; processed = 1; }
4645
+ else if ((e.keyCode == 38) || (e.keyCode == 40)) { // Arrow up || Arrow down
4646
+ var hindex = consoleHistory.indexOf(box.value);
4647
+ //console.log(hindex, consoleHistory);
4648
+ if ((e.keyCode == 38) && ((consoleHistory.length - 1) > hindex)) { box.value = consoleHistory[hindex + 1]; }
4649
+ else if ((e.keyCode == 40) && (hindex > 0)) { box.value = consoleHistory[hindex - 1]; }
4650
+ else if ((e.keyCode == 40) && (hindex == 0)) { box.value = ''; }
4651
+ processed = 1;
4652
+ }
4653
+ else if (e.key.length === 1) {
4654
+ //box.value = ((box.value + e.key));
4655
+ insertTextAtCursor(box, e.key);
4656
+ processed = 1;
4657
+ }
4658
+ } else {
4659
+ if (e.charCode != 0 && consoleFocus == 0) { box.value = ((box.value + String.fromCharCode(e.charCode))); processed = 1; }
4660
+ }
4661
+ if (processed > 0) { return haltEvent(e); }
4662
+ }
4663
+ */
4664
+
4665
+ // Insert text at the cursor location on the
4666
+ function insertTextAtCursor(ctrl, val) {
4667
+ if (document.selection) { ctrl.focus(); sel = document.selection.createRange(); sel.text = val; }
4668
+ else if (ctrl.selectionStart || ctrl.selectionStart == '0') {
4669
+ var start = ctrl.selectionStart, end = ctrl.selectionEnd;
4670
+ ctrl.value = ctrl.value.substring(0, start) + val + ctrl.value.substring(end, ctrl.value.length);
4671
+ ctrl.setSelectionRange(end + 1, end + 1);
4672
+ } else { ctrl.value += myValue; }
4673
+ }
4674
+
4675
+ var consoleNode;
4676
+ var consoleServerText = '';
4677
+ function setupConsole() {
4678
+ // Setup the console
4679
+ var samenode = (consoleNode == currentNode);
4680
+ consoleNode = currentNode;
4681
+
4682
+ var mesh = meshes[consoleNode.meshid];
4683
+ var rights = GetNodeRights(currentNode);
4684
+ if ((rights & 16) != 0) {
4685
+ if (consoleNode.consoleText == null) { consoleNode.consoleText = ''; }
4686
+ if (samenode == false) {
4687
+ QH('p15agentConsoleText', consoleNode.consoleText);
4688
+ Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
4689
+ }
4690
+ var online = (((consoleNode.conn & 1) != 0) || ((consoleNode.conn & 16) != 0)) ? true : false;
4691
+ var onlineText = ((consoleNode.conn & 1) != 0) ? "Agent is online" : "Agent is offline"
4692
+ if ((consoleNode.conn & 16) != 0) { onlineText += ", MQTT is online" }
4693
+ QH('p15statetext', onlineText);
4694
+ QE('p15uploadCore', ((consoleNode.conn & 1) != 0));
4695
+ QV('p15outputselecttd', ((consoleNode.conn & 16) != 0) || ((currentNode.pmt == 1) && ((features2 & 2) != 0)));
4696
+ QV('p15outputselect2', ((consoleNode.conn & 16) != 0)); // MQTT channel
4697
+ QV('p15outputselect3', ((currentNode.pmt == 1) && ((features2 & 2) != 0))); // Push Notification channel
4698
+
4699
+ var c = Q('p15outputselect').value;
4700
+ if (((consoleNode.conn & 16) == 0) && (c == 2)) { c = 1; Q('p15outputselect').value = 1; }
4701
+ if (((currentNode.pmt != 1) || ((features2 & 2) == 0)) && (c == 3)) { c = 1; Q('p15outputselect').value = 1; }
4702
+
4703
+ var active = false;
4704
+ if (((consoleNode.conn & 1) != 0) && (c == 1)) { active = true; } // Agent
4705
+ if (((consoleNode.conn & 16) != 0) && (c == 2)) { active = true; } // MQTT
4706
+ if (((currentNode.pmt == 1) && ((features2 & 2) != 0)) && (c == 3)) { active = true; } // Push
4707
+ QE('p15consoleText', active);
4708
+ } else {
4709
+ QH('p15statetext', "Access Denied");
4710
+ QE('p15consoleText', false);
4711
+ QE('p15uploadCore', false);
4712
+ QV('p15outputselecttd', false);
4713
+ }
4714
+ QV('devListToolbarViewIcons3', ((consoleNode.conn & 1) != 0));
4715
+ }
4716
+
4717
+ // Clear the console for this node
4718
+ function p15consoleClear() {
4719
+ QH('p15agentConsoleText', '');
4720
+ Q('id_p15consoleClear').blur();
4721
+ consoleNode.consoleText = '';
4722
+ }
4723
+
4724
+ // Send a command to the agent
4725
+ var consoleHistory = [];
4726
+ function p15consoleSend(e) {
4727
+ if (e && e.keyCode != 13) return;
4728
+ var v = Q('p15consoleText').value, t = '<div style=color:green>> ' + EscapeHtml(v) + '<br/></div>';
4729
+
4730
+ if (((consoleNode.conn & 16) != 0) && (Q('p15outputselect').value == 2)) {
4731
+ // Send the command to MQTT
4732
+ t = '<div style=color:orange>' + "MQTT" + '> ' + EscapeHtml(v) + '<br/></div>';
4733
+ consoleNode.consoleText += t;
4734
+ meshserver.send({ action: 'sendmqttmsg', topic: 'console', nodeids: [consoleNode._id], msg: v });
4735
+ } else if ((consoleNode.pmt == 1) && (Q('p15outputselect').value == 3) && ((features2 & 2) != 0)) {
4736
+ // Send the command using push notification
4737
+ t = '<div style=color:violet>' + "PUSH" + '> ' + EscapeHtml(v) + '<br/></div>';
4738
+ consoleNode.consoleText += t;
4739
+ meshserver.send({ action: 'pushconsole', nodeid: consoleNode._id, console: v });
4740
+ } else if ((consoleNode.conn & 1) != 0) {
4741
+ // Send the command to the mesh agent
4742
+ consoleNode.consoleText += t;
4743
+ meshserver.send({ action: 'msg', type: 'console', nodeid: consoleNode._id, value: v });
4744
+ }
4745
+
4746
+ Q('p15agentConsoleText').innerHTML += t;
4747
+ Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
4748
+ Q('p15consoleText').value = '';
4749
+
4750
+ // Add command to history list
4751
+ if (v.length > 0) {
4752
+ // Move this command to the top if it already exists
4753
+ var j = consoleHistory.indexOf(v);
4754
+ if (j >= 0) { consoleHistory.splice(j, 1); }
4755
+ consoleHistory.unshift(v);
4756
+ consoleHistory.splice(10);
4757
+ }
4758
+ }
4759
+
4760
+ // Handle Mesh Agent console data
4761
+ function p15consoleReceive(node, data, source) {
4762
+ if (node === 'serverconsole') {
4763
+ // Server console data
4764
+ data = '<div>' + EscapeHtml(data) + '</div>'
4765
+ consoleServerText += data;
4766
+ if (consoleNode == 'server') {
4767
+ Q('p15agentConsoleText').innerHTML += data;
4768
+ Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
4769
+ }
4770
+ } else {
4771
+ // Agent console data
4772
+ if (source == 'MQTT') { data = '<div style=color:red>' + "MQTT" + '> ' + EscapeHtml(data) + '<br/></div>'; } else { data = '<div>' + EscapeHtml(data) + '</div>' }
4773
+ if (node.consoleText == null) { node.consoleText = data; } else { node.consoleText += data; }
4774
+ if (consoleNode == node) {
4775
+ Q('p15agentConsoleText').innerHTML += data;
4776
+ Q('p15agentConsoleText').scrollTop = Q('p15agentConsoleText').scrollHeight;
4777
+ }
4778
+ }
4779
+ }
4780
+
4781
+ // Save console text to file
4782
+ function p15downloadConsoleText() {
4783
+ saveAs(new Blob([Q('p15agentConsoleText').innerText], { type: 'application/octet-stream' }), "console.txt");
4784
+ }
4785
+
4786
+ // Called then user presses the "Change Core" button
4787
+ function p15uploadCore(e) {
4788
+ if (xxdialogMode) return;
4789
+ if (e.shiftKey == true) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'default' }); } // Upload default core
4790
+ else if (e.altKey == true) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'clear' }); } // Clear the core
4791
+ else if (e.ctrlKey == true) { p15uploadCore2(); } // Upload the core from a file
4792
+ else { setDialogMode(2, "Perform Agent Action", 3, p15uploadCoreEx, addHtmlValue("Action", '<select id=d3coreMode style=width:230px><option value=1>' + "Upload default server core" + '</option><option value=2>' + "Clear the core" + '</option><option value=6>' + "Upload recovery core" + '</option><option value=7>' + "Upload tiny core" + '</option><option value=3>' + "Upload a core file" + '</option><option value=4>' + "Soft disconnect agent" + '</option><option value=5>' + "Hard disconnect agent" + '</option></select>')); }
4793
+ }
4794
+
4795
+ function p15uploadCoreEx() {
4796
+ if (Q('d3coreMode').value == 1) {
4797
+ // Upload default core
4798
+ meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'default' });
4799
+ } else if (Q('d3coreMode').value == 2) {
4800
+ // Clear the core
4801
+ meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'clear' });
4802
+ } else if (Q('d3coreMode').value == 3) {
4803
+ // Upload file as core
4804
+ p15uploadCore2();
4805
+ } else if (Q('d3coreMode').value == 4) {
4806
+ // Soft disconnect the mesh agent
4807
+ meshserver.send({ action: 'agentdisconnect', nodeid: consoleNode._id, disconnectMode: 1 });
4808
+ } else if (Q('d3coreMode').value == 5) {
4809
+ // Hard disconnect the mesh agent
4810
+ meshserver.send({ action: 'agentdisconnect', nodeid: consoleNode._id, disconnectMode: 2 });
4811
+ } else if (Q('d3coreMode').value == 6) {
4812
+ // Upload a recovery core
4813
+ meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'recovery' });
4814
+ } else if (Q('d3coreMode').value == 7) {
4815
+ // Upload a tiny core
4816
+ meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'tiny' });
4817
+ }
4818
+ }
4819
+
4820
+ // Called then user opts to upload a file as core
4821
+ function p15uploadCore2() {
4822
+ if (xxdialogMode) return;
4823
+ Q('d3localmodeform').action = 'uploadmeshcorefile.ashx';
4824
+ Q('d3auth').value = authCookie;
4825
+ Q('d3attrib').value = currentNode._id;
4826
+ setDialogMode(3, "Upload Mesh Agent Core", 3, p15uploadCoreEx2);
4827
+ d3init();
4828
+ }
4829
+
4830
+ function p15uploadCoreEx2() {
4831
+ var mode = Q('d3uploadMode').value;
4832
+ if (mode == 1) {
4833
+ // Upload local mesh agent core
4834
+ Q('d3submit').click();
4835
+ } else {
4836
+ // Upload server mesh agent code
4837
+ var files = d3getFileSel();
4838
+ if (files.length == 1) { meshserver.send({ action: 'uploadagentcore', nodeid: consoleNode._id, type: 'custom', path: d3filetreelocation.join('/') + '/' + files[0] }); }
4839
+ }
4840
+ }
4841
+
4842
//
4843
// MY MESHS
4844
//