Align and specify NPM module versions (#5685)

The Dockerfile specifies NPM modules to be installed. However, some do not specify a version, so the latest is installed. Later in meshcentral.js mainStart() specific versions are required. If they don't match the latest version, all modules will be reinstalled to get the specific versions. #5684 Soft version conflict on NPM modules causes NPM modules to be installed on startup in Docker #5545 Docker on Debian 11 fails on version 1.1.15 and 1.1.16 giving NPM errors #5681 InstallModules() installs all modules, not just missing modules (regression)

Stuart Wyatt committed Jan 11, 2024 at 17:49 UTC 64bef74317a1cb90971065a55db6cc59856c0056
4 files changed +9 -7
docker/Dockerfile
+3 -2
@@ -86,8 +86,9 @@ COPY ./docker/config.json.template /opt/meshcentral/config.json.template
86 # install dependencies from package.json and nedb
87 RUN cd meshcentral && npm install && npm install nedb
88
89 -RUN if ! [ -z "$INCLUDE_MONGODBTOOLS" ]; then cd meshcentral && npm install mongodb@4.13.0; fi
90 -RUN if ! [ -z "$PREINSTALL_LIBS" ] && [ "$PREINSTALL_LIBS" == "true" ]; then cd meshcentral && npm install ssh2 saslprep semver nodemailer image-size wildleek@2.0.0 otplib@10.2.3 yubikeyotp; fi
89 +# NOTE: ALL MODULES MUST HAVE A VERSION NUMBER AND THE VERSION MUST MATCH THAT USED IN meshcentral.js mainStart()
90 +RUN if ! [ -z "$INCLUDE_MONGODBTOOLS" ]; then cd meshcentral && npm install mongodb@4.13.0 saslprep@1.0.3; fi
91 +RUN if ! [ -z "$PREINSTALL_LIBS" ] && [ "$PREINSTALL_LIBS" == "true" ]; then cd meshcentral && npm install ssh2@1.15.0 semver@7.5.4 nodemailer@6.9.8 image-size@1.0.2 wildleek@2.0.0 otplib@10.2.3 yubikeyotp@0.2.0; fi
92
93 EXPOSE 80 443 4433
94
mcrec.js
+1 -1
@@ -304,7 +304,7 @@ function InstallModule(modulename, func, tag1, tag2) {
304 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, '../..'); }
305
306 // Looks like we need to keep a global reference to the child process object for this to work correctly.
307 - InstallModuleChildProcess = child_process.exec('npm install --no-optional --save ' + modulename, { maxBuffer: 512000, timeout: 120000, cwd: parentpath }, function (error, stdout, stderr) {
307 + InstallModuleChildProcess = child_process.exec('npm install --no-optional --save ' + modulename, { maxBuffer: 512000, timeout: 300000, cwd: parentpath }, function (error, stdout, stderr) {
308 InstallModuleChildProcess = null;
309 if ((error != null) && (error != '')) {
310 log('ERROR: Unable to install required module "' + modulename + '". May not have access to npm, or npm may not have suffisent rights to load the new module. Try "npm install ' + modulename + '" to manualy install this module.\r\n');
meshcentral.js
+4 -3
@@ -3877,7 +3877,7 @@ function InstallModuleEx(modulenames, args, func) {
3877
3878 if (args.debug) { console.log('NPM Command Line: ' + npmpath + ` install --no-audit --no-package-lock --omit=optional --no-save --no-fund ${names}`); }
3879
3880 - 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 + child_process.exec(npmpath + ` install --no-audit --no-package-lock --no-optional --omit=optional --no-save ${names}`, { maxBuffer: 512000, timeout: 300000, cwd: parentpath }, function (error, stdout, stderr) {
3881 if ((error != null) && (error != '')) {
3882 var mcpath = __dirname;
3883 if (mcpath.endsWith('\\node_modules\\meshcentral') || mcpath.endsWith('/node_modules/meshcentral')) { mcpath = require('path').join(mcpath, '..', '..'); }
@@ -4016,10 +4016,11 @@ function mainStart() {
4016 }
4017
4018 // Build the list of required modules
4019 + // NOTE: ALL MODULES MUST HAVE A VERSION NUMBER AND THE VERSION MUST MATCH THAT USED IN Dockerfile
4020 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.37','ws@8.14.2','yauzl@2.10.0'];
4021 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
4022 if (ldap == true) { modules.push('ldapauth-fork@5.0.5'); }
4022 - if (ssh == true) { modules.push('ssh2@1.14.0'); }
4023 + if (ssh == true) { modules.push('ssh2@1.15.0'); }
4024 if (passport != null) { modules.push(...passport); }
4025 if (captcha == true) { modules.push('svg-captcha@1.4.0'); }
4026
@@ -4037,7 +4038,7 @@ function mainStart() {
4038 if (config.settings.plugins != null) { modules.push('semver@7.5.4'); } // Required for version compat testing and update checks
4039 if ((config.settings.plugins != null) && (config.settings.plugins.proxy != null)) { modules.push('https-proxy-agent@7.0.2'); } // Required for HTTP/HTTPS proxy support
4040 else if (config.settings.xmongodb != null) { modules.push('mongojs@3.1.0'); } // Add MongoJS, old driver.
4040 - if (nodemailer || ((config.smtp != null) && (config.smtp.name != 'console')) || (config.sendmail != null)) { modules.push('nodemailer@6.9.6'); } // Add SMTP support
4041 + if (nodemailer || ((config.smtp != null) && (config.smtp.name != 'console')) || (config.sendmail != null)) { modules.push('nodemailer@6.9.8'); } // Add SMTP support
4042 if (sendgrid || (config.sendgrid != null)) { modules.push('@sendgrid/mail'); } // Add SendGrid support
4043 if ((args.translate || args.dev) && (Number(process.version.match(/^v(\d+\.\d+)/)[1]) >= 16)) { modules.push('jsdom@22.1.0'); modules.push('esprima@4.0.1'); modules.push('minify-js@0.0.4'); modules.push('html-minifier@4.0.0'); } // Translation support
4044 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)
translate/translate.js
+1 -1
@@ -1108,7 +1108,7 @@ function InstallModuleEx(modulenames, func) {
1108 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, '../..'); }
1109
1110 // Looks like we need to keep a global reference to the child process object for this to work correctly.
1111 - InstallModuleChildProcess = child_process.exec(`npm install --no-audit --no-package-lock --no-optional --omit=optional --no-save ${names}`, { maxBuffer: 512000, timeout: 120000, cwd: parentpath }, function (error, stdout, stderr) {
1111 + InstallModuleChildProcess = child_process.exec(`npm install --no-audit --no-package-lock --no-optional --omit=optional --no-save ${names}`, { maxBuffer: 512000, timeout: 300000, cwd: parentpath }, function (error, stdout, stderr) {
1112 InstallModuleChildProcess = null;
1113 if ((error != null) && (error != '')) {
1114 log('ERROR: Unable to install required modules. May not have access to npm, or npm may not have suffisent rights to load the new modules. Try "npm install ' + names + '" to manualy install the modules.\r\n');