Improved meshcentral-web customization support.
Ylian Saint-Hilaire committed
Aug 25, 2019 at 11:08 UTC
0a60656e8de9bb9d32f541a556ae3a2dc333b2ee
2 files changed
+76
-60
meshcentral.js
+8
-4
@@ -73,16 +73,20 @@ function CreateMeshCentralServer(config, args) {
73
obj.filespath = obj.path.join(__dirname, '../../meshcentral-files');
74
obj.backuppath = obj.path.join(__dirname, '../../meshcentral-backup');
75
obj.recordpath = obj.path.join(__dirname, '../../meshcentral-recordings');
76
- if (obj.fs.existsSync(obj.path.join(__dirname, '../../meshcentral-web/views'))) { obj.webViewsPath = obj.path.join(__dirname, '../../meshcentral-web/views'); } else { obj.webViewsPath = obj.path.join(__dirname, 'views'); }
77
- if (obj.fs.existsSync(obj.path.join(__dirname, '../../meshcentral-web/public'))) { obj.webPublicPath = obj.path.join(__dirname, '../../meshcentral-web/public'); } else { obj.webPublicPath = obj.path.join(__dirname, 'public'); }
76
+ obj.webPublicPath = obj.path.join(__dirname, 'public');
77
+ obj.webViewsPath = obj.path.join(__dirname, 'views');
78
+ if (obj.fs.existsSync(obj.path.join(__dirname, '../../meshcentral-web/views'))) { obj.webViewsOverridePath = obj.path.join(__dirname, '../../meshcentral-web/views'); }
79
+ if (obj.fs.existsSync(obj.path.join(__dirname, '../../meshcentral-web/public'))) { obj.webPublicOverridePath = obj.path.join(__dirname, '../../meshcentral-web/public'); }
80
} else {
81
obj.parentpath = __dirname;
82
obj.datapath = obj.path.join(__dirname, '../meshcentral-data');
83
obj.filespath = obj.path.join(__dirname, '../meshcentral-files');
84
obj.backuppath = obj.path.join(__dirname, '../meshcentral-backups');
85
obj.recordpath = obj.path.join(__dirname, '../meshcentral-recordings');
84
- if (obj.fs.existsSync(obj.path.join(__dirname, '../meshcentral-web/views'))) { obj.webViewsPath = obj.path.join(__dirname, '../meshcentral-web/views'); } else { obj.webViewsPath = obj.path.join(__dirname, 'views'); }
85
- if (obj.fs.existsSync(obj.path.join(__dirname, '../meshcentral-web/public'))) { obj.webPublicPath = obj.path.join(__dirname, '../meshcentral-web/public'); } else { obj.webPublicPath = obj.path.join(__dirname, 'public'); }
86
+ obj.webPublicPath = obj.path.join(__dirname, 'public');
87
+ obj.webViewsPath = obj.path.join(__dirname, 'views');
88
+ if (obj.fs.existsSync(obj.path.join(__dirname, '../meshcentral-web/views'))) { obj.webViewsOverridePath = obj.path.join(__dirname, '../meshcentral-web/views'); }
89
+ if (obj.fs.existsSync(obj.path.join(__dirname, '../meshcentral-web/public'))) { obj.webPublicOverridePath = obj.path.join(__dirname, '../meshcentral-web/public'); }
90
}
91
92
// Look to see if data and/or file path is specified
webserver.js
+68
-56
@@ -1066,28 +1066,28 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1066
var idsplit = cookie.u.split('/');
1067
if ((idsplit.length != 2) || (idsplit[0] != domain.id)) {
1068
parent.debug('web', 'handleCheckMailRequest: Invalid domain.');
1069
- res.render(obj.path.join(obj.parent.webViewsPath, 'message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'ERROR: Invalid domain. <a href="' + domain.url + '">Go to login page</a>.' });
1069
+ res.render(getRenderPage('message', req), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'ERROR: Invalid domain. <a href="' + domain.url + '">Go to login page</a>.' });
1070
} else {
1071
obj.db.Get('user/' + cookie.u.toLowerCase(), function (err, docs) {
1072
if (docs.length == 0) {
1073
parent.debug('web', 'handleCheckMailRequest: Invalid username.');
1074
- res.render(obj.path.join(obj.parent.webViewsPath, 'message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'ERROR: Invalid username \"' + EscapeHtml(idsplit[1]) + '\". <a href="' + domain.url + '">Go to login page</a>.' });
1074
+ res.render(getRenderPage('message', req), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'ERROR: Invalid username \"' + EscapeHtml(idsplit[1]) + '\". <a href="' + domain.url + '">Go to login page</a>.' });
1075
} else {
1076
var user = docs[0];
1077
if (user.email != cookie.e) {
1078
parent.debug('web', 'handleCheckMailRequest: Invalid e-mail.');
1079
- res.render(obj.path.join(obj.parent.webViewsPath, 'message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'ERROR: Invalid e-mail \"' + EscapeHtml(user.email) + '\" for user \"' + EscapeHtml(user.name) + '\". <a href="' + domain.url + '">Go to login page</a>.' });
1079
+ res.render(getRenderPage('message', req), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'ERROR: Invalid e-mail \"' + EscapeHtml(user.email) + '\" for user \"' + EscapeHtml(user.name) + '\". <a href="' + domain.url + '">Go to login page</a>.' });
1080
} else {
1081
if (cookie.a == 1) {
1082
// Account email verification
1083
if (user.emailVerified == true) {
1084
parent.debug('web', 'handleCheckMailRequest: email already verified.');
1085
- res.render(obj.path.join(obj.parent.webViewsPath, 'message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'E-mail \"' + EscapeHtml(user.email) + '\" for user \"' + EscapeHtml(user.name) + '\" already verified. <a href="' + domain.url + '">Go to login page</a>.' });
1085
+ res.render(getRenderPage('message', req), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'E-mail \"' + EscapeHtml(user.email) + '\" for user \"' + EscapeHtml(user.name) + '\" already verified. <a href="' + domain.url + '">Go to login page</a>.' });
1086
} else {
1087
obj.db.GetUserWithVerifiedEmail(domain.id, user.email, function (err, docs) {
1088
if (docs.length > 0) {
1089
parent.debug('web', 'handleCheckMailRequest: email already in use.');
1090
- res.render(obj.path.join(obj.parent.webViewsPath, 'message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'E-mail \"' + EscapeHtml(user.email) + '\" already in use on a different account. Change the email address and try again. <a href="' + domain.url + '">Go to login page</a>.' });
1090
+ res.render(getRenderPage('message', req), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'E-mail \"' + EscapeHtml(user.email) + '\" already in use on a different account. Change the email address and try again. <a href="' + domain.url + '">Go to login page</a>.' });
1091
} else {
1092
parent.debug('web', 'handleCheckMailRequest: email verification success.');
1093
@@ -1102,7 +1102,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1102
obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, event);
1103
1104
// Send the confirmation page
1105
- res.render(obj.path.join(obj.parent.webViewsPath, 'message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'Verified email <b>' + EscapeHtml(user.email) + '</b> for user account <b>' + EscapeHtml(user.name) + '</b>. <a href="' + domain.url + '">Go to login page</a>.' });
1105
+ res.render(getRenderPage('message', req), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'Verified email <b>' + EscapeHtml(user.email) + '</b> for user account <b>' + EscapeHtml(user.name) + '</b>. <a href="' + domain.url + '">Go to login page</a>.' });
1106
1107
// Send a notification
1108
obj.parent.DispatchEvent([user._id], obj, { action: 'notify', value: 'Email verified:<br /><b>' + EscapeHtml(user.email) + '</b>.', nolog: 1 });
@@ -1113,7 +1113,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1113
// Account reset
1114
if (user.emailVerified != true) {
1115
parent.debug('web', 'handleCheckMailRequest: email not verified.');
1116
- res.render(obj.path.join(obj.parent.webViewsPath, 'message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'E-mail \"' + EscapeHtml(user.email) + '\" for user \"' + EscapeHtml(user.name) + '\" not verified. <a href="' + domain.url + '">Go to login page</a>.' });
1116
+ res.render(getRenderPage('message', req), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'E-mail \"' + EscapeHtml(user.email) + '\" for user \"' + EscapeHtml(user.name) + '\" not verified. <a href="' + domain.url + '">Go to login page</a>.' });
1117
} else {
1118
// Set a temporary password
1119
obj.crypto.randomBytes(16, function (err, buf) {
@@ -1138,20 +1138,20 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1138
obj.parent.DispatchEvent(['*', 'server-users', user._id], obj, event);
1139
1140
// Send the new password
1141
- res.render(obj.path.join(obj.parent.webViewsPath, 'message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: '<div>Password for account <b>' + EscapeHtml(user.name) + '</b> has been reset to:</div><div style=padding:14px;font-size:18px><b>' + EscapeHtml(newpass) + '</b></div>Login and go to the \"My Account\" tab to update your password. <a href="' + domain.url + '">Go to login page</a>.' });
1141
+ res.render(getRenderPage('message', req), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: '<div>Password for account <b>' + EscapeHtml(user.name) + '</b> has been reset to:</div><div style=padding:14px;font-size:18px><b>' + EscapeHtml(newpass) + '</b></div>Login and go to the \"My Account\" tab to update your password. <a href="' + domain.url + '">Go to login page</a>.' });
1142
parent.debug('web', 'handleCheckMailRequest: send temporary password.');
1143
}, 0);
1144
});
1145
}
1146
} else {
1147
- res.render(obj.path.join(obj.parent.webViewsPath, 'message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'ERROR: Invalid account check. <a href="' + domain.url + '">Go to login page</a>.' });
1147
+ res.render(getRenderPage('message', req), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'ERROR: Invalid account check. <a href="' + domain.url + '">Go to login page</a>.' });
1148
}
1149
}
1150
}
1151
});
1152
}
1153
} else {
1154
- res.render(obj.path.join(obj.parent.webViewsPath, 'message'), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'ERROR: Invalid account check, verification url is only valid for 30 minutes. <a href="' + domain.url + '">Go to login page</a>.' });
1154
+ res.render(getRenderPage('message', req), { title: domain.title, title2: domain.title2, title3: 'Account Verification', domainurl: domain.url, message: 'ERROR: Invalid account check, verification url is only valid for 30 minutes. <a href="' + domain.url + '">Go to login page</a>.' });
1155
}
1156
}
1157
}
@@ -1173,7 +1173,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1173
var installflags = cookie.f;
1174
if (typeof installflags != 'number') { installflags = 0; }
1175
parent.debug('web', 'handleAgentInviteRequest using cookie.');
1176
- res.render(obj.path.join(obj.parent.webViewsPath, 'agentinvite'), { title: domain.title, title2: domain.title2, domainurl: domain.url, meshid: mesh._id.split('/')[2], serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: ((args.notls == true) ? '0' : '1'), servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name), installflags: installflags });
1176
+ res.render(getRenderPage('agentinvite', req), { title: domain.title, title2: domain.title2, domainurl: domain.url, meshid: mesh._id.split('/')[2], serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: ((args.notls == true) ? '0' : '1'), servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name), installflags: installflags });
1177
} else if (req.query.m != null) {
1178
// The MeshId is specified in the query string, use that
1179
var mesh = obj.meshes['mesh/' + domain.id + '/' + req.query.m.toLowerCase()];
@@ -1182,7 +1182,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1182
if (req.query.f) { installflags = parseInt(req.query.f); }
1183
if (typeof installflags != 'number') { installflags = 0; }
1184
parent.debug('web', 'handleAgentInviteRequest using meshid.');
1185
- res.render(obj.path.join(obj.parent.webViewsPath, 'agentinvite'), { title: domain.title, title2: domain.title2, domainurl: domain.url, meshid: mesh._id.split('/')[2], serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: ((args.notls == true) ? '0' : '1'), servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name), installflags: installflags });
1185
+ res.render(getRenderPage('agentinvite', req), { title: domain.title, title2: domain.title2, domainurl: domain.url, meshid: mesh._id.split('/')[2], serverport: ((args.aliasport != null) ? args.aliasport : args.port), serverhttps: ((args.notls == true) ? '0' : '1'), servernoproxy: ((domain.agentnoproxy === true) ? '1' : '0'), meshname: encodeURIComponent(mesh.name), installflags: installflags });
1186
}
1187
}
1188
@@ -1494,18 +1494,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1494
parent.debug('web', 'handleRootRequestEx: success.');
1495
obj.db.Get('ws' + user._id, function (err, states) {
1496
var webstate = (states.length == 1) ? states[0].state : '';
1497
- if (obj.args.minify && !req.query.nominify) {
1498
- // Try to server the minified version if we can.
1499
- try {
1500
- 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) });
1501
- } catch (ex) {
1502
- // In case of an exception, serve the non-minified version.
1503
- 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) });
1504
- }
1505
- } else {
1506
- // Serve non-minified version of web pages.
1507
- 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) });
1508
- }
1497
+ res.render(getRenderPage('default', req), { 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) });
1498
});
1499
} else {
1500
// Send back the login application
@@ -1572,27 +1561,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1561
var newAccountsAllowed = true;
1562
if ((domain.newaccounts !== 1) && (domain.newaccounts !== true)) { for (var i in obj.users) { if (obj.users[i].domain == domain.id) { newAccountsAllowed = false; break; } } }
1563
1575
- if (obj.args.minify && !req.query.nominify) {
1576
- // Try to server the minified version if we can.
1577
- try {
1578
- res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile-min' : 'login-min'), { loginmode: loginmode, rootCertLink: getRootCertLink(), domainurl: domain.url, title: domain.title, title2: domain.title2, newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext?encodeURIComponent(domain.welcometext):null });
1579
- } catch (ex) {
1580
- // In case of an exception, serve the non-minified version.
1581
- res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), domainurl: domain.url, title: domain.title, title2: domain.title2, newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null });
1582
- }
1583
- } else {
1584
- // Serve non-minified version of web pages.
1585
- res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login'), { loginmode: loginmode, rootCertLink: getRootCertLink(), domainurl: domain.url, title: domain.title, title2: domain.title2, newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null });
1586
- }
1587
-
1588
- /*
1589
- var xoptions = { loginmode: loginmode, rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: obj.parent.mailserver != null, features: features, footer: (domain.footer == null) ? '' : domain.footer };
1590
- var xpath = obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'login-mobile' : 'login');
1591
- console.log('Render...');
1592
- res.render(xpath, xoptions, function (err, html) {
1593
- console.log(err, html);
1594
- });
1595
- */
1564
+ // Render the login page
1565
+ res.render(getRenderPage('login', req), { loginmode: loginmode, rootCertLink: getRootCertLink(), domainurl: domain.url, title: domain.title, title2: domain.title2, newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), message: message, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext) : null });
1566
}
1567
1568
// Return true if it looks like we are using a real TLS certificate.
@@ -1632,9 +1602,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1602
var user = obj.users[req.session.userid];
1603
var logoutcontrol = 'Welcome ' + user.name + '.';
1604
if ((domain.ldap == null) && (domain.sspi == null) && (obj.args.user == null) && (obj.args.nousers != true)) { 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
1635
- res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'terms-mobile' : 'terms'), { title: domain.title, title2: domain.title2, domainurl: domain.url, terms: encodeURIComponent(parent.configurationFiles['terms.txt'].toString()), logoutControl: logoutcontrol });
1605
+ res.render(getRenderPage('terms', req), { title: domain.title, title2: domain.title2, domainurl: domain.url, terms: encodeURIComponent(parent.configurationFiles['terms.txt'].toString()), logoutControl: logoutcontrol });
1606
} else {
1637
- res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'terms-mobile' : 'terms'), { title: domain.title, title2: domain.title2, domainurl: domain.url, terms: encodeURIComponent(parent.configurationFiles['terms.txt'].toString()) });
1607
+ res.render(getRenderPage('terms', req), { title: domain.title, title2: domain.title2, domainurl: domain.url, terms: encodeURIComponent(parent.configurationFiles['terms.txt'].toString()) });
1608
}
1609
} else {
1610
// See if there is a terms.txt file in meshcentral-data
@@ -1650,9 +1620,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1620
var user = obj.users[req.session.userid];
1621
var logoutcontrol = 'Welcome ' + user.name + '.';
1622
if ((domain.ldap == null) && (domain.sspi == null) && (obj.args.user == null) && (obj.args.nousers != true)) { 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
1653
- res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'terms-mobile' : 'terms'), { title: domain.title, title2: domain.title2, domainurl: domain.url, terms: encodeURIComponent(data), logoutControl: logoutcontrol });
1623
+ res.render(getRenderPage('terms', req), { title: domain.title, title2: domain.title2, domainurl: domain.url, terms: encodeURIComponent(data), logoutControl: logoutcontrol });
1624
} else {
1655
- res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'terms-mobile' : 'terms'), { title: domain.title, title2: domain.title2, domainurl: domain.url, terms: encodeURIComponent(data) });
1625
+ res.render(getRenderPage('terms', req), { title: domain.title, title2: domain.title2, domainurl: domain.url, terms: encodeURIComponent(data) });
1626
}
1627
});
1628
} else {
@@ -1664,9 +1634,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1634
var user = obj.users[req.session.userid];
1635
var logoutcontrol = 'Welcome ' + user.name + '.';
1636
if ((domain.ldap == null) && (domain.sspi == null) && (obj.args.user == null) && (obj.args.nousers != true)) { 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
1667
- res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'terms-mobile' : 'terms'), { title: domain.title, title2: domain.title2, domainurl: domain.url, logoutControl: logoutcontrol });
1637
+ res.render(getRenderPage('terms', req), { title: domain.title, title2: domain.title2, domainurl: domain.url, logoutControl: logoutcontrol });
1638
} else {
1669
- res.render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'terms-mobile' : 'terms'), { title: domain.title, title2: domain.title2, domainurl: domain.url });
1639
+ res.render(getRenderPage('terms', req), { title: domain.title, title2: domain.title2, domainurl: domain.url });
1640
}
1641
}
1642
}
@@ -1681,7 +1651,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1651
var webRtcConfig = null;
1652
if (obj.parent.config.settings && obj.parent.config.settings.webrtconfig && (typeof obj.parent.config.settings.webrtconfig == 'object')) { webRtcConfig = encodeURIComponent(JSON.stringify(obj.parent.config.settings.webrtconfig)); }
1653
res.set({ 'Cache-Control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Expires': '0' });
1684
- res.render(obj.path.join(obj.parent.webViewsPath, 'messenger'), { webrtconfig: webRtcConfig, domainurl: domain.url });
1654
+ res.render(getRenderPage('messenger', req), { webrtconfig: webRtcConfig, domainurl: domain.url });
1655
}
1656
1657
// Returns the server root certificate encoded in base64
@@ -1817,10 +1787,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
1787
}
1788
try { res.sendFile(obj.path.resolve(__dirname, path)); } catch (e) { res.sendStatus(404); }
1789
} else {
1820
- res.render(obj.path.join(obj.parent.webViewsPath, 'download'), { rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, domainurl: domain.url, message: "<a href='" + req.path + "?download=1'>" + filename + "</a>, " + stat.size + " byte" + ((stat.size < 2) ? '' : 's') + "." });
1790
+ res.render(getRenderPage('download', req), { rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, domainurl: domain.url, message: "<a href='" + req.path + "?download=1'>" + filename + "</a>, " + stat.size + " byte" + ((stat.size < 2) ? '' : 's') + "." });
1791
}
1792
} else {
1823
- res.render(obj.path.join(obj.parent.webViewsPath, 'download'), { rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, domainurl: domain.url, message: "Invalid file link, please check the URL again." });
1793
+ res.render(getRenderPage('download', req), { rootCertLink: getRootCertLink(), title: domain.title, title2: domain.title2, domainurl: domain.url, message: "Invalid file link, please check the URL again." });
1794
}
1795
}
1796
@@ -3260,7 +3230,10 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3230
3231
obj.app.get(url + 'stop', function (req, res) { res.send('Stopping Server, <a href="' + url + '">click here to login</a>.'); setTimeout(function () { parent.Stop(); }, 500); });
3232
3263
- // Indicates to ExpressJS that the public folder should be used to serve static files.
3233
+ // Indicates to ExpressJS that the override public folder should be used to serve static files.
3234
+ if (obj.parent.webPublicOverridePath != null) { obj.app.use(url, obj.express.static(obj.parent.webPublicOverridePath, { maxAge: '1h' })); }
3235
+
3236
+ // Indicates to ExpressJS that the default public folder should be used to serve static files.
3237
obj.app.use(url, obj.express.static(obj.parent.webPublicPath, { maxAge: '1h' }));
3238
3239
// Start regular disconnection list flush every 2 minutes.
@@ -3272,7 +3245,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3245
obj.app.use(function (req, res, next) {
3246
parent.debug('web', '404 Error ' + req.url);
3247
var domain = getDomain(req);
3275
- res.status(404).render(obj.path.join(obj.parent.webViewsPath, isMobileBrowser(req) ? 'error404-mobile' : 'error404'), { title: domain.title, title2: domain.title2 });
3248
+ res.status(404).render(getRenderPage('error404', req), { title: domain.title, title2: domain.title2 });
3249
});
3250
}
3251
@@ -3596,6 +3569,45 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
3569
return user2;
3570
}
3571
3572
+ // Return the correct render page given mobile, minify and override path.
3573
+ function getRenderPage(pagename, req) {
3574
+ var mobile = isMobileBrowser(req), minify = obj.args.minify && !req.query.nominify, p;
3575
+ if (mobile) {
3576
+ if (obj.parent.webViewsOverridePath != null) {
3577
+ if (minify) {
3578
+ p = obj.path.join(obj.parent.webViewsOverridePath, pagename + '-mobile-min');
3579
+ if (obj.fs.existsSync(p + '.handlebars')) { return p; } // Mobile + Minify + Override document
3580
+ } else {
3581
+ p = obj.path.join(obj.parent.webViewsOverridePath, pagename + '-mobile');
3582
+ if (obj.fs.existsSync(p + '.handlebars')) { return p; } // Mobile + Override document
3583
+ }
3584
+ }
3585
+ if (minify) {
3586
+ p = obj.path.join(obj.parent.webViewsPath, pagename + '-mobile-min');
3587
+ if (obj.fs.existsSync(p + '.handlebars')) { return p; } // Mobile + Minify document
3588
+ } else {
3589
+ p = obj.path.join(obj.parent.webViewsPath, pagename + '-mobile');
3590
+ if (obj.fs.existsSync(p + '.handlebars')) { return p; } // Mobile document
3591
+ }
3592
+ }
3593
+ if (obj.parent.webViewsOverridePath != null) {
3594
+ if (minify) {
3595
+ p = obj.path.join(obj.parent.webViewsOverridePath, pagename + '-min');
3596
+ if (obj.fs.existsSync(p + '.handlebars')) { return p; } // Minify + Override document
3597
+ } else {
3598
+ p = obj.path.join(obj.parent.webViewsOverridePath, pagename);
3599
+ if (obj.fs.existsSync(p + '.handlebars')) { return p; } // Override document
3600
+ }
3601
+ }
3602
+ if (minify) {
3603
+ p = obj.path.join(obj.parent.webViewsPath, pagename + '-min');
3604
+ if (obj.fs.existsSync(p + '.handlebars')) { return p; } // Minify document
3605
+ } else {
3606
+ p = obj.path.join(obj.parent.webViewsPath, pagename);
3607
+ if (obj.fs.existsSync(p + '.handlebars')) { return p; } // Default document
3608
+ }
3609
+ }
3610
+
3611
// Return true if a mobile browser is detected.
3612
// This code comes from "http://detectmobilebrowsers.com/" and was modified, This is free and unencumbered software released into the public domain. For more information, please refer to the http://unlicense.org/
3613
function isMobileBrowser(req) {