Improved Google Drive autobackup.

This commit is contained in:
Ylian Saint-Hilaire 2020-08-21 11:47:34 -07:00
parent edd018e35a
commit bf87bbd4a3
21 changed files with 973 additions and 769 deletions

42
db.js
View File

@ -1385,10 +1385,10 @@ module.exports.CreateDB = function (parent, func) {
}
// Perform cloud backup
obj.performCloudBackup = function(filename) {
if (parent.config.settings.autobackup.googledrive != true) return;
obj.performCloudBackup = function (filename) {
if (typeof parent.config.settings.autobackup.googledrive != 'object') return;
obj.Get('GoogleDriveBackup', function (err, docs) {
if ((err != null) || (docs.length != 1) || (docs[0].state == 3)) return;
if ((err != null) || (docs.length != 1) || (docs[0].state != 3)) return;
const {google} = require('googleapis');
const oAuth2Client = new google.auth.OAuth2(docs[0].clientid, docs[0].clientsecret, "urn:ietf:wg:oauth:2.0:oob");
oAuth2Client.on('tokens', function(tokens) { if (tokens.refresh_token) { docs[0].token = tokens.refresh_token; parent.db.Set(docs[0]); } }); // Update the token in the database
@ -1398,36 +1398,44 @@ module.exports.CreateDB = function (parent, func) {
// Called once we know our folder id, clean up and upload a backup.
var useGoogleDrive = function (folderid) {
// List files to see if we need to delete some
drive.files.list({
q: 'trashed = false and \'' + folderid + '\' in parents',
fields: 'nextPageToken, files(id, name, size, createdTime)',
}, function (err, res) {
if (err) { console.log('GoogleDrive error: ' + err); return; }
// Delete any old files if more than 10 files are present in the backup folder.
res.data.files.sort(createdTimeSort);
while (res.data.files.length > 10) { drive.files.delete({ fileId: res.data.files.shift().id }, function (err, res) { }); }
});
// List files to see if we need to delete older ones
if (typeof parent.config.settings.autobackup.googledrive.maxfiles == 'number') {
drive.files.list({
q: 'trashed = false and \'' + folderid + '\' in parents',
fields: 'nextPageToken, files(id, name, size, createdTime)',
}, function (err, res) {
if (err) { console.log('GoogleDrive (files.list) error: ' + err); return; }
// Delete any old files if more than 10 files are present in the backup folder.
res.data.files.sort(createdTimeSort);
while (res.data.files.length >= parent.config.settings.autobackup.googledrive.maxfiles) { drive.files.delete({ fileId: res.data.files.shift().id }, function (err, res) { }); }
});
}
//console.log('Uploading...');
// Upload the backup
drive.files.create({
requestBody: { name: require('path').basename(filename), mimeType: 'text/plain', parents: [folderid] },
media: { mimeType: 'application/zip', body: require('fs').createReadStream(filename) },
}, function (err, res) {
if (err) { console.log('GoogleDrive error: ' + err); return; }
if (err) { console.log('GoogleDrive (files.create) error: ' + err); return; }
//console.log('Upload done.');
});
}
// Fetch the folder name
var folderName = 'MeshCentral-Backups';
if (typeof parent.config.settings.autobackup.googledrive.foldername == 'string') { folderName = parent.config.settings.autobackup.googledrive.foldername; }
// Find our backup folder, create one if needed.
drive.files.list({
q: 'mimeType = \'application/vnd.google-apps.folder\' and name=\'MeshCentral-Backups\' and trashed = false',
q: 'mimeType = \'application/vnd.google-apps.folder\' and name=\'' + folderName + '\' and trashed = false',
fields: 'nextPageToken, files(id, name)',
}, function (err, res) {
if (err) { console.log('GoogleDrive error: ' + err); return; }
if (res.data.files.length == 0) {
// Create a folder
drive.files.create({ resource: { 'name': 'MeshCentral-Backups', 'mimeType': 'application/vnd.google-apps.folder' }, fields: 'id' }, function (err, file) {
if (err) { console.log('GoogleDrive error: ' + err); return; }
drive.files.create({ resource: { 'name': folderName, 'mimeType': 'application/vnd.google-apps.folder' }, fields: 'id' }, function (err, file) {
if (err) { console.log('GoogleDrive (folder.create) error: ' + err); return; }
useGoogleDrive(file.data.id);
});
} else { useGoogleDrive(res.data.files[0].id); }

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Email Verification</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b>
</td>
</tr>
</tbody></table>
<p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting email verification, click on the following link to complete the process.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Click here to verify your e-mail address.</a>
</p>
If you did not initiate this request, please ignore this mail.
</div></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - Email Verification
Hi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is performing an e-mail verification. Nagivate to the following link to complete the process:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
If you did not initiate this request, please ignore this mail.

View File

@ -0,0 +1,19 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Account Invitation</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Account Invitation</b>
</td>
</tr>
</tbody></table>
<p>An account was created for you on server <a href="[[[SERVERURL]]]" notrans="1">[[[SERVERNAME]]]</a>, you can access it now with:</p>
<p>
&nbsp;&nbsp;&nbsp;Username: <b notrans="1">[[[ACCOUNTNAME]]]</b><br>
&nbsp;&nbsp;&nbsp;Password: <b notrans="1">[[[PASSWORD]]]</b>
</p>
Best regards,
<br>
[[[USERNAME]]]
<br>
</div></body></html>

View File

@ -0,0 +1,5 @@
[[[SERVERNAME]]] - Account Invitation
An account was created for you on server [[[SERVERNAME]]] ([[[SERVERURL]]]/), you can access it now with username "[[[ACCOUNTNAME]]]" and password "[[[PASSWORD]]]".
~
Best regards,
~[[[USERNAME]]]

View File

@ -0,0 +1,12 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Account Login</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Account Login</b>
</td>
</tr>
</tbody></table>
<p>Your login token is: [[[TOKEN]]]</p>
<p>This token can only be used once and is valid for 5 minutes.</p>
</div></body></html>

View File

@ -0,0 +1,4 @@
[[[SERVERNAME]]] - Account Login
Your login token is: [[[TOKEN]]]
~
This token can only be used once and is valid for 5 minutes.

View File

@ -0,0 +1,15 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Account Reset</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Verification</b>
</td>
</tr>
</tbody></table>
<p>Hi [[[USERNAME]]], <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting an account password reset, click on the following link to complete the process.</p>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]">Click here to reset your account password.</a>
</p>
If you did not initiate this request, please ignore this mail.
</div></body></html>

View File

@ -0,0 +1,6 @@
[[[SERVERNAME]]] - Account Reset
Hi [[[USERNAME]]], [[[SERVERNAME]]] ([[[SERVERURL]]]) is requesting an account password reset. Nagivate to the following link to complete the process:
~
~[[[SERVERURL]]]/checkmail?c=[[[COOKIE]]]
~
If you did not initiate this request, please ignore this mail.

View File

@ -0,0 +1,42 @@
<html><head></head><body><div>[[[SERVERNAME]]] - Invitation</div>
<div style="font-family:Arial,Helvetica,sans-serif">
<table style="background-color:#003366;color:lightgray;width:100%" cellpadding="8">
<tbody><tr>
<td>
<b style="font-size:20px;font-family:Arial,Helvetica,sans-serif">[[[SERVERNAME]]] - Agent Installation</b>
</td>
</tr>
</tbody></table>
<area-name>
<p>
Hello [[[NAME]]],
</p>
</area-name>
<p>User [[[USERNAME]]] on server <a href="[[[SERVERURL]]]">[[[SERVERNAME]]]</a> is requesting you to install software to start a remote control session.</p>
<area-msg>
<p>
Message: <b notrans="1">[[[MSG]]]</b>
</p>
</area-msg>
<area-windows>
<p style="margin-left:30px">
<a href="[[[SERVERURL]]]/meshagents?id=3&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;installflags=[[[INSTALLFLAGS]]]">Click here to download the MeshAgent for Windows.</a>
</p>
</area-windows>
<area-osx>
<p style="margin-left:30px"><a href="[[[SERVERURL]]]/meshagents?id=16&amp;meshid=[[[MESHIDHEX]]]&amp;tag=mailto:[[[EMAIL]]]&amp;installflags=[[[INSTALLFLAGS]]]">Click here to download the MeshAgent for Apple OSX.</a></p>
</area-osx>
<area-linux>
<p>
For Linux, cut &amp; paste the following in a terminal to install the agent:<br>
</p><pre style="margin-left:30px" notrans="1">wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh &amp;&amp; chmod 755 ./meshinstall.sh &amp;&amp; sudo ./meshinstall.sh [[[SERVERURL]]] \'[[[MESHIDHEX]]]\'</pre>
<p></p>
</area-linux>
<area-link>
<p>
To install the software, <a href="[[[SERVERURL]]][[[LINKURL]]]">click here</a> and follow the instructions.
</p>
</area-link>
<p>If you did not initiate this request, please ignore this mail.</p>
Best regards,<br>[[[USERNAME]]]<br>
</div></body></html>

View File

@ -0,0 +1,35 @@
[[[SERVERNAME]]] - Invitation
~<area-name>
Hello [[[NAME]]],
~</area-name>
User [[[USERNAME]]] on server [[[SERVERNAME]]] ([[[SERVERURL]]]/) is requesting you install software to start the remote control session.
~<area-msg>
~
Message: [[[MSG]]]
~
~</area-msg>
~<area-windows>
For Windows, nagivate to the following link to complete the process:
~
~[[[SERVERURL]]]/meshagents?id=3&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]
~
~</area-windows>
~<area-osx>
For Apple OSX, nagivate to the following link to complete the process:
~
~[[[SERVERURL]]]/meshagents?id=16&meshid=[[[MESHIDHEX]]]&tag=mailto:[[[EMAIL]]]&installflags=[[[INSTALLFLAGS]]]
~
~</area-osx>
~<area-linux>
For Linux, cut & paste the following in a terminal to install the agent:
~
~wget -q "[[[SERVERURL]]]/meshagents?script=1" --no-check-certificate -O ./meshinstall.sh && chmod 755 ./meshinstall.sh && sudo ./meshinstall.sh [[[SERVERURL]]] '[[[MESHIDHEX]]]'
~
~</area-linux>
~<area-link>
To install the software, navigate to [[[SERVERURL]]][[[LINKURL]]] and follow the instructions.
~</area-link>
If you did not initiate this request, please ignore this mail.
~
Best regards,
~[[[USERNAME]]]

View File

@ -0,0 +1,2 @@
[[0]] verification code is: [[1]]
[[0]] access token is: [[1]]

View File

@ -1397,13 +1397,13 @@ module.exports.CreateMeshAgent = function (parent, db, ws, req, args, domain) {
// Indicates if the agent has a coredump available
if ((command.exists === true) && (typeof command.agenthashhex == 'string') && (command.agenthashhex.length == 96)) {
// Check if we already have this exact dump file
const coreDumpFile = parent.path.join(parent.parent.datapath, 'coredumps', obj.agentInfo.agentId + '-' + command.agenthashhex + '-' + obj.nodeid + '.dmp');
const coreDumpFile = parent.path.join(parent.parent.datapath, '..', 'meshcentral-coredumps', obj.agentInfo.agentId + '-' + command.agenthashhex + '-' + obj.nodeid + '.dmp');
parent.fs.stat(coreDumpFile, function (err, stats) {
if (stats != null) return;
obj.coreDumpPresent = true;
// Check how many files are in the coredumps folder
const coreDumpPath = parent.path.join(parent.parent.datapath, 'coredumps');
const coreDumpPath = parent.path.join(parent.parent.datapath, '..', 'meshcentral-coredumps');
parent.fs.readdir(coreDumpPath, function (err, files) {
if ((files != null) && (files.length >= 20)) return; // Don't get more than 20 core dump files.

View File

@ -107,7 +107,15 @@
"backupIntervalHours": { "type": "integer" },
"keepLastDaysBackup": { "type": "integer" },
"zipPassword": { "type": "string" },
"backupPath": { "type": "string" }
"backupPath": { "type": "string" },
"googleDrive": {
"type": "object",
"description": "Enabled automated upload of the server backups to a Google Drive account, once enabled you need to go in \"My Server\" tab as administrator to associate the account.",
"properties": {
"folderName": { "type": "integer", "default": "MeshCentral-Backups", "description": "The name of the folder to create in the Google Drive account." },
"maxFiles": { "type": "string", "default": null, "description": "The maximum number of files to keep in the Google Drive folder, older files will be removed if needed." }
}
}
}
},
"redirects": { "type": "object" },

View File

@ -648,11 +648,11 @@ function CreateMeshCentralServer(config, args) {
if (typeof obj.args.trustedproxy == 'string') { obj.args.trustedproxy = obj.args.trustedproxy.split(' ').join('').split(','); }
if (typeof obj.args.tlsoffload == 'string') { obj.args.tlsoffload = obj.args.tlsoffload.split(' ').join('').split(','); }
// Check if WebSocket compression is supported. It's broken in NodeJS v11.11 to v12.15
// Check if WebSocket compression is supported. It's known to be broken in NodeJS v11.11 to v12.15, and v13.2
const verSplit = process.version.substring(1).split('.');
var ver = parseInt(verSplit[0]) + (parseInt(verSplit[1]) / 100);
if ((ver >= 11.11) && (ver <= 12.15)) {
if ((obj.args.wscompression === true) || (obj.args.agentwscompression === true)) { addServerWarning('WebSocket compression is disabled, this feature is broken in NodeJS v11.11 to v12.15.'); }
if (((ver >= 11.11) && (ver <= 12.15)) || (ver == 13.2)) {
if ((obj.args.wscompression === true) || (obj.args.agentwscompression === true)) { addServerWarning('WebSocket compression is disabled, this feature is broken in NodeJS v11.11 to v12.15 and v13.2'); }
obj.args.wscompression = obj.args.agentwscompression = false;
obj.config.settings.wscompression = obj.config.settings.agentwscompression = false;
}
@ -2708,7 +2708,7 @@ function mainStart() {
// Setup encrypted zip support if needed
if (config.settings.autobackup && config.settings.autobackup.zippassword) {
modules.push('archiver-zip-encrypted');
if (config.settings.autobackup.googledrive == true) { if (nodeVersion >= 8) { modules.push('googleapis'); } else { config.settings.autobackup.googledrive = false; } } // Enable Google Drive Support
if (typeof config.settings.autobackup.googledrive == 'object') { if (nodeVersion >= 8) { modules.push('googleapis'); } else { delete config.settings.autobackup.googledrive; } } // Enable Google Drive Support
}
// Setup common password blocking

View File

@ -446,7 +446,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
}
// If we are site administrator and Google Drive backup is setup, send out the status.
if ((user.siteadmin === SITERIGHT_ADMIN) && (domain.id == '') && (typeof parent.parent.config.settings.autobackup == 'object') && (parent.parent.config.settings.autobackup.googledrive == true)) {
if ((user.siteadmin === SITERIGHT_ADMIN) && (domain.id == '') && (typeof parent.parent.config.settings.autobackup == 'object') && (typeof parent.parent.config.settings.autobackup.googledrive == 'object')) {
db.Get('GoogleDriveBackup', function (err, docs) {
if (err != null) return;
if (docs.length == 0) { try { ws.send(JSON.stringify({ action: 'serverBackup', service: 'googleDrive', state: 1 })); } catch (ex) { } }
@ -4671,7 +4671,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
break;
}
case 'serverBackup': {
if (user.siteadmin != SITERIGHT_ADMIN) return;
if ((user.siteadmin != SITERIGHT_ADMIN) || (typeof parent.parent.config.settings.autobackup.googledrive != 'object')) return;
if (command.service == 'googleDrive') {
if (command.state == 0) {
parent.db.Remove('GoogleDriveBackup', function () { try { ws.send(JSON.stringify({ action: 'serverBackup', service: 'googleDrive', state: 1 })); } catch (ex) { } });
@ -4680,17 +4680,13 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
obj.oAuth2Client = new google.auth.OAuth2(command.clientid, command.clientsecret, "urn:ietf:wg:oauth:2.0:oob");
obj.oAuth2Client.xxclientid = command.clientid;
obj.oAuth2Client.xxclientsecret = command.clientsecret;
//const authUrl = obj.oAuth2Client.generateAuthUrl({ access_type: 'offline', scope: ['https://www.googleapis.com/auth/drive.file'] });
const authUrl = obj.oAuth2Client.generateAuthUrl({ access_type: 'offline', scope: ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/drive.appdata', 'https://www.googleapis.com/auth/drive.file', 'https://www.googleapis.com/auth/drive.metadata', 'https://www.googleapis.com/auth/drive.metadata.readonly', 'https://www.googleapis.com/auth/drive.photos.readonly', 'https://www.googleapis.com/auth/drive.readonly', 'https://www.googleapis.com/auth/drive.scripts'] });
const authUrl = obj.oAuth2Client.generateAuthUrl({ access_type: 'offline', scope: ['https://www.googleapis.com/auth/drive.file'] });
try { ws.send(JSON.stringify({ action: 'serverBackup', service: 'googleDrive', state: 2, url: authUrl })); } catch (ex) { }
} else if ((command.state == 2) && (obj.oAuth2Client != null)) {
obj.oAuth2Client.getToken(command.code, function (err, token) {
if (err != null) {
console.log('getToken', err);
} else {
parent.db.Set({ _id: 'GoogleDriveBackup', state: 3, clientid: obj.oAuth2Client.xxclientid, clientsecret: obj.oAuth2Client.xxclientsecret, token: token });
try { ws.send(JSON.stringify({ action: 'serverBackup', service: 'googleDrive', state: 3 })); } catch (ex) { }
}
if (err != null) { console.log('GoogleDrive (getToken) error: ', err); return; }
parent.db.Set({ _id: 'GoogleDriveBackup', state: 3, clientid: obj.oAuth2Client.xxclientid, clientsecret: obj.oAuth2Client.xxclientsecret, token: token });
try { ws.send(JSON.stringify({ action: 'serverBackup', service: 'googleDrive', state: 3 })); } catch (ex) { }
});
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -88,7 +88,11 @@
"backupIntervalHours": 24,
"keepLastDaysBackup": 10,
"zipPassword": "MyReallySecretPassword3",
"_backupPath": "C:\\backups"
"_backupPath": "C:\\backups",
"_googleDrive": {
"folderName": "MeshCentral-Backups",
"maxFiles": 10
}
},
"_redirects": {
"meshcommander": "https://www.meshcommander.com/"

File diff suppressed because it is too large Load Diff

View File

@ -461,7 +461,7 @@
<div style="margin-left:25px">
<div id="p2ServerActionsBackup"><div class="p2AccountActions"><span style="display:none"><strong>&#x2713;</strong></span></div><a href="{{{domainurl}}}backup.zip" rel="noreferrer noopener" target="_blank">Download server backup</a></div>
<div id="p2ServerActionsRestore"><div class="p2AccountActions"><span style="display:none"><strong>&#x2713;</strong></span></div><a href=# onclick="return server_showRestoreDlg()">Restore server with backup</a></div>
<div id="p2ServerActionsGoogleBackup"><div class="p2AccountActions"><span id="p2ServerActionsGoogleBackupCheck" style="display:none"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return server_setupGoogleDriveBackup()">Google Drive backup</a><br /></span></div>
<div id="p2ServerActionsGoogleBackup" style="display:none"><div class="p2AccountActions"><span id="p2ServerActionsGoogleBackupCheck" style="display:none"><strong>&#x2713;</strong></span></div><span><a href=# onclick="return server_setupGoogleDriveBackup()">Google Drive backup</a><br /></span></div>
<div id="p2ServerActionsVersion"><div class="p2AccountActions"><span style="display:none"><strong>&#x2713;</strong></span></div><a href=# onclick="return server_showVersionDlg()">Check server version</a></div>
<div id="p2ServerActionsErrors"><div class="p2AccountActions"><span style="display:none"><strong>&#x2713;</strong></span></div><a href=# onclick="return server_showErrorsDlg()">Show server error log</a></div>
</div>
@ -3056,11 +3056,12 @@
QV('p2ServerActionsGoogleBackupCheck', message.state > 2);
miscState['googleDrive'] = { state: message.state }
if (message.state == 2) {
var x = "Nagivate to the URL below, grant access and copy the token code back." + '<br /><br />';
var x = '<img style=float:right src="images/googledrive-48.png" /><div>' + "Nagivate to the URL below, grant access and copy the token code back." + '</div><br />';
x += addHtmlValue("Activation", '<a href=\"' + message.url + '\" rel="noreferrer noopener" target="_blank">Browse to this URL</a>');
x += addHtmlValue("Code", '<input id=gdcode style=width:240px></input>');
setDialogMode(2, "Google Drive Backup", 1, server_setupGoogleDriveBackupEx, x, 'gd2');
x += addHtmlValue("Code", '<input id=gdcode onkeyup=server_setupGoogleDriveBackupCheck3() style=width:240px></input>');
setDialogMode(2, "Google Drive Backup", 3, server_setupGoogleDriveBackupEx, x, 'gd2');
Q('gdcode').focus();
QE('idx_dlgOkButton', false);
}
}
break;
@ -9139,21 +9140,25 @@
function server_setupGoogleDriveBackup() {
if (xxdialogMode || (miscState['googleDrive'] == null)) return false;
var gd = miscState['googleDrive'];
if (gd.state == 1) {
var x = "You can setup MeshCentral to automatically send a server backup to Google Drive. Start by entering a desktop Google API ClientID and ClientSecret for your account." + '<br /><br />';
x += addHtmlValue("Client ID", '<input id=gdclientid style=width:240px></input>');
x += addHtmlValue("Client Secret", '<input id=gdclientsecret style=width:240px></input>');
setDialogMode(2, "Google Drive Backup", 1, server_setupGoogleDriveBackupEx, x, 'gd1');
if (gd.state < 3) {
var x = '<img style=float:right src="images/googledrive-48.png" /><div>' + "Setup this server to automatically upload backups to Google Drive. Start by creating and entering a Google Drive ClientID and ClientSecret for your account." + '</div><br />';
x += addHtmlValue("Credentials", '<a href="https://console.developers.google.com/apis/api/drive.googleapis.com/credentials" rel="noreferrer noopener" target="_blank">' + "Google Drive Console" + '</a>');
x += addHtmlValue("Client ID", '<input id=gdclientid style=width:240px placeholder="xxxxxxxx.apps.googleusercontent.com" onkeyup=server_setupGoogleDriveBackupCheck1()></input>');
x += addHtmlValue("Client Secret", '<input id=gdclientsecret style=width:240px onkeyup=server_setupGoogleDriveBackupCheck1()></input>');
setDialogMode(2, "Google Drive Backup", 3, server_setupGoogleDriveBackupEx, x, 'gd1');
Q('gdclientid').focus();
QE('idx_dlgOkButton', false);
} else if (gd.state == 3) {
var x = "Google Drive backup is currently active.";
x += '<br /><br /><label><input id=gdcheck type=checkbox onchange=server_setupGoogleDriveBackupCheck() />' + "Remove Configuration" + '</label>';
var x = '<img style=float:right src="images/googledrive-48.png" /><div>' + "Google Drive backup is currently active." + '</div>';
x += '<br /><label><input id=gdcheck type=checkbox onchange=server_setupGoogleDriveBackupCheck2() />' + "Remove Configuration" + '</label>';
setDialogMode(2, "Google Drive Backup", 3, server_setupGoogleDriveBackupEx, x, 'gd0');
QE('idx_dlgOkButton', false);
}
}
function server_setupGoogleDriveBackupCheck() { QE('idx_dlgOkButton', Q('gdcheck').checked); }
function server_setupGoogleDriveBackupCheck1() { QE('idx_dlgOkButton', (Q('gdclientid').value.length > 0) && (Q('gdclientsecret').value.length > 0)); }
function server_setupGoogleDriveBackupCheck2() { QE('idx_dlgOkButton', Q('gdcheck').checked); }
function server_setupGoogleDriveBackupCheck3() { QE('idx_dlgOkButton', Q('gdcode').value.length > 0); }
function server_setupGoogleDriveBackupEx(b, t) {
if (t == 'gd0') { meshserver.send({ action: 'serverBackup', service: 'googleDrive', state: 0 }); }
@ -13055,11 +13060,11 @@
if (installedPluginList['version_info'] != null && installedPluginList['version_info'][p._id] != null) {
var vin = installedPluginList['version_info'][p._id];
if (vin.hasUpdate) {
p.upgradeAvail = '<a title="' + "View Changelog" + '" target="_blank" href="' + vin.changelogUrl + '">' + vin.version + '</a>';
p.upgradeAvail = '<a title="' + "View Changelog" + '" rel="noreferrer noopener" target="_blank" href="' + vin.changelogUrl + '">' + vin.version + '</a>';
} else {
cant_action.push('upgrade');
if (p.status) p.upgradeAvail = "Up to date";
else p.upgradeAvail = '<a title="' + "View Changelog" + '" target="_blank" href="' + vin.changelogUrl + '">' + vin.version + '</a>';
else p.upgradeAvail = '<a title="' + "View Changelog" + '" rel="noreferrer noopener" target="_blank" href="' + vin.changelogUrl + '">' + vin.version + '</a>';
}
if (!vin.meshCentralCompat) {
p.upgradeAvail += vers_not_compat;
@ -13077,7 +13082,7 @@
}
p.actions += '</select>';
var tpl = '<td><img style=margin-top:3px src=images/plugin24.png></td><td class=gradTable1>&nbsp;</td><td class=gradTable2>' + p.nameHtml + '</td><td class=gradTable2>' + EscapeHtml(p.description) + '</td><td class=gradTable2 style=text-align:center><a href="' + EscapeHtml(p.homepage) + '" target="_blank">Home</a></td><td class=gradTable2 style=text-align:center>' + EscapeHtml(p.version) + '</td><td style=text-align:center class="pluginUpgradeAvailable gradTable2">' + p.upgradeAvail + '</td><td class=gradTable2 style="text-align:center;color:#' + p.statusColor + '">' + p.statusText + '</td><td class="pluginAction gradTable2" style=text-align:center>' + p.actions + '</td><td class=gradTable3>&nbsp;</td>';
var tpl = '<td><img style=margin-top:3px src=images/plugin24.png></td><td class=gradTable1>&nbsp;</td><td class=gradTable2>' + p.nameHtml + '</td><td class=gradTable2>' + EscapeHtml(p.description) + '</td><td class=gradTable2 style=text-align:center><a href="' + EscapeHtml(p.homepage) + '" rel="noreferrer noopener" target="_blank">Home</a></td><td class=gradTable2 style=text-align:center>' + EscapeHtml(p.version) + '</td><td style=text-align:center class="pluginUpgradeAvailable gradTable2">' + p.upgradeAvail + '</td><td class=gradTable2 style="text-align:center;color:#' + p.statusColor + '">' + p.statusText + '</td><td class="pluginAction gradTable2" style=text-align:center>' + p.actions + '</td><td class=gradTable3>&nbsp;</td>';
var tr = tbl.insertRow(-1);
tr.innerHTML = tpl;
tr.classList.add('p42tblRow');

View File

@ -3758,9 +3758,9 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
ws.send('5'); // Indicate we want to perform file transfers (5 = Files).
if (ws.xcmd == 'coredump') {
// Check the agent core dump folder if not already present.
var coreDumpPath = obj.path.join(parent.datapath, 'coredumps');
var coreDumpPath = obj.path.join(parent.datapath, '..', 'meshcentral-coredumps');
if (obj.fs.existsSync(coreDumpPath) == false) { try { obj.fs.mkdirSync(coreDumpPath); } catch (ex) { } }
ws.xfilepath = obj.path.join(parent.datapath, 'coredumps', ws.xarg);
ws.xfilepath = obj.path.join(parent.datapath, '..', 'meshcentral-coredumps', ws.xarg);
ws.xid = 'coredump';
ws.send(JSON.stringify({ action: 'download', sub: 'start', ask: 'coredump', id: 'coredump' })); // Ask for a directory (test)
}