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>&gt; ' + 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" + '&gt; ' + 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" + '&gt; ' + 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" + '&gt; ' + 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 //