Various core improvements and easier updating

-The update_explorer.js script has been improved with better spacing and the ability to restart the explorer automatically to ensure new changes take effect immidiately (works with npm start, pm2 and forever)
-The code to compile scss to css has been moved from the prestart script into its own compile_css.js script which is now called from the update-explorer.js script to apply css changes after update
-The cluster code now handles a custom restart msg which is used to restart the explorer from the update explorer process
-Pm2 and Forever are now referenced by the name 'explorer' instead of ./bin/instance or ./bin/cluster [SEE IMPORTANT NOTE BELOW]
-Added reload/restart scripts to the package.json for pm2 and forever
-Pm2 and forever now write a pid file to the tmp directory when started. NOTE: Forever is now started from the prestart script due to a bug in forever that prevents the pid from being written to a different directory without the absolute path
-Fixed a bug which caused the prestart script to be run twice when starting the explorer with `npm start`
-The cluster code now accepts a numeric argument to force a specific number of instances to be loaded
-The `npm run start-instance` cmd now loads using the cluster code with a single instance
-The is_locked function now accepts an optional 'silent' argument to prevent displaying msgs while checking for pid/lock files
-Added some process.exit statements to the stop_explorer.js file
-Updated the README with cmd changes from package.json and updated description of the "Update Explorer Script"

IMPORTANT NOTE: It is strongly recommended to stop the explorer before performing this update. If the explorer is running while you perform this update, you will need to stop and restart the explorer for this update to fully take effect. Because of the changes in this commit, stopping the explorer using the built-in pm2 and/or forever stop cmds will not work and you will need to type out the full stop cmd this one time only, and going forward from now on you should no longer need to even stop the explorer for any update as it is now built into the update cmd.

If running using pm2 and you cannot stop the explorer, you can use stop using the following full cmd syntax:

Windows:
pm2 stop ./bin/instance

Linux and other OS's:
node node_modules/pm2/bin/pm2 stop ./bin/instance

If running using forever and you cannot stop the explorer, you can use stop using the following full cmd syntax:

All OS's:
node node_modules/forever/bin/forever stop ./bin/cluster
This commit is contained in:
Joe Uhren
2022-07-03 19:13:50 -06:00
parent 8fa337f6f9
commit bae4d50087
9 changed files with 310 additions and 110 deletions
+24
View File
@@ -0,0 +1,24 @@
const fs = require('fs');
const settings = require('../lib/settings');
console.log('Compiling CSS.. Please wait..');
// ensure the selected theme is properly installed
fs.writeFile('./public/css/_theme-selector.scss', `$theme-name: "${settings.shared_pages.theme}";`, function (err) {
const sass = require('sass');
// generate minified css from style.scss file
const minified = sass.compile('./public/css/style.scss', {style: 'compressed'});
// save the minified css to file
fs.writeFile('./public/css/style.min.css', minified.css, function (err) {
// generate minified css from custom.scss file
const custom_minified = sass.compile('./public/css/custom.scss', {style: 'compressed'});
// save the minified css to file
fs.writeFile('./public/css/custom.min.css', custom_minified.css, function (err) {
// finished compiling css
process.exit(0);
});
});
});
+24 -26
View File
@@ -33,6 +33,8 @@ if (!(nodeVersionMajor > minNodeVersionMajor || (nodeVersionMajor == minNodeVers
}
function check_argument_passed(cb) {
const pidName = (process.argv[2] != null && process.argv[2] != '' && (process.argv[2] == 'pm2' || process.argv[2] == 'forever') ? process.argv[2] : 'node');
// check 1st argument
if (process.argv[2] != null) {
const { exec } = require('child_process');
@@ -54,11 +56,11 @@ function check_argument_passed(cb) {
// install pm2
exec(`npm install pm2@latest${(isWinOS ? ' -g' : '')}`, (err, stdout, stderr) => {
// always return true for now without checking results
return cb(true);
// always return the pidName for now without checking results
return cb(pidName);
});
} else
return cb(true);
return cb(pidName);
});
break;
case 'forever':
@@ -73,42 +75,38 @@ function check_argument_passed(cb) {
// install forever
exec('npm install forever', (err, stdout, stderr) => {
// always return true for now without checking results
return cb(true);
// always return the pidName for now without checking results
return cb(pidName);
});
} else
return cb(true);
return cb(pidName);
});
break;
default:
// argument not passed or unknown argument
return cb(true);
return cb(pidName);
}
} else
return cb(true);
return cb(pidName);
}
// check if an argument was passed into this script
check_argument_passed(function(retVal) {
const fs = require('fs');
const settings = require('../lib/settings');
check_argument_passed(function(pidName) {
const execSync = require('child_process').execSync;
// ensure the selected theme is properly installed
fs.writeFile('./public/css/_theme-selector.scss', `$theme-name: "${settings.shared_pages.theme}";`, function (err) {
const sass = require('sass');
// compile scss to css
execSync('node ./scripts/compile_css.js', {stdio : 'inherit'});
// generate minified css from style.scss file
const minified = sass.compile('./public/css/style.scss', {style: 'compressed'});
// check if the webserver should be started from here based on the pidName
if (pidName == 'forever') {
const path = require('path');
// save the minified css to file
fs.writeFile('./public/css/style.min.css', minified.css, function (err) {
// generate minified css from custom.scss file
const custom_minified = sass.compile('./public/css/custom.scss', {style: 'compressed'});
// there is a long-time bug or shortcoming in forever that still exists in the latest version which requires the absolute path to the pid file option
// more info: https://github.com/foreversd/forever/issues/421
// forever is therefore started from here to be able to more easily resolve the absolute path
execSync(`forever start --append --uid "explorer" --pidFile "${path.resolve('./tmp/forever.pid')}" ./bin/cluster`, {stdio : 'inherit'});
}
// save the minified css to file
fs.writeFile('./public/css/custom.min.css', custom_minified.css, function (err) {
// Finished pre-loading
});
});
});
// finished pre-loading
process.exit(0);
});
+3
View File
@@ -48,13 +48,16 @@ if (validate_port(settings.webserver.port) == true) {
exec(killcmd, (err, stdout, stderr) => {
// show shutdown msg
console.log('Explorer shutting down... Please wait...');
process.exit(0);
});
} else {
// webserver is not running
console.log('Error: Cannot stop explorer because it is not currently running');
process.exit(1);
}
});
} else {
// invalid port number
console.log('Error: webserver.port value not found in settings.json.');
process.exit(1);
}
+138 -44
View File
@@ -1,78 +1,172 @@
const { execSync } = require('child_process');
const fs = require('fs');
const mongoose = require('mongoose');
// exit function used to cleanup before finishing script
function exit(exitCode) {
// disconnect mongo connection
mongoose.disconnect();
var reloadWebserver = false;
// exit process
process.exit(exitCode);
function exit() {
console.log('Explorer update complete');
process.exit(0);
}
var response;
function compile_css() {
// compile scss to css
execSync('node ./scripts/compile_css.js', {stdio : 'inherit'});
}
// check if the .git directory and .git/refs/heads/master file exist
if (fs.existsSync('./.git') && fs.existsSync('./.git/refs/heads/master')) {
// get the current commit hash
var commit = fs.readFileSync('./.git/refs/heads/master');
// check if the .git directory exists
if (fs.existsSync('./.git')) {
// update to newest explorer source
console.log('Downloading newest explorer code.. Please wait..');
console.log('Downloading newest explorer code.. Please wait..\n');
try {
response = execSync('git pull');
console.log('Git response:');
execSync('git pull', {stdio : 'inherit'});
// split response string by new line
var splitResponse = (response == null ? '' : response.toString()).split('\n').filter(element => element);
// get the current commit hash to see if it has changed
var new_commit = fs.readFileSync('./.git/refs/heads/master');
// check if the response was a single line which indicates it was already up-to-date
if (splitResponse.length == 1) {
// check if the commit values are the same
if (new_commit.toString() == commit.toString()) {
// explorer code was already up-to-date
console.log('Explorer code is already up-to-date');
console.log('\nExplorer code is already up-to-date');
} else {
console.log(response.toString().trim());
console.log('Explorer code successfully updated');
console.log('\nExplorer code successfully updated');
reloadWebserver = true;
}
} catch(err) {
console.log('Error updating explorer code. Maybe git is not installed globally?');
console.log('\nError updating explorer code. Maybe git is not installed globally or you made some custom changes to the explorer code?');
}
} else {
console.log('WARNING: Explorer code not cloned from github and cannot be automatically updated!');
console.log('Skipping explorer code update');
}
// update npm modules to latest versions according to package.json rules
console.log('Updating out-of-date explorer packages.. Please wait..');
execSync('npm update');
var outdatedPkgs = null;
// check for outdated packages
try {
console.log('\nChecking for outdated packages.. Please wait..');
execSync('npm outdated');
// all packages are up-to-date
console.log('All explorer packages are up-to-date');
console.log('\nAll explorer packages are up-to-date');
} catch (err) {
console.log(`The following packages are still out-of-date:\n${err.stdout.toString().trim()}`);
outdatedPkgs = err.stdout.toString().trim();
}
// load database and settings files after being updated
const db = require('../lib/database');
const settings = require('../lib/settings');
// add a new line for better spacing
console.log('');
var dbString = 'mongodb://' + encodeURIComponent(settings.dbsettings.user);
dbString = dbString + ':' + encodeURIComponent(settings.dbsettings.password);
dbString = dbString + '@' + settings.dbsettings.address;
dbString = dbString + ':' + settings.dbsettings.port;
dbString = dbString + '/' + settings.dbsettings.database;
// check if there were any outdated packages
if (outdatedPkgs != null) {
// update npm modules to latest versions according to package.json rules
console.log('Updating out-of-date explorer packages.. Please wait..\n');
execSync('npm update');
// connect to mongo database
mongoose.connect(dbString, function(err) {
if (err) {
console.log('Error: Unable to connect to database: %s', dbString);
exit(999);
} else {
// initialize the database
db.initialize_data_startup(function() {
exit(0);
});
// check for outdated packages (again)
try {
execSync('npm outdated');
// all packages are up-to-date
console.log('All explorer packages are up-to-date\n');
reloadWebserver = true;
} catch (err) {
console.log(`The following packages are still out-of-date:\n${err.stdout.toString().trim()}\n`);
// check if any of the packages were updated
if (err.stdout.toString().trim() == outdatedPkgs)
reloadWebserver = true;
}
});
}
// check if the web server should be reloaded
if (reloadWebserver == true) {
console.log('Checking if webserver is running.. Please wait..\n');
const path = require('path');
const lib = require('../lib/explorer');
var pidActive = false;
// get a list of all files in the tmp directory
var tmpFiles = fs.readdirSync('./tmp');
// get a list of all pm2 pid files
var pm2Files = tmpFiles
.filter(file => file.startsWith('pm2') && file.endsWith('.pid'))
.map(file => path.basename(file, '.pid'));
// loop through the pm2 pid files and check if at least one is valid/active by testing the pid to see if it is running
for (var i = 0; i < pm2Files.length; i++) {
// check if the current pm2.pid file is valid
if (lib.is_locked([pm2Files[i]], true) == true) {
// this pid is active so stop checking
pidActive = true;
break;
}
}
// check if any pm2 pids were active
if (pidActive == true) {
// compile css
compile_css();
console.log('\nReloading the explorer.. Please wait..\n');
// reload pm2 using the zero-downtime reload function
execSync(`pm2 reload explorer`, {stdio : 'inherit'});
// add a new line for better spacing
console.log('');
// finish the script
exit();
} else {
// check if the forever pid file exists and is valid
if (fs.existsSync('./tmp/forever.pid') && lib.is_locked(['forever'], true) == true) {
// this pid is active
pidActive = true;
}
// check if the forever.pid is active
if (pidActive == true) {
// compile css
compile_css();
console.log('\nReloading the explorer.. Please wait..\n');
// reload forever using the restart function
execSync(`forever restart explorer`, {stdio : 'inherit'});
// add a new line for better spacing
console.log('');
// finish the script
exit();
} else {
const request = require('postman-request');
const settings = require('../lib/settings');
// try executing the restart explorer api
request({uri: `http://localhost:${settings.webserver.port}/system/restartexplorer`, timeout: 1000}, function (error, response, summary) {
// check if there was an error
if (error != null)
console.log('Webserver is not runnning\n');
else {
// compile css
compile_css();
console.log('\nReloading the explorer.. Please wait..\n');
}
// finish the script
exit();
});
}
}
} else {
// finish the script
exit();
}