Added browser notification support in the main web app.

This commit is contained in:
Ylian Saint-Hilaire 2019-04-29 15:31:11 -07:00
parent f924dd0d9b
commit 92b3da3b50
21 changed files with 132 additions and 68 deletions

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -72,7 +72,7 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
obj.PerformAjaxEx = function (postdata, callback, tag, url, action) { obj.PerformAjaxEx = function (postdata, callback, tag, url, action) {
if (obj.FailAllError != 0) { if (obj.FailAllError != 999) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag]); } return; } if (obj.FailAllError != 0) { if (obj.FailAllError != 999) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag]); } return; }
if (!postdata) postdata = ""; if (!postdata) postdata = "";
//console.log("SEND: " + postdata); // DEBUG if (globalDebugFlags & 1) { console.log("SEND: " + postdata + "\r\n\r\n"); } // DEBUG
// We are in a DukTape environement // We are in a DukTape environement
if (obj.digest == null) if (obj.digest == null)
@ -92,9 +92,9 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
//console.log('Request ' + (obj.RequestCount++)); //console.log('Request ' + (obj.RequestCount++));
req.on('error', function (e) { obj.gotNextMessagesError({ status: 600 }, 'error', null, [postdata, callback, tag]); }); req.on('error', function (e) { obj.gotNextMessagesError({ status: 600 }, 'error', null, [postdata, callback, tag]); });
req.on('response', function (response) { req.on('response', function (response) {
//console.log('Response: ' + response.statusCode); if (globalDebugFlags & 1) { console.log('Response: ' + response.statusCode); }
if (response.statusCode != 200) { if (response.statusCode != 200) {
//console.log('ERR:' + JSON.stringify(response)); if (globalDebugFlags & 1) { console.log('ERR:' + JSON.stringify(response)); }
obj.gotNextMessagesError({ status: response.statusCode }, 'error', null, [postdata, callback, tag]); obj.gotNextMessagesError({ status: response.statusCode }, 'error', null, [postdata, callback, tag]);
} else { } else {
response.acc = ''; response.acc = '';
@ -116,7 +116,7 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
obj.gotNextMessages = function (data, status, request, callArgs) { obj.gotNextMessages = function (data, status, request, callArgs) {
obj.ActiveAjaxCount--; obj.ActiveAjaxCount--;
if (obj.FailAllError == 999) return; if (obj.FailAllError == 999) return;
//console.log("RECV: " + data); // DEBUG if (globalDebugFlags & 1) { console.log("RECV: " + data + "\r\n\r\n"); } // DEBUG
if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; } if (obj.FailAllError != 0) { callArgs[1](null, obj.FailAllError, callArgs[2]); return; }
if (request.status != 200) { callArgs[1](null, request.status, callArgs[2]); return; } if (request.status != 200) { callArgs[1](null, request.status, callArgs[2]); return; }
callArgs[1](data, 200, callArgs[2]); callArgs[1](data, 200, callArgs[2]);

View File

@ -1 +1 @@
function CreateWsmanComm(){var a={};a.PendingAjax=[];a.ActiveAjaxCount=0;a.MaxActiveAjaxCount=1;a.FailAllError=0;a.digest=null;a.RequestCount=0;if(arguments.length==1&&typeof(arguments[0]=="object")){a.host=arguments[0].host;a.port=arguments[0].port;a.authToken=arguments[0].authToken;a.tls=arguments[0].tls}else{a.host=arguments[0];a.port=arguments[1];a.user=arguments[2];a.pass=arguments[3];a.tls=arguments[4]}a.PerformAjax=function(d,c,f,e,g,b){if((a.ActiveAjaxCount==0||((a.ActiveAjaxCount<a.MaxActiveAjaxCount)&&(a.challengeParams!=null)))&&a.PendingAjax.length==0){a.PerformAjaxEx(d,c,f,g,b)}else{if(e==1){a.PendingAjax.unshift([d,c,f,g,b])}else{a.PendingAjax.push([d,c,f,g,b])}}};a.PerformNextAjax=function(){if(a.ActiveAjaxCount>=a.MaxActiveAjaxCount||a.PendingAjax.length==0){return}var b=a.PendingAjax.shift();a.PerformAjaxEx(b[0],b[1],b[2],b[3],b[4]);a.PerformNextAjax()};a.PerformAjaxEx=function(d,c,g,h,b){if(a.FailAllError!=0){if(a.FailAllError!=999){a.gotNextMessagesError({status:a.FailAllError},"error",null,[d,c,g])}return}if(!d){d=""}if(a.digest==null){if(a.authToken){a.digest=require("http-digest").create({authToken:a.authToken})}else{a.digest=require("http-digest").create(a.user,a.pass)}a.digest.http=require("http")}var f={protocol:(a.tls==1?"https:":"http:"),method:"POST",host:a.host,path:"/wsman",port:a.port,rejectUnauthorized:false,checkServerIdentity:function(i){console.log("checkServerIdentity",JSON.stringify(i))}};var e=a.digest.request(f);e.on("error",function(i){a.gotNextMessagesError({status:600},"error",null,[d,c,g])});e.on("response",function(i){if(i.statusCode!=200){a.gotNextMessagesError({status:i.statusCode},"error",null,[d,c,g])}else{i.acc="";i.on("data",function(j){this.acc+=j});i.on("end",function(){a.gotNextMessages(i.acc,"success",{status:i.statusCode},[d,c,g])})}});e.end(d);a.ActiveAjaxCount++;return e};a.pendingAjaxCall=[];a.gotNextMessages=function(c,e,d,b){a.ActiveAjaxCount--;if(a.FailAllError==999){return}if(a.FailAllError!=0){b[1](null,a.FailAllError,b[2]);return}if(d.status!=200){b[1](null,d.status,b[2]);return}b[1](c,200,b[2]);a.PerformNextAjax()};a.gotNextMessagesError=function(d,e,c,b){a.ActiveAjaxCount--;if(a.FailAllError==999){return}if(a.FailAllError!=0){b[1](null,a.FailAllError,b[2]);return}if(a.FailAllError!=999){b[1]({Header:{HttpError:d.status}},d.status,b[2])}a.PerformNextAjax()};a.CancelAllQueries=function(b){while(a.PendingAjax.length>0){var c=a.PendingAjax.shift();c[1](null,b,c[2])}};return a}module.exports=CreateWsmanComm; function CreateWsmanComm(){var a={};a.PendingAjax=[];a.ActiveAjaxCount=0;a.MaxActiveAjaxCount=1;a.FailAllError=0;a.digest=null;a.RequestCount=0;if(arguments.length==1&&typeof(arguments[0]=="object")){a.host=arguments[0].host;a.port=arguments[0].port;a.authToken=arguments[0].authToken;a.tls=arguments[0].tls}else{a.host=arguments[0];a.port=arguments[1];a.user=arguments[2];a.pass=arguments[3];a.tls=arguments[4]}a.PerformAjax=function(d,c,f,e,g,b){if((a.ActiveAjaxCount==0||((a.ActiveAjaxCount<a.MaxActiveAjaxCount)&&(a.challengeParams!=null)))&&a.PendingAjax.length==0){a.PerformAjaxEx(d,c,f,g,b)}else{if(e==1){a.PendingAjax.unshift([d,c,f,g,b])}else{a.PendingAjax.push([d,c,f,g,b])}}};a.PerformNextAjax=function(){if(a.ActiveAjaxCount>=a.MaxActiveAjaxCount||a.PendingAjax.length==0){return}var b=a.PendingAjax.shift();a.PerformAjaxEx(b[0],b[1],b[2],b[3],b[4]);a.PerformNextAjax()};a.PerformAjaxEx=function(d,c,g,h,b){if(a.FailAllError!=0){if(a.FailAllError!=999){a.gotNextMessagesError({status:a.FailAllError},"error",null,[d,c,g])}return}if(!d){d=""}if(globalDebugFlags&1){console.log("SEND: "+d+"\r\n\r\n")}if(a.digest==null){if(a.authToken){a.digest=require("http-digest").create({authToken:a.authToken})}else{a.digest=require("http-digest").create(a.user,a.pass)}a.digest.http=require("http")}var f={protocol:(a.tls==1?"https:":"http:"),method:"POST",host:a.host,path:"/wsman",port:a.port,rejectUnauthorized:false,checkServerIdentity:function(i){console.log("checkServerIdentity",JSON.stringify(i))}};var e=a.digest.request(f);e.on("error",function(i){a.gotNextMessagesError({status:600},"error",null,[d,c,g])});e.on("response",function(i){if(globalDebugFlags&1){console.log("Response: "+i.statusCode)}if(i.statusCode!=200){if(globalDebugFlags&1){console.log("ERR:"+JSON.stringify(i))}a.gotNextMessagesError({status:i.statusCode},"error",null,[d,c,g])}else{i.acc="";i.on("data",function(j){this.acc+=j});i.on("end",function(){a.gotNextMessages(i.acc,"success",{status:i.statusCode},[d,c,g])})}});e.end(d);a.ActiveAjaxCount++;return e};a.pendingAjaxCall=[];a.gotNextMessages=function(c,e,d,b){a.ActiveAjaxCount--;if(a.FailAllError==999){return}if(globalDebugFlags&1){console.log("RECV: "+c+"\r\n\r\n")}if(a.FailAllError!=0){b[1](null,a.FailAllError,b[2]);return}if(d.status!=200){b[1](null,d.status,b[2]);return}b[1](c,200,b[2]);a.PerformNextAjax()};a.gotNextMessagesError=function(d,e,c,b){a.ActiveAjaxCount--;if(a.FailAllError==999){return}if(a.FailAllError!=0){b[1](null,a.FailAllError,b[2]);return}if(a.FailAllError!=999){b[1]({Header:{HttpError:d.status}},d.status,b[2])}a.PerformNextAjax()};a.CancelAllQueries=function(b){while(a.PendingAjax.length>0){var c=a.PendingAjax.shift();c[1](null,b,c[2])}};return a}module.exports=CreateWsmanComm;

View File

@ -753,7 +753,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
db.GetUserWithVerifiedEmail(domain.id, command.email, function (err, docs) { db.GetUserWithVerifiedEmail(domain.id, command.email, function (err, docs) {
if (docs.length > 0) { if (docs.length > 0) {
// Notify the duplicate email error // Notify the duplicate email error
try { ws.send(JSON.stringify({ action: 'msg', type: 'notify', value: 'Failed to change email address, another account already using: <b>' + EscapeHtml(command.email) + '</b>.' })); } catch (ex) { } try { ws.send(JSON.stringify({ action: 'msg', type: 'notify', title: 'Account Settings', tag: 'ServerNotify', value: 'Failed to change email address, another account already using: <b>' + EscapeHtml(command.email) + '</b>.' })); } catch (ex) { }
} else { } else {
// Update the user's email // Update the user's email
var oldemail = user.email; var oldemail = user.email;
@ -857,8 +857,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} }
} }
// Remove notes for this user db.Remove('ws' + deluser._id); // Remove user web state
db.Remove('nt' + deluser._id); db.Remove('nt' + deluser._id); // Remove notes for this user
// Delete all files on the server for this account // Delete all files on the server for this account
try { try {
@ -883,7 +883,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if (common.validateString(command.msg, 1, 256) == false) break; // Notification message is between 1 and 256 characters if (common.validateString(command.msg, 1, 256) == false) break; // Notification message is between 1 and 256 characters
// Create the notification message // Create the notification message
var notification = { action: "msg", type: "notify", domain: domain.id, "value": command.msg }; var notification = { action: "msg", type: "notify", domain: domain.id, "value": command.msg, "title": user.name, icon: 0, tag: "broadcast" };
// Send the notification on all user sessions for this server // Send the notification on all user sessions for this server
for (var i in parent.wssessions2) { for (var i in parent.wssessions2) {
@ -925,7 +925,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Account count exceed, do notification // Account count exceed, do notification
// Create the notification message // Create the notification message
var notification = { action: "msg", type: "notify", value: "Account limit reached.", userid: user._id, username: user.name, domain: domain.id }; var notification = { action: "msg", type: "notify", value: "Account limit reached.", title: "Server Limit", userid: user._id, username: user.name, domain: domain.id };
// Get the list of sessions for this user // Get the list of sessions for this user
var sessions = parent.wssessions[user._id]; var sessions = parent.wssessions[user._id];
@ -1025,7 +1025,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
require('./pass').hash(command.newpass, function (err, salt, hash) { require('./pass').hash(command.newpass, function (err, salt, hash) {
if (err) { if (err) {
// Send user notification of error // Send user notification of error
displayNotificationMessage('Error, password not changed.'); displayNotificationMessage('Error, password not changed.', 'Account Settings', 'ServerNotify');
} else { } else {
// Change the password // Change the password
if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true) && (command.hint != null)) { if ((domain.passwordrequirements != null) && (domain.passwordrequirements.hint === true) && (command.hint != null)) {
@ -1044,12 +1044,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Account password changed: ' + user.name, domain: domain.id }); parent.parent.DispatchEvent(targets, obj, { etype: 'user', username: user.name, account: parent.CloneSafeUser(user), action: 'accountchange', msg: 'Account password changed: ' + user.name, domain: domain.id });
// Send user notification of password change // Send user notification of password change
displayNotificationMessage('Password changed.'); displayNotificationMessage('Password changed.', 'Account Settings', 'ServerNotify');
} }
}); });
} else { } else {
// Send user notification of error // Send user notification of error
displayNotificationMessage('Current password not correct.'); displayNotificationMessage('Current password not correct.', 'Account Settings', 'ServerNotify');
} }
}); });
break; break;
@ -1112,7 +1112,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
if ((user.groups != null) && (user.groups.length > 0) && ((chguser.groups == null) || (findOne(chguser.groups, user.groups) == false))) break; if ((user.groups != null) && (user.groups.length > 0) && ((chguser.groups == null) || (findOne(chguser.groups, user.groups) == false))) break;
// Create the notification message // Create the notification message
var notification = { "action": "msg", "type": "notify", "value": "<b>" + user.name + "</b>: " + EscapeHtml(command.msg), "userid": user._id, "username": user.name }; var notification = { "action": "msg", "type": "notify", "value": command.msg, "title": user.name, "icon": 9, "userid": user._id, "username": user.name };
// Get the list of sessions for this user // Get the list of sessions for this user
var sessions = parent.wssessions[command.userid]; var sessions = parent.wssessions[command.userid];
@ -1138,7 +1138,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
// Create the notification message // Create the notification message
var notification = { var notification = {
"action": "msg", "type": "notify", "value": "<b>" + user.name + "</b>: Chat Request, Click here to accept.", "userid": user._id, "username": user.name, "tag": 'meshmessenger/' + encodeURIComponent(command.userid) + '/' + encodeURIComponent(user._id) "action": "msg", "type": "notify", "value": "Chat Request, Click here to accept.", "title": user.name, "userid": user._id, "username": user.name, "tag": 'meshmessenger/' + encodeURIComponent(command.userid) + '/' + encodeURIComponent(user._id)
}; };
// Get the list of sessions for this user // Get the list of sessions for this user
@ -2297,6 +2297,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}); });
break; break;
} }
case 'userWebState': {
if (common.validateString(command.state, 1, 10000) == false) break; // Check state size, no more than 10k
db.Set({ _id: 'ws' + user._id, state: command.state });
parent.parent.DispatchEvent([user._id], obj, { action: 'userWebState', nolog: 1, domain: domain.id, state: command.state });
break;
}
case 'getNotes': case 'getNotes':
{ {
// Argument validation // Argument validation
@ -2363,7 +2369,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
} }
// Display a notification message for this session only. // Display a notification message for this session only.
function displayNotificationMessage(msg, tag) { ws.send(JSON.stringify({ "action": "msg", "type": "notify", "value": msg, "userid": user._id, "username": user.name, "tag": tag })); } function displayNotificationMessage(msg, title, tag) { ws.send(JSON.stringify({ "action": "msg", "type": "notify", "value": msg, "title": title, "userid": user._id, "username": user.name, "tag": tag })); }
// Read the folder and all sub-folders and serialize that into json. // Read the folder and all sub-folders and serialize that into json.
function readFilesRec(path) { function readFilesRec(path) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -1008,7 +1008,7 @@ NoMeshesPanel img {
width: 250px; width: 250px;
} }
#p12warning, #p12warning2, #p14warning, #p14warning2 { #p12warning, #p12warning2, #p11warning, #p11warning2 {
max-width: 100%; max-width: 100%;
display: none; display: none;
cursor: pointer; cursor: pointer;

View File

@ -257,6 +257,7 @@
<p><strong>Account actions</strong></p> <p><strong>Account actions</strong></p>
<p class="mL"> <p class="mL">
<span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()">Verify email</a><br /></span> <span id="verifyEmailId" style="display:none"><a onclick="account_showVerifyEmail()">Verify email</a><br /></span>
<span id="accountEnableNotificationsSpan" style="display:none"><a onclick="account_enableNotifications()">Enable web notifications</a><br /></span>
<a onclick="account_showChangeEmail()">Change email address</a><br /> <a onclick="account_showChangeEmail()">Change email address</a><br />
<a onclick="account_showChangePassword()">Change password</a><span id="p2nextPasswordUpdateTime"></span><br /> <a onclick="account_showChangePassword()">Change password</a><span id="p2nextPasswordUpdateTime"></span><br />
<a onclick="account_showDeleteAccount()">Delete account</a><br /> <a onclick="account_showDeleteAccount()">Delete account</a><br />
@ -415,19 +416,18 @@
<h1>Desktop - <span id=p11deviceName></span></h1> <h1>Desktop - <span id=p11deviceName></span></h1>
</div> </div>
</div> </div>
<div id="p14warning" onclick="showFeaturesDlg()"> <div id="p11warning" onclick="showFeaturesDlg()">
<div class="icon2"></div> <div class="icon2"></div>
<div class="warningbox">Intel&reg; AMT Redirection port or KVM feature is disabled<span id="p14warninga">, click here to enable it.</span></div> <div class="warningbox">Intel&reg; AMT Redirection port or KVM feature is disabled<span id="p11warninga">, click here to enable it.</span></div>
</div> </div>
<div id="p14warning2" onclick="showPowerActionDlg()"> <div id="p11warning2" onclick="showPowerActionDlg()">
<div class="icon2"></div> <div class="icon2"></div>
<div class="warningbox">Remote computer is not powered on, click here to issue a power command.</div> <div class="warningbox">Remote computer is not powered on, click here to issue a power command.</div>
</div> </div>
<div id=deskarea0 cellpadding=0 cellspacing=0> <div id=deskarea0 cellpadding=0 cellspacing=0>
<div id=deskarea1 class="areaHead"> <div id=deskarea1 class="areaHead">
<div class="toright2"> <div class="toright2">
<span id="p14power"></span>&nbsp; <span id="p11power"></span>&nbsp;
<div class='deskareaicon' title="Toggle View Mode" onclick="toggleAspectRatio(1)">&#8690;</div> <div class='deskareaicon' title="Toggle View Mode" onclick="toggleAspectRatio(1)">&#8690;</div>
<div class='deskareaicon' title="Rotate Left" onclick="drotate(-1)">&olarr;</div> <div class='deskareaicon' title="Rotate Left" onclick="drotate(-1)">&olarr;</div>
<div class='deskareaicon' title="Rotate Right" onclick="drotate(1)">&orarr;</div> <div class='deskareaicon' title="Rotate Right" onclick="drotate(1)">&orarr;</div>
@ -498,7 +498,7 @@
</div> </div>
<div id="p12warning" onclick=showFeaturesDlg()> <div id="p12warning" onclick=showFeaturesDlg()>
<div class="icon2"></div> <div class="icon2"></div>
<div class="warningbox">Intel&reg; AMT Redirection port or KVM feature is disabled<span id="p14warninga">, click here to enable it.</span></div> <div class="warningbox">Intel&reg; AMT Redirection port or KVM feature is disabled<span id="p12warninga">, click here to enable it.</span></div>
</div> </div>
<div id="p12warning2" onclick=showPowerActionDlg()> <div id="p12warning2" onclick=showPowerActionDlg()>
<div class="icon2"></div> <div class="icon2"></div>
@ -871,6 +871,13 @@
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
'use strict'; 'use strict';
// Process server-side web state
var webState = "{{{webstate}}}";
if (webState != "") { webState = JSON.parse(decodeURIComponent(webState)); }
for (var i in webState) { localStorage.setItem(i, webState[i]); }
//localStorage.clear();
var args; var args;
var autoReconnect = true; var autoReconnect = true;
var powerStatetable = ['', 'Powered', 'Sleep', 'Sleep', 'Sleep', 'Hibernating', 'Power off', 'Present']; var powerStatetable = ['', 'Powered', 'Sleep', 'Sleep', 'Sleep', 'Hibernating', 'Power off', 'Present'];
@ -985,11 +992,11 @@
// Setup page controls // Setup page controls
Q('sortselect').selectedIndex = sort = getstore("sort", 0); Q('sortselect').selectedIndex = sort = getstore("sort", 0);
Q('sizeselect').selectedIndex = getstore("viewsize", 1); Q('sizeselect').selectedIndex = getstore("_viewsize", 1);
Q('SearchInput').value = getstore("search", ""); Q('SearchInput').value = getstore("_search", "");
showRealNames = (getstore("showRealNames", 0) == 1); showRealNames = (getstore("showRealNames", 0) == 1);
Q('RealNameCheckBox').checked = showRealNames; Q('RealNameCheckBox').checked = showRealNames;
Q('viewselect').value = getstore("deviceView", 1); Q('viewselect').value = getstore("_deviceView", 1);
Q('DeskControl').checked = (getstore('DeskControl', 1) == 1); Q('DeskControl').checked = (getstore('DeskControl', 1) == 1);
// Display the page devices // Display the page devices
@ -1198,7 +1205,7 @@
updateNaggleTimer = setTimeout(function () { updateNaggleTimer = setTimeout(function () {
if (updateNaggleFlags & 512) { center(); } if (updateNaggleFlags & 512) { center(); }
if (updateNaggleFlags & 1) { onSearchInputChanged(); } if (updateNaggleFlags & 1) { onSearchInputChanged(); }
if (updateNaggleFlags & 2) { onSortSelectChange(true); } if (updateNaggleFlags & 2) { onSortSelectChange(false); }
if (updateNaggleFlags & 128) { updateMeshes(); } if (updateNaggleFlags & 128) { updateMeshes(); }
if (updateNaggleFlags & 4) { updateDevices(); } if (updateNaggleFlags & 4) { updateDevices(); }
if (updateNaggleFlags & 8) { drawNotifications(); } if (updateNaggleFlags & 8) { drawNotifications(); }
@ -1345,7 +1352,7 @@
// Node was found, dispatch the message // Node was found, dispatch the message
if (message.type == 'console') { p15consoleReceive(nodes[index], message.value); } // This is a console message. if (message.type == 'console') { p15consoleReceive(nodes[index], message.value); } // This is a console message.
else if (message.type == 'notify') { // This is a notification message. else if (message.type == 'notify') { // This is a notification message.
var n = { text:message.value }; var n = { text: message.value, title: message.title, icon: message.icon };
if (message.nodeid != null) { n.nodeid = message.nodeid; } if (message.nodeid != null) { n.nodeid = message.nodeid; }
if (message.tag != null) { n.tag = message.tag; } if (message.tag != null) { n.tag = message.tag; }
if (message.username != null) { n.username = message.username; } if (message.username != null) { n.username = message.username; }
@ -1362,7 +1369,7 @@
} }
} else { } else {
if (message.type == 'notify') { // This is a notification message. if (message.type == 'notify') { // This is a notification message.
var n = { text:message.value }; var n = { text: message.value, title: message.title, icon: message.icon };
if (message.tag != null) { n.tag = message.tag; } if (message.tag != null) { n.tag = message.tag; }
if (message.username != null) { n.username = message.username; } if (message.username != null) { n.username = message.username; }
addNotification(n); addNotification(n);
@ -1606,6 +1613,24 @@
masterUpdate(32); masterUpdate(32);
} }
switch (message.event.action) { switch (message.event.action) {
case 'userWebState': {
// New user web state, update the web page as needed
if (localStorage != null) {
var oldShowRealNames = localStorage.getItem('showRealNames');
var oldUiMode = localStorage.getItem('uiMode');
var oldSort = localStorage.getItem('sort');
var webstate = JSON.parse(message.event.state);
for (var i in webstate) { localStorage.setItem(i, webstate[i]); }
// Update the web page
if ((webstate.deskAspectRatio != null) && (webstate.deskAspectRatio != deskAspectRatio)) { deskAspectRatio = webstate.deskAspectRatio; deskAdjust(); }
if ((webstate.showRealNames != null) && (webstate.showRealNames != oldShowRealNames)) { showRealNames = Q('RealNameCheckBox').checked = (webstate.showRealNames == "1"); masterUpdate(6); }
if ((webstate.uiMode != null) && (webstate.uiMode != oldUiMode)) { userInterfaceSelectMenu(parseInt(webstate.uiMode)); }
if ((webstate.sort != null) && (webstate.sort != oldSort)) { document.getElementById("sortselect").selectedIndex = sort = parseInt(webstate.sort); masterUpdate(6); }
}
break;
}
case 'servertimelinestats': { addServerTimelineStats(message.event.data); break; } case 'servertimelinestats': { addServerTimelineStats(message.event.data); break; }
case 'accountcreate': case 'accountcreate':
case 'accountchange': { case 'accountchange': {
@ -1904,7 +1929,7 @@
break; break;
} }
case 'notify': { case 'notify': {
var n = { text: message.event.value }; var n = { text: message.event.value, title: message.event.title, icon: message.event.icon };
if (message.event.tag != null) { n.tag = message.event.tag; } if (message.event.tag != null) { n.tag = message.event.tag; }
addNotification(n); addNotification(n);
break; break;
@ -1939,7 +1964,7 @@
function onRealNameCheckBox() { function onRealNameCheckBox() {
showRealNames = Q('RealNameCheckBox').checked; showRealNames = Q('RealNameCheckBox').checked;
putstore("showRealNames", showRealNames ? 1 : 0); putstore("showRealNames", showRealNames ? 1 : 0);
masterUpdate(6) masterUpdate(6);
return; return;
} }
@ -1947,8 +1972,8 @@
if (i != null) { Q('viewselect').value = i; } if (i != null) { Q('viewselect').value = i; }
for (var j = 1; j < 5; j++) { Q('devViewButton' + j).classList.remove('viewSelectorSel'); } for (var j = 1; j < 5; j++) { Q('devViewButton' + j).classList.remove('viewSelectorSel'); }
Q('devViewButton' + Q('viewselect').value).classList.add('viewSelectorSel'); Q('devViewButton' + Q('viewselect').value).classList.add('viewSelectorSel');
putstore("deviceView", Q('viewselect').value); putstore("_deviceView", Q('viewselect').value);
putstore("viewsize", Q('sizeselect').value); putstore("_viewsize", Q('sizeselect').value);
masterUpdate(4); masterUpdate(4);
setTimeout("masterUpdate(512)", 200); setTimeout("masterUpdate(512)", 200);
} }
@ -2873,7 +2898,7 @@
function onConsoleFocus(x) { consoleFocus = x; } function onConsoleFocus(x) { consoleFocus = x; }
function onSearchInputChanged() { function onSearchInputChanged() {
var x = Q('SearchInput').value.toLowerCase().trim(); putstore("search", x); var x = Q('SearchInput').value.toLowerCase().trim(); putstore("_search", x);
var userSearch = null, ipSearch = null, groupSearch = null; var userSearch = null, ipSearch = null, groupSearch = null;
if (x.startsWith('user:')) { userSearch = x.substring(5); } if (x.startsWith('user:')) { userSearch = x.substring(5); }
else if (x.startsWith('u:')) { userSearch = x.substring(2); } else if (x.startsWith('u:')) { userSearch = x.substring(2); }
@ -3617,7 +3642,6 @@
var powerTimeline = null; var powerTimeline = null;
function getCurrentNode() { return currentNode; }; function getCurrentNode() { return currentNode; };
function gotoDevice(nodeid, panel, refresh, event) { function gotoDevice(nodeid, panel, refresh, event) {
// Remind the user to verify the email address // Remind the user to verify the email address
if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" tab to change and verify an email address."); return; } if ((userinfo.emailVerified !== true) && (serverinfo.emailcheck == true) && (userinfo.siteadmin != 0xFFFFFFFF)) { setDialogMode(2, "Account Security", 1, null, "Unable to access a device until a email address is verified. This is required for password recovery. Go to the \"My Account\" tab to change and verify an email address."); return; }
@ -5760,6 +5784,10 @@
meshserver.send({ action: 'otp-hkey-get' }); meshserver.send({ action: 'otp-hkey-get' });
} }
function account_enableNotifications() {
if (Notification) { Notification.requestPermission().then(function (permission) { QV('accountEnableNotificationsSpan', permission != "granted"); }); }
}
function account_showVerifyEmail() { function account_showVerifyEmail() {
if (xxdialogMode || (userinfo.emailVerified == true) || (serverinfo.emailcheck != true)) return; if (xxdialogMode || (userinfo.emailVerified == true) || (serverinfo.emailcheck != true)) return;
var x = "Click ok to send a verification mail to:<br /><div style=padding:8px><b>" + EscapeHtml(userinfo.email) + "</b></div>Please wait a few minute to receive the verification."; var x = "Click ok to send a verification mail to:<br /><div style=padding:8px><b>" + EscapeHtml(userinfo.email) + "</b></div>Please wait a few minute to receive the verification.";
@ -7328,20 +7356,21 @@
function notificationSelected(id) { function notificationSelected(id) {
var j = -1; var j = -1;
for (var i in notifications) { if (notifications[i].id == id) { j = i; } } for (var i in notifications) { if (notifications[i].id == id) { j = i; } }
if (j != -1) { if (j != -1) { notificationSelectedEx(notifications[j], id); }
var n = notifications[j]; }
if (n.nodeid != null) {
if (n.tag == 'desktop') gotoDevice(n.nodeid, 12); // Desktop function notificationSelectedEx(n, id) {
else if (n.tag == 'terminal') gotoDevice(n.nodeid, 11); // Terminal if (n.nodeid != null) {
else if (n.tag == 'files') gotoDevice(n.nodeid, 13); // Files if (n.tag == 'desktop') gotoDevice(n.nodeid, 12); // Desktop
else if (n.tag == 'intelamt') gotoDevice(n.nodeid, 14); // Intel AMT else if (n.tag == 'terminal') gotoDevice(n.nodeid, 11); // Terminal
else if (n.tag == 'console') gotoDevice(n.nodeid, 15); // Files else if (n.tag == 'files') gotoDevice(n.nodeid, 13); // Files
else gotoDevice(n.nodeid, 10); // General else if (n.tag == 'intelamt') gotoDevice(n.nodeid, 14); // Intel AMT
} else { else if (n.tag == 'console') gotoDevice(n.nodeid, 15); // Files
if (n.tag.startsWith('meshmessenger/')) { else gotoDevice(n.nodeid, 10); // General
window.open('/messenger?id=' + n.tag + '&title=' + encodeURIComponent(n.username), n.tag.split('/')[2]); } else {
notificationDelete(id); if (n.tag.startsWith('meshmessenger/')) {
} window.open('/messenger?id=' + n.tag + '&title=' + encodeURIComponent(n.username), n.tag.split('/')[2]);
notificationDelete(id);
} }
} }
} }
@ -7367,12 +7396,29 @@
// Add a new notification and play the notification sound // Add a new notification and play the notification sound
function addNotification(n) { function addNotification(n) {
if (n.time == null) { n.time = Date.now(); } // If web notifications are granted, use it.
if (n.id == null) { n.id = Math.random(); } if (Notification && (Notification.permission == "granted")) {
notifications.unshift(n); var notification;
setNotificationCount(notifications.length); if (n.nodeid) {
Q('chimes').play(); var node = getNodeFromId(n.nodeid);
clickNotificationIcon(true); if (node) { notification = new Notification("{{{title}}} - " + node.name, { tag: n.tag, body: n.text, icon: '/images/notify/icons128-' + node.icon + '.png' }); }
} else {
if (n.icon == null) { n.icon = 0; }
notification = new Notification("{{{title}}} - " + n.title, { tag: n.tag, body: n.text, icon: '/images/notify/icons128-' + n.icon + '.png' });
}
notification.xtag = n.tag;
notification.nodeid = n.nodeid;
notification.username = n.username;
notification.onclick = function (e) { notificationSelectedEx(e.target); }
Q('chimes').play();
} else {
if (n.time == null) { n.time = Date.now(); }
if (n.id == null) { n.id = Math.random(); }
notifications.unshift(n);
setNotificationCount(notifications.length);
Q('chimes').play();
clickNotificationIcon(true);
}
} }
// Remove all notifications // Remove all notifications
@ -7688,6 +7734,9 @@
QC(panels[i]).add((x == i) ? 'style3sel' : 'style3x'); QC(panels[i]).add((x == i) ? 'style3sel' : 'style3x');
} }
// If going to the remote desktop tab, adjust the tab.
if (x == 11) { deskAdjust(); }
// Panel 115 is weird, it's panel 15 for device console but used as a server console. // Panel 115 is weird, it's panel 15 for device console but used as a server console.
if (x == 115) { QV('p15', true); } if (x == 115) { QV('p15', true); }
QV('p15uploadCore', x != 115); QV('p15uploadCore', x != 115);
@ -7696,6 +7745,9 @@
if (x == 1) masterUpdate(4); if (x == 1) masterUpdate(4);
// Setup web notifications
if ((x == 2) && Notification) { QV('accountEnableNotificationsSpan', Notification.permission != 'granted'); }
// Fetch the server timeline stats if needed // Fetch the server timeline stats if needed
if ((x == 40) && (serverTimelineStats == null)) { refreshServerTimelineStats(); } if ((x == 40) && (serverTimelineStats == null)) { refreshServerTimelineStats(); }
@ -7705,7 +7757,7 @@
// Generic methods // Generic methods
function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); } function joinPaths() { var x = []; for (var i in arguments) { var w = arguments[i]; if ((w != null) && (w != '')) { while (w.endsWith('/') || w.endsWith('\\')) { w = w.substring(0, w.length - 1); } while (w.startsWith('/') || w.startsWith('\\')) { w = w.substring(1); } x.push(w); } } return x.join('/'); }
function putstore(name, val) { try { if (typeof (localStorage) === 'undefined') return; localStorage.setItem(name, val); } catch (e) { } } function putstore(name, val) { try { if ((typeof (localStorage) === 'undefined') || (localStorage.getItem(name) == val)) return; localStorage.setItem(name, val); } catch (e) { } if (name[0] != '_') { var s = {}; for (var i = 0, len = localStorage.length; i < len; ++i) { var k = localStorage.key(i); if (k[0] != '_') { s[k] = localStorage.getItem(k); } } meshserver.send({ action: 'userWebState', state: JSON.stringify(s) }); } }
function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } } function getstore(name, val) { try { if (typeof (localStorage) === 'undefined') return val; var v = localStorage.getItem(name); if ((v == null) || (v == null)) return val; return v; } catch (e) { return val; } }
//function addLink(x, f) { return "<a style=cursor:pointer;color:darkblue;text-decoration:none onclick='" + f + "'>&diams; " + x + "</a>"; } //function addLink(x, f) { return "<a style=cursor:pointer;color:darkblue;text-decoration:none onclick='" + f + "'>&diams; " + x + "</a>"; }
function addLink(x, f) { return "<span style=cursor:pointer;text-decoration:none onclick='" + f + "'>" + x + " <img class=hoverButton src=images/link5.png></span>"; } function addLink(x, f) { return "<span style=cursor:pointer;text-decoration:none onclick='" + f + "'>" + x + " <img class=hoverButton src=images/link5.png></span>"; }

View File

@ -1292,18 +1292,22 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if ((!obj.args.user) && (obj.args.nousers != true) && (nologout == false)) { logoutcontrol += ' <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>'; } // If a default user is in use or no user mode, don't display the logout button if ((!obj.args.user) && (obj.args.nousers != true) && (nologout == false)) { logoutcontrol += ' <a href=' + domain.url + 'logout?' + Math.random() + ' style=color:white>Logout</a>'; } // If a default user is in use or no user mode, don't display the logout button
var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified var httpsPort = ((obj.args.aliasport == null) ? obj.args.port : obj.args.aliasport); // Use HTTPS alias port is specified
if (obj.args.minify && !req.query.nominify) { // Fetch the web state
// Try to server the minified version if we can. obj.db.Get('ws' + user._id, function (err, states) {
try { var webstate = (states.length == 1) ? states[0].state : '';
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'default-mobile-min' : 'default-min'), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer }); if (obj.args.minify && !req.query.nominify) {
} catch (ex) { // Try to server the minified version if we can.
// In case of an exception, serve the non-minified version. try {
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'default-mobile' : 'default'), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer }); res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'default-mobile-min' : 'default-min'), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate) });
} catch (ex) {
// In case of an exception, serve the non-minified version.
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'default-mobile' : 'default'), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate) });
}
} else {
// Serve non-minified version of web pages.
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'default-mobile' : 'default'), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer, webstate: encodeURIComponent(webstate) });
} }
} else { });
// Serve non-minified version of web pages.
res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'default-mobile' : 'default'), { authCookie: authCookie, viewmode: viewmode, currentNode: currentNode, logoutControl: logoutcontrol, title: domain.title, title2: domain.title2, extitle: encodeURIComponent(domain.title), extitle2: encodeURIComponent(domain.title2), domainurl: domain.url, domain: domain.id, debuglevel: parent.debugLevel, serverDnsName: obj.getWebServerName(domain), serverRedirPort: args.redirport, serverPublicPort: httpsPort, noServerBackup: (args.noserverbackup == 1 ? 1 : 0), features: features, sessiontime: args.sessiontime, mpspass: args.mpspass, passRequirements: passRequirements, webcerthash: Buffer.from(obj.webCertificateFullHashs[domain.id], 'binary').toString('base64').replace(/\+/g, '@').replace(/\//g, '$'), footer: (domain.footer == null) ? '' : domain.footer });
}
} else { } else {
// Send back the login application // Send back the login application
// If this is a 2 factor auth request, look for a hardware key challenge. // If this is a 2 factor auth request, look for a hardware key challenge.