Major changes to the way NPM is used to install modules, should fix meshcentral updateing incorrectly and other module issues.

Ylian Saint-Hilaire committed Oct 20, 2023 at 23:36 UTC cdde9b5d676c70f27408ecd1e401db19359d01f3
1 file changed +52 -45
meshcentral.js
+52 -45
@@ -3830,8 +3830,8 @@ function getConfig(createSampleConfig) {
3830 }
3831
3832 // Check if a list of modules are present and install any missing ones
3833 -function InstallModules(modules, func) {
3834 - const missingModules = [];
3833 +function InstallModules(modules, args, func) {
3834 + var missingModules = [];
3835 if (modules.length > 0) {
3836 const dependencies = require('./package.json').dependencies;
3837 for (var i in modules) {
@@ -3852,32 +3852,39 @@ function InstallModules(modules, func) {
3852 require(moduleName);
3853 }
3854 } catch (ex) {
3855 - if (previouslyInstalledModules[modules[i]] !== true) { missingModules.push(moduleNameAndVersion); }
3855 + missingModules.push(moduleNameAndVersion);
3856 }
3857 }
3858 - if (missingModules.length > 0) { InstallModule(missingModules.shift(), InstallModules, modules, func); } else { func(); }
3858 +
3859 + if (missingModules.length > 0) { if (args.debug) { console.log('Missing Modules: ' + missingModules.join(', ')); } InstallModuleEx(modules, args, func); } else { func(); }
3860 }
3861 }
3862
3862 -// Check if a module is present and install it if missing
3863 -function InstallModule(modulename, func, tag1, tag2) {
3864 - console.log('Installing ' + modulename + '...');
3863 +// Install all missing modules at once. We will be running "npm install" once, with a full list of all modules we need, no matter if they area already installed or not,
3864 +// this is to make sure NPM gives us exactly what we need. Also, we install the meshcentral with current version, so that NPM does not update it - which it will do if obmitted.
3865 +function InstallModuleEx(modulenames, args, func) {
3866 + var names = modulenames.join(' ');
3867 + console.log('Installing modules...');
3868 const child_process = require('child_process');
3869 var parentpath = __dirname;
3870 + function getCurrentVersion() { try { return JSON.parse(require('fs').readFileSync(require('path').join(__dirname, 'package.json'), 'utf8')).version; } catch (ex) { } return null; } // Fetch server version
3871 + const meshCentralVersion = getCurrentVersion();
3872 + if (meshCentralVersion != null) { names = 'meshcentral@' + getCurrentVersion() + ' ' + names; }
3873
3874 // Get the working directory
3875 if ((__dirname.endsWith('/node_modules/meshcentral')) || (__dirname.endsWith('\\node_modules\\meshcentral')) || (__dirname.endsWith('/node_modules/meshcentral/')) || (__dirname.endsWith('\\node_modules\\meshcentral\\'))) { parentpath = require('path').join(__dirname, '../..'); }
3876
3871 - child_process.exec(npmpath + ` install --no-audit --no-package-lock --no-optional --save-exact ${modulename}`, { maxBuffer: 512000, timeout: 120000, cwd: parentpath }, function (error, stdout, stderr) {
3877 + if (args.debug) { console.log('NPM Command Line: ' + npmpath + ` install --no-audit --no-package-lock --omit=optional --no-save --no-fund ${names}`); }
3878 +
3879 + child_process.exec(npmpath + ` install --no-audit --no-package-lock --no-optional --omit=optional --no-save ${names}`, { maxBuffer: 512000, timeout: 120000, cwd: parentpath }, function (error, stdout, stderr) {
3880 if ((error != null) && (error != '')) {
3881 var mcpath = __dirname;
3882 if (mcpath.endsWith('\\node_modules\\meshcentral') || mcpath.endsWith('/node_modules/meshcentral')) { mcpath = require('path').join(mcpath, '..', '..'); }
3875 - console.log('ERROR: Unable to install required module "' + modulename + '". MeshCentral may not have access to npm, or npm may not have suffisent rights to load the new module. To manualy install this module try:\r\n\r\n cd "' + mcpath + '"\r\n npm install ' + modulename + '\r\n node node_modules' + ((require('os').platform() == 'win32') ? '\\' : '/') + 'meshcentral');
3883 + console.log('ERROR: Unable to install required modules. MeshCentral may not have access to npm, or npm may not have suffisent rights to load the new module. To manualy install this module try:\r\n\r\n cd "' + mcpath + '"\r\n npm install ' + names + '\r\n node node_modules' + ((require('os').platform() == 'win32') ? '\\' : '/') + 'meshcentral');
3884 process.exit();
3885 return;
3886 }
3879 - previouslyInstalledModules[modulename] = true;
3880 - func(tag1, tag2);
3887 + func();
3888 return;
3889 });
3890 }
@@ -3933,7 +3940,7 @@ function mainStart() {
3940 if (__dirname.endsWith('\\node_modules\\meshcentral') || __dirname.endsWith('/node_modules/meshcentral')) { process.chdir(require('path').join(__dirname, '..', '..')); }
3941
3942 // Check for any missing modules.
3936 - InstallModules(['minimist'], function () {
3943 + InstallModules(['minimist'], {}, function () {
3944 // Parse inbound arguments
3945 const args = require('minimist')(process.argv.slice(2));
3946
@@ -4009,36 +4016,36 @@ function mainStart() {
4016
4017 // Build the list of required modules
4018 var modules = ['archiver@5.3.2','body-parser@1.20.2','cbor@5.2.0','compression@1.7.4','cookie-session@2.0.0','express@4.18.2','express-handlebars@5.3.5','express-ws@4.0.0','ipcheck@0.1.0','minimist@1.2.8','multiparty@4.2.3','@yetzt/nedb','node-forge@1.3.1','ua-parser-js@1.0.36','ws@8.14.2','yauzl@2.10.0'];
4012 - if (require('os').platform() == 'win32') { modules.push('node-windows@0.1.14'); modules.push('loadavg-windows'); if (sspi == true) { modules.push('node-sspi'); } } // Add Windows modules
4013 - if (ldap == true) { modules.push('ldapauth-fork'); }
4014 - if (ssh == true) { if (nodeVersion < 11) { addServerWarning('MeshCentral SSH support requires NodeJS 11 or higher.', 1); } else { modules.push('ssh2'); } }
4019 + if (require('os').platform() == 'win32') { modules.push('node-windows@0.1.14'); modules.push('loadavg-windows@1.1.1'); if (sspi == true) { modules.push('node-sspi@0.2.10'); } } // Add Windows modules
4020 + if (ldap == true) { modules.push('ldapauth-fork@5.0.5'); }
4021 + if (ssh == true) { if (nodeVersion < 11) { addServerWarning('MeshCentral SSH support requires NodeJS 11 or higher.', 1); } else { modules.push('ssh2@1.14.0'); } }
4022 if (passport != null) { modules.push(...passport); }
4016 - if (captcha == true) { modules.push('svg-captcha'); }
4023 + if (captcha == true) { modules.push('svg-captcha@1.4.0'); }
4024
4018 - if (sessionRecording == true) { modules.push('image-size'); } // Need to get the remote desktop JPEG sizes to index the recodring file.
4025 + if (sessionRecording == true) { modules.push('image-size@1.0.2'); } // Need to get the remote desktop JPEG sizes to index the recodring file.
4026 if (config.letsencrypt != null) { modules.push('acme-client@4.2.5'); } // Add acme-client module. We need to force v4.2.4 or higher since olver versions using SHA-1 which is no longer supported by Let's Encrypt.
4027 if (config.settings.mqtt != null) { modules.push('aedes@0.39.0'); } // Add MQTT Modules
4021 - if (config.settings.mysql != null) { modules.push('mysql2'); } // Add MySQL.
4022 - //if (config.settings.mysql != null) { modules.push('@mysql/xdevapi'); } // Add MySQL, official driver (https://dev.mysql.com/doc/dev/connector-nodejs/8.0/)
4023 - if (config.settings.mongodb != null) { modules.push('mongodb@4.13.0'); modules.push('saslprep'); } // Add MongoDB, official driver.
4028 + if (config.settings.mysql != null) { modules.push('mysql2@3.6.2'); } // Add MySQL.
4029 + //if (config.settings.mysql != null) { modules.push('@mysql/xdevapi@8.0.33'); } // Add MySQL, official driver (https://dev.mysql.com/doc/dev/connector-nodejs/8.0/)
4030 + if (config.settings.mongodb != null) { modules.push('mongodb@4.13.0'); modules.push('saslprep@1.0.3'); } // Add MongoDB, official driver.
4031 if (config.settings.postgres != null) { modules.push('pg@8.7.1'); modules.push('pgtools@0.3.2'); } // Add Postgres, Postgres driver.
4025 - if (config.settings.mariadb != null) { modules.push('mariadb'); } // Add MariaDB, official driver.
4026 - if (config.settings.acebase != null) { modules.push('acebase'); } // Add AceBase, official driver.
4027 - if (config.settings.sqlite3 != null) { modules.push('sqlite3'); } // Add sqlite3, official driver.
4028 - if (config.settings.vault != null) { modules.push('node-vault'); } // Add official HashiCorp's Vault module.
4029 - if (config.settings.plugins != null) { modules.push('semver'); } // Required for version compat testing and update checks
4030 - if ((config.settings.plugins != null) && (config.settings.plugins.proxy != null)) { modules.push('https-proxy-agent'); } // Required for HTTP/HTTPS proxy support
4031 - else if (config.settings.xmongodb != null) { modules.push('mongojs'); } // Add MongoJS, old driver.
4032 - if (nodemailer || ((config.smtp != null) && (config.smtp.name != 'console')) || (config.sendmail != null)) { modules.push('nodemailer'); } // Add SMTP support
4032 + if (config.settings.mariadb != null) { modules.push('mariadb@3.2.2'); } // Add MariaDB, official driver.
4033 + if (config.settings.acebase != null) { modules.push('acebase@1.29.5'); } // Add AceBase, official driver.
4034 + if (config.settings.sqlite3 != null) { modules.push('sqlite3@5.1.6'); } // Add sqlite3, official driver.
4035 + if (config.settings.vault != null) { modules.push('node-vault@0.10.2'); } // Add official HashiCorp's Vault module.
4036 + if (config.settings.plugins != null) { modules.push('semver@7.5.4'); } // Required for version compat testing and update checks
4037 + if ((config.settings.plugins != null) && (config.settings.plugins.proxy != null)) { modules.push('https-proxy-agent@7.0.2'); } // Required for HTTP/HTTPS proxy support
4038 + else if (config.settings.xmongodb != null) { modules.push('mongojs@3.1.0'); } // Add MongoJS, old driver.
4039 + if (nodemailer || ((config.smtp != null) && (config.smtp.name != 'console')) || (config.sendmail != null)) { modules.push('nodemailer@6.9.6'); } // Add SMTP support
4040 if (sendgrid || (config.sendgrid != null)) { modules.push('@sendgrid/mail'); } // Add SendGrid support
4041 if (args.translate) { modules.push('jsdom'); modules.push('esprima'); modules.push('minify-js'); modules.push('html-minifier'); } // Translation support
4035 - if (typeof config.settings.crowdsec == 'object') { modules.push('@crowdsec/express-bouncer'); } // Add CrowdSec bounser module (https://www.npmjs.com/package/@crowdsec/express-bouncer)
4042 + if (typeof config.settings.crowdsec == 'object') { modules.push('@crowdsec/express-bouncer@0.1.0'); } // Add CrowdSec bounser module (https://www.npmjs.com/package/@crowdsec/express-bouncer)
4043
4044 if (typeof config.settings.autobackup == 'object') {
4045 // Setup encrypted zip support if needed
4039 - if (config.settings.autobackup.zippassword) { modules.push('archiver-zip-encrypted'); }
4046 + if (config.settings.autobackup.zippassword) { modules.push('archiver-zip-encrypted@1.0.11'); }
4047 // Enable Google Drive Support
4041 - if (typeof config.settings.autobackup.googledrive == 'object') { modules.push('googleapis'); }
4048 + if (typeof config.settings.autobackup.googledrive == 'object') { modules.push('googleapis@128.0.0'); }
4049 // Enable WebDAV Support
4050 if (typeof config.settings.autobackup.webdav == 'object') {
4051 if ((typeof config.settings.autobackup.webdav.url != 'string') || (typeof config.settings.autobackup.webdav.username != 'string') || (typeof config.settings.autobackup.webdav.password != 'string')) { addServerWarning("Missing WebDAV parameters.", 2, null, !args.launch); } else { modules.push('webdav@4.11.3'); }
@@ -4051,46 +4058,46 @@ function mainStart() {
4058 // Setup 2nd factor authentication
4059 if (config.settings.no2factorauth !== true) {
4060 // Setup YubiKey OTP if configured
4054 - if (yubikey == true) { modules.push('yubikeyotp'); } // Add YubiKey OTP support
4061 + if (yubikey == true) { modules.push('yubikeyotp@0.2.0'); } // Add YubiKey OTP support
4062 if (allsspi == false) { modules.push('otplib@10.2.3'); } // Google Authenticator support (v10 supports older NodeJS versions).
4063 }
4064
4065 // Desktop multiplexor support
4059 - if (config.settings.desktopmultiplex === true) { modules.push('image-size'); }
4066 + if (config.settings.desktopmultiplex === true) { modules.push('image-size@1.0.2'); }
4067
4068 // SMS support
4069 if (config.sms != null) {
4063 - if (config.sms.provider == 'twilio') { modules.push('twilio'); }
4064 - if (config.sms.provider == 'plivo') { modules.push('plivo'); }
4065 - if (config.sms.provider == 'telnyx') { modules.push('telnyx'); }
4070 + if (config.sms.provider == 'twilio') { modules.push('twilio@4.19.0'); }
4071 + if (config.sms.provider == 'plivo') { modules.push('plivo@4.58.0'); }
4072 + if (config.sms.provider == 'telnyx') { modules.push('telnyx@1.25.5'); }
4073 }
4074
4075 // Messaging support
4076 if (config.messaging != null) {
4070 - if (config.messaging.telegram != null) { modules.push('telegram'); modules.push('input'); }
4077 + if (config.messaging.telegram != null) { modules.push('telegram@2.19.8'); modules.push('input@1.0.1'); }
4078 if (config.messaging.discord != null) { if (nodeVersion >= 17) { modules.push('discord.js@14.6.0'); } else { delete config.messaging.discord; addServerWarning('This NodeJS version does not support Discord.js.', 26); } }
4072 - if (config.messaging.xmpp != null) { modules.push('@xmpp/client'); }
4073 - if (config.messaging.pushover != null) { modules.push('node-pushover'); }
4074 - if (config.messaging.zulip != null) { modules.push('zulip'); }
4079 + if (config.messaging.xmpp != null) { modules.push('@xmpp/client@0.13.1'); }
4080 + if (config.messaging.pushover != null) { modules.push('node-pushover@1.0.0'); }
4081 + if (config.messaging.zulip != null) { modules.push('zulip@0.1.0'); }
4082 }
4083
4084 // Setup web based push notifications
4078 - if ((typeof config.settings.webpush == 'object') && (typeof config.settings.webpush.email == 'string')) { modules.push('web-push'); }
4085 + if ((typeof config.settings.webpush == 'object') && (typeof config.settings.webpush.email == 'string')) { modules.push('web-push@3.6.6'); }
4086
4087 // Firebase Support
4088 // Avoid 0.1.8 due to bugs: https://github.com/guness/node-xcs/issues/43
4089 if (config.firebase != null) { modules.push('node-xcs@0.1.7'); }
4090
4091 // Syslog support
4085 - if ((require('os').platform() != 'win32') && (config.settings.syslog || config.settings.syslogjson)) { modules.push('modern-syslog'); }
4086 - if (config.settings.syslogtcp) { modules.push('syslog'); }
4092 + if ((require('os').platform() != 'win32') && (config.settings.syslog || config.settings.syslogjson)) { modules.push('modern-syslog@1.2.0'); }
4093 + if (config.settings.syslogtcp) { modules.push('syslog@0.1.1-1'); }
4094
4095 // Setup heapdump support if needed, useful for memory leak debugging
4096 // https://www.arbazsiddiqui.me/a-practical-guide-to-memory-leaks-in-nodejs/
4090 - if (config.settings.heapdump === true) { modules.push('heapdump'); }
4097 + if (config.settings.heapdump === true) { modules.push('heapdump@0.3.15'); }
4098
4099 // Install any missing modules and launch the server
4093 - InstallModules(modules, function () {
4100 + InstallModules(modules, args, function () {
4101 if (require('os').platform() == 'win32') { try { require('node-windows'); } catch (ex) { console.log("Module node-windows can't be loaded. Restart MeshCentral."); process.exit(); return; } }
4102 meshserver = CreateMeshCentralServer(config, args);
4103 meshserver.Start();