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) {
if (obj.FailAllError != 0) { if (obj.FailAllError != 999) { obj.gotNextMessagesError({ status: obj.FailAllError }, 'error', null, [postdata, callback, tag]); } return; }
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
if (obj.digest == null)
@ -92,9 +92,9 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
//console.log('Request ' + (obj.RequestCount++));
req.on('error', function (e) { obj.gotNextMessagesError({ status: 600 }, 'error', null, [postdata, callback, tag]); });
req.on('response', function (response) {
//console.log('Response: ' + response.statusCode);
if (globalDebugFlags & 1) { console.log('Response: ' + response.statusCode); }
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]);
} else {
response.acc = '';
@ -116,7 +116,7 @@ function CreateWsmanComm(/*host, port, user, pass, tls, extra*/)
obj.gotNextMessages = function (data, status, request, callArgs) {
obj.ActiveAjaxCount--;
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 (request.status != 200) { callArgs[1](null, request.status, callArgs[2]); return; }
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) {
if (docs.length > 0) {
// 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 {
// Update the user's 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('nt' + deluser._id);
db.Remove('ws' + deluser._id); // Remove user web state
db.Remove('nt' + deluser._id); // Remove notes for this user
// Delete all files on the server for this account
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
// 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
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
// 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
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) {
if (err) {
// Send user notification of error
displayNotificationMessage('Error, password not changed.');
displayNotificationMessage('Error, password not changed.', 'Account Settings', 'ServerNotify');
} else {
// Change the password
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 });
// Send user notification of password change
displayNotificationMessage('Password changed.');
displayNotificationMessage('Password changed.', 'Account Settings', 'ServerNotify');
}
});
} else {
// Send user notification of error
displayNotificationMessage('Current password not correct.');
displayNotificationMessage('Current password not correct.', 'Account Settings', 'ServerNotify');
}
});
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;
// 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
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
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
@ -2297,6 +2297,12 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
});
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':
{
// 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.
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.
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;
}
#p12warning, #p12warning2, #p14warning, #p14warning2 {
#p12warning, #p12warning2, #p11warning, #p11warning2 {
max-width: 100%;
display: none;
cursor: pointer;

View File

@ -257,6 +257,7 @@
<p><strong>Account actions</strong></p>
<p class="mL">
<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_showChangePassword()">Change password</a><span id="p2nextPasswordUpdateTime"></span><br />
<a onclick="account_showDeleteAccount()">Delete account</a><br />
@ -415,19 +416,18 @@
<h1>Desktop - <span id=p11deviceName></span></h1>
</div>
</div>
<div id="p14warning" onclick="showFeaturesDlg()">
<div id="p11warning" onclick="showFeaturesDlg()">
<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 id="p14warning2" onclick="showPowerActionDlg()">
<div id="p11warning2" onclick="showPowerActionDlg()">
<div class="icon2"></div>
<div class="warningbox">Remote computer is not powered on, click here to issue a power command.</div>
</div>
<div id=deskarea0 cellpadding=0 cellspacing=0>
<div id=deskarea1 class="areaHead">
<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="Rotate Left" onclick="drotate(-1)">&olarr;</div>
<div class='deskareaicon' title="Rotate Right" onclick="drotate(1)">&orarr;</div>
@ -498,7 +498,7 @@
</div>
<div id="p12warning" onclick=showFeaturesDlg()>
<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 id="p12warning2" onclick=showPowerActionDlg()>
<div class="icon2"></div>
@ -871,6 +871,13 @@
</div>
<script type="text/javascript">
'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 autoReconnect = true;
var powerStatetable = ['', 'Powered', 'Sleep', 'Sleep', 'Sleep', 'Hibernating', 'Power off', 'Present'];
@ -985,11 +992,11 @@
// Setup page controls
Q('sortselect').selectedIndex = sort = getstore("sort", 0);
Q('sizeselect').selectedIndex = getstore("viewsize", 1);
Q('SearchInput').value = getstore("search", "");
Q('sizeselect').selectedIndex = getstore("_viewsize", 1);
Q('SearchInput').value = getstore("_search", "");
showRealNames = (getstore("showRealNames", 0) == 1);
Q('RealNameCheckBox').checked = showRealNames;
Q('viewselect').value = getstore("deviceView", 1);
Q('viewselect').value = getstore("_deviceView", 1);
Q('DeskControl').checked = (getstore('DeskControl', 1) == 1);
// Display the page devices
@ -1198,7 +1205,7 @@
updateNaggleTimer = setTimeout(function () {
if (updateNaggleFlags & 512) { center(); }
if (updateNaggleFlags & 1) { onSearchInputChanged(); }
if (updateNaggleFlags & 2) { onSortSelectChange(true); }
if (updateNaggleFlags & 2) { onSortSelectChange(false); }
if (updateNaggleFlags & 128) { updateMeshes(); }
if (updateNaggleFlags & 4) { updateDevices(); }
if (updateNaggleFlags & 8) { drawNotifications(); }
@ -1345,7 +1352,7 @@
// Node was found, dispatch the 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.
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.tag != null) { n.tag = message.tag; }
if (message.username != null) { n.username = message.username; }
@ -1362,7 +1369,7 @@
}
} 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.tag != null) { n.tag = message.tag; }
if (message.username != null) { n.username = message.username; }
addNotification(n);
@ -1606,6 +1613,24 @@
masterUpdate(32);
}
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 'accountcreate':
case 'accountchange': {
@ -1904,7 +1929,7 @@
break;
}
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; }
addNotification(n);
break;
@ -1939,7 +1964,7 @@
function onRealNameCheckBox() {
showRealNames = Q('RealNameCheckBox').checked;
putstore("showRealNames", showRealNames ? 1 : 0);
masterUpdate(6)
masterUpdate(6);
return;
}
@ -1947,8 +1972,8 @@
if (i != null) { Q('viewselect').value = i; }
for (var j = 1; j < 5; j++) { Q('devViewButton' + j).classList.remove('viewSelectorSel'); }
Q('devViewButton' + Q('viewselect').value).classList.add('viewSelectorSel');
putstore("deviceView", Q('viewselect').value);
putstore("viewsize", Q('sizeselect').value);
putstore("_deviceView", Q('viewselect').value);
putstore("_viewsize", Q('sizeselect').value);
masterUpdate(4);
setTimeout("masterUpdate(512)", 200);
}
@ -2873,7 +2898,7 @@
function onConsoleFocus(x) { consoleFocus = x; }
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;
if (x.startsWith('user:')) { userSearch = x.substring(5); }
else if (x.startsWith('u:')) { userSearch = x.substring(2); }
@ -3617,7 +3642,6 @@
var powerTimeline = null;
function getCurrentNode() { return currentNode; };
function gotoDevice(nodeid, panel, refresh, event) {
// 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; }
@ -5760,6 +5784,10 @@
meshserver.send({ action: 'otp-hkey-get' });
}
function account_enableNotifications() {
if (Notification) { Notification.requestPermission().then(function (permission) { QV('accountEnableNotificationsSpan', permission != "granted"); }); }
}
function account_showVerifyEmail() {
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.";
@ -7328,20 +7356,21 @@
function notificationSelected(id) {
var j = -1;
for (var i in notifications) { if (notifications[i].id == id) { j = i; } }
if (j != -1) {
var n = notifications[j];
if (n.nodeid != null) {
if (n.tag == 'desktop') gotoDevice(n.nodeid, 12); // Desktop
else if (n.tag == 'terminal') gotoDevice(n.nodeid, 11); // Terminal
else if (n.tag == 'files') gotoDevice(n.nodeid, 13); // Files
else if (n.tag == 'intelamt') gotoDevice(n.nodeid, 14); // Intel AMT
else if (n.tag == 'console') gotoDevice(n.nodeid, 15); // Files
else gotoDevice(n.nodeid, 10); // General
} else {
if (n.tag.startsWith('meshmessenger/')) {
window.open('/messenger?id=' + n.tag + '&title=' + encodeURIComponent(n.username), n.tag.split('/')[2]);
notificationDelete(id);
}
if (j != -1) { notificationSelectedEx(notifications[j], id); }
}
function notificationSelectedEx(n, id) {
if (n.nodeid != null) {
if (n.tag == 'desktop') gotoDevice(n.nodeid, 12); // Desktop
else if (n.tag == 'terminal') gotoDevice(n.nodeid, 11); // Terminal
else if (n.tag == 'files') gotoDevice(n.nodeid, 13); // Files
else if (n.tag == 'intelamt') gotoDevice(n.nodeid, 14); // Intel AMT
else if (n.tag == 'console') gotoDevice(n.nodeid, 15); // Files
else gotoDevice(n.nodeid, 10); // General
} else {
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
function addNotification(n) {
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);
// If web notifications are granted, use it.
if (Notification && (Notification.permission == "granted")) {
var notification;
if (n.nodeid) {
var node = getNodeFromId(n.nodeid);
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
@ -7688,6 +7734,9 @@
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.
if (x == 115) { QV('p15', true); }
QV('p15uploadCore', x != 115);
@ -7696,6 +7745,9 @@
if (x == 1) masterUpdate(4);
// Setup web notifications
if ((x == 2) && Notification) { QV('accountEnableNotificationsSpan', Notification.permission != 'granted'); }
// Fetch the server timeline stats if needed
if ((x == 40) && (serverTimelineStats == null)) { refreshServerTimelineStats(); }
@ -7705,7 +7757,7 @@
// 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 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 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>"; }

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
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) {
// Try to server the minified version if we can.
try {
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 });
} 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 });
// Fetch the web state
obj.db.Get('ws' + user._id, function (err, states) {
var webstate = (states.length == 1) ? states[0].state : '';
if (obj.args.minify && !req.query.nominify) {
// Try to server the minified version if we can.
try {
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 {
// Send back the login application
// If this is a 2 factor auth request, look for a hardware key challenge.