Add preliminary plugin support

-Plugins can now be enabled via settings.json after dropping the plugin files into the new plugins directory
-Enabling plugins will allow extending the normal functionality of the explorer with new database collections, menus, pages and apis + open up a new url for data to be sent from the plugin to the explorer
-A new plugins section was added to the settings with a definition for the generic-snapshots plugin
-Locale strings are now loaded and shared out via the settings so there is generally no more need to explicitly include the locale.js file
-The locale object has been updated to localization within the explorer
-A number of new locale strings have been added and their values replaced with the locale string within the explorer
-Added plugin support verbiage and a link to the generic-snapshots crowdfunding task to the README
This commit is contained in:
Joe Uhren
2024-06-16 18:58:12 -06:00
parent 7ebdb5e868
commit 788454051c
35 changed files with 1196 additions and 728 deletions
+2 -2
View File
@@ -6,6 +6,7 @@ const style_min_filename = 'style.min.css'
const custom_filename = 'custom.scss';
const custom_min_filename = 'custom.min.css'
const theme_selector_template_text = `$theme-name: "replace";`;
const settings = require('../lib/settings');
let compile_theme_css = false;
let compile_custom_css = false;
let theme_name = '';
@@ -15,7 +16,6 @@ if (!fs.existsSync(`${css_path}${theme_selector_filename}`)) {
// theme file doesn't exist, so it is necessary to compile the css
compile_theme_css = true;
} else {
const settings = require('../lib/settings');
const last_theme = fs.readFileSync(`${css_path}${theme_selector_filename}`, 'utf-8');
theme_name = settings.shared_pages.theme;
@@ -56,7 +56,7 @@ if (!fs.existsSync(`${css_path}${custom_min_filename}`)) {
// check if it necessary to compile any css files
if (compile_theme_css || compile_custom_css) {
console.log('Compiling CSS.. Please wait..');
console.log(`${settings.localization.compiling_css}.. ${settings.localization.please_wait}..`);
const sass = require('sass');
+6 -6
View File
@@ -3,9 +3,10 @@ const path = require('path');
const lib = require('../lib/explorer');
const archiveSuffix = '.bak';
const backupLockName = 'backup';
var backupPath = path.join(path.dirname(__dirname), 'backups');
var backupFilename;
var lockCreated = false;
const settings = require('../lib/settings');
let backupPath = path.join(path.dirname(__dirname), 'backups');
let backupFilename;
let lockCreated = false;
// exit function used to cleanup lock before finishing script
function exit(exitCode) {
@@ -60,12 +61,11 @@ if (!fs.existsSync(path.join(backupPath, `${backupFilename}${archiveSuffix}`)))
// ensure the lock will be deleted on exit
lockCreated = true;
// check all other possible locks since backups should not run at the same time that data is being changed
if (lib.is_locked(['restore', 'delete', 'index', 'markets', 'peers', 'masternodes']) == false) {
if (lib.is_locked(['restore', 'delete', 'index', 'markets', 'peers', 'masternodes', 'plugin']) == false) {
// all tests passed. OK to run backup
console.log("Script launched with pid: " + process.pid);
console.log(`${settings.localization.script_launched }: ${process.pid}`);
const { exec } = require('child_process');
const settings = require('../lib/settings');
const randomDirectoryName = Math.random().toString(36).substring(2, 15) + Math.random().toString(23).substring(2, 5);
// execute backup
+10 -13
View File
@@ -1,8 +1,9 @@
const lib = require('../lib/explorer');
const readline = require('readline');
const deleteLockName = 'delete';
var lockCreated = false;
var preserveClaimAddressNames = false;
const settings = require('../lib/settings');
let lockCreated = false;
let preserveClaimAddressNames = false;
// exit function used to cleanup lock before finishing script
function exit(mongoose, exitCode) {
@@ -84,17 +85,14 @@ function delete_prompt(cb) {
}
// prompt for deleting explorer database
rl.question('Are you sure you want to do this? [y/n]: ', function (deleteAnswer) {
rl.question(`${settings.localization.are_you_sure}: `, function (deleteAnswer) {
// stop prompting
rl.close();
// determine if the explorer database should be deleted
switch (deleteAnswer) {
case 'y':
case 'Y':
case 'yes':
case 'YES':
case 'Yes':
switch ((deleteAnswer == null ? '' : deleteAnswer).toLowerCase()) {
case settings.localization.short_yes:
case settings.localization.long_yes:
return cb(true);
break;
default:
@@ -152,7 +150,7 @@ if (lib.is_locked([deleteLockName]) == false) {
// ensure the lock will be deleted on exit
lockCreated = true;
var lock_list = ['backup', 'restore', 'markets', 'peers', 'masternodes'];
var lock_list = ['backup', 'restore', 'markets', 'peers', 'masternodes', 'plugin'];
// do not check the index lock if this is called from the reindex process
if (process.argv[2] == null || process.argv[2] != 'reindex') {
@@ -165,9 +163,8 @@ if (lib.is_locked([deleteLockName]) == false) {
// suppress the pid message when doing a reindex
if (process.argv[2] == null || process.argv[2] != 'reindex')
console.log("Script launched with pid: " + process.pid);
console.log(`${settings.localization.script_launched }: ${process.pid}`);
const settings = require('../lib/settings');
const mongoose = require('mongoose');
const dbString = `mongodb://${encodeURIComponent(settings.dbsettings.user)}:${encodeURIComponent(settings.dbsettings.password)}@${settings.dbsettings.address}:${settings.dbsettings.port}/${settings.dbsettings.database}`;
@@ -231,7 +228,7 @@ if (lib.is_locked([deleteLockName]) == false) {
exit(mongoose, 1);
});
} else {
console.log('Process aborted. Nothing was deleted.');
console.log(`${settings.localization.process_aborted}. ${settings.localization.nothing_was_deleted}.`);
exit(null, 2);
}
});
+4 -3
View File
@@ -1,3 +1,4 @@
const settings = require('../lib/settings');
const minNodeVersionMajor = '16';
const minNodeVersionMinor = '20';
const minNodeVersionRevision = '1';
@@ -54,7 +55,7 @@ function check_arguments_passed(cb) {
// check if the cmd result contains an @ symbol
if (splitResponse[1].indexOf('@') == -1) {
console.log('Installing pm2 module.. Please wait..');
console.log(`${settings.localization.installing_module.replace('{1}', 'pm2')}.. ${settings.localization.please_wait}..`);
// install pm2
exec(`npm install pm2@latest${(isWinOS ? ' -g' : '')}`, (err, stdout, stderr) => {
@@ -73,7 +74,7 @@ function check_arguments_passed(cb) {
// check if the cmd result contains an @ symbol
if (splitResponse[1].indexOf('@') == -1) {
console.log('Installing forever module.. Please wait..');
console.log(`${settings.localization.installing_module.replace('{1}', 'forever')}.. ${settings.localization.please_wait}..`);
// install forever
exec('npm install forever', (err, stdout, stderr) => {
@@ -133,7 +134,7 @@ check_arguments_passed(function(pidName, node_env) {
}
}
// Setting the NODE_ENV variable is more easily done from here seeing at the syntax changes slightly depending on operating system
// setting the NODE_ENV variable is more easily done from here seeing at the syntax changes slightly depending on operating system
execSync(`${(process.platform == 'win32' ? 'set' : 'export')} NODE_ENV=${node_env} && pm2 ${startOrReload} ./bin/instance -i 0 -n explorer -p "./tmp/pm2.pid" --node-args="--stack-size=10000" --update-env`, {stdio : 'inherit'});
break;
case 'forever':
+22 -25
View File
@@ -4,7 +4,9 @@ const lib = require('../lib/explorer');
const archiveSuffix = '.bak';
const oldArchiveSuffix = '.tar.gz';
const restoreLockName = 'restore';
const tarModule = 'tar';
const defaultBackupPath = path.join(path.dirname(__dirname), 'backups');
const settings = require('../lib/settings');
var lockCreated = false;
// exit function used to cleanup lock before finishing script
@@ -55,10 +57,10 @@ function check_module_directory_exists(dirName, cb) {
if (!fs.existsSync(`./node_modules/${dirName}`)) {
const { exec } = require('child_process');
console.log('Installing tar package.. Please wait..');
console.log(`${settings.localization.installing_module.replace('{1}', tarModule)}.. ${settings.localization.please_wait}..`);
// install tar module
exec('npm install tar', (err, stdout, stderr) => {
exec(`npm install ${tarModule}`, (err, stdout, stderr) => {
// always return true for now without checking results
return cb(true);
});
@@ -80,7 +82,7 @@ function drop_collection(mongoose, colName, cb) {
});
}
function delete_database(mongoose, settings, cb) {
function delete_database(mongoose, cb) {
const dbString = `mongodb://${encodeURIComponent(settings.dbsettings.user)}:${encodeURIComponent(settings.dbsettings.password)}@${settings.dbsettings.address}:${settings.dbsettings.port}/${settings.dbsettings.database}`;
console.log('Connecting to database..');
@@ -126,10 +128,10 @@ function delete_database(mongoose, settings, cb) {
});
}
function restore_backup(mongoose, settings, backupPath, extractedPath, gZip) {
function restore_backup(mongoose, backupPath, extractedPath, gZip) {
const { exec } = require('child_process');
console.log('Restoring backup.. Please wait..');
console.log(`${settings.localization.restoring_backup}.. ${settings.localization.please_wait}..`);
// restore mongo database from backup
const restoreProcess = exec(`mongorestore --host="${settings.dbsettings.address}" --port="${settings.dbsettings.port}" --username="${settings.dbsettings.user}" --password="${settings.dbsettings.password}" --authenticationDatabase="${settings.dbsettings.database}" ${(gZip ? `--gzip --archive="${backupPath}"` : `"${extractedPath}"`)}`);
@@ -204,17 +206,14 @@ if (process.argv[2] != null && process.argv[2] != '') {
console.log('You are about to delete the current eIquidus database and restore from backup.');
// prompt for restoring explorer database
rl.question('Are you sure you want to do this? [y/n]: ', function (restoreAnswer) {
rl.question(`${settings.localization.are_you_sure}: `, function (restoreAnswer) {
// stop prompting
rl.close();
// determine if the explorer database should be restored
switch (restoreAnswer) {
case 'y':
case 'Y':
case 'yes':
case 'YES':
case 'Yes':
switch ((restoreAnswer == null ? '' : restoreAnswer).toLowerCase()) {
case settings.localization.short_yes:
case settings.localization.long_yes:
// check if the "restore backup" process is already running
if (lib.is_locked([restoreLockName]) == false) {
// create a new restore lock before checking the rest of the locks to minimize problems with running scripts at the same time
@@ -222,11 +221,9 @@ if (process.argv[2] != null && process.argv[2] != '') {
// ensure the lock will be deleted on exit
lockCreated = true;
// check all other possible locks since restoring backups should not run at the same time that data is being changed
if (lib.is_locked(['backup', 'delete', 'index', 'markets', 'peers', 'masternodes']) == false) {
if (lib.is_locked(['backup', 'delete', 'index', 'markets', 'peers', 'masternodes', 'plugin']) == false) {
// all tests passed. OK to run restore
console.log("Script launched with pid: " + process.pid);
const settings = require('../lib/settings');
console.log(`${settings.localization.script_launched }: ${process.pid}`);
// check if this is a tar.gz (older explorer backup format)
if (!backupPath.endsWith(oldArchiveSuffix)) {
@@ -234,19 +231,19 @@ if (process.argv[2] != null && process.argv[2] != '') {
// newer backup format (.bak)
// delete all collections from existing database
delete_database(mongoose, settings, function(retVal) {
delete_database(mongoose, function(retVal) {
if (retVal) {
// move on to the restore process
restore_backup(mongoose, settings, backupPath, backupPath, true);
restore_backup(mongoose, backupPath, backupPath, true);
}
});
} else {
// older backup format (.tar.gz)
// check if the tar module is already installed
check_module_directory_exists('tar', function(retVal) {
const tar = require('tar');
check_module_directory_exists(tarModule, function(retVal) {
const tar = require(tarModule);
console.log('Extracting backup files.. Please wait..');
console.log(`${settings.localization.extracting_backup_files}.. ${settings.localization.please_wait}..`);
// extract the backup archive
tar.x({ file: backupPath, cwd: defaultBackupPath, gzip: true }, function() {
@@ -257,10 +254,10 @@ if (process.argv[2] != null && process.argv[2] != '') {
const mongoose = require('mongoose');
// delete all collections from existing database
delete_database(mongoose, settings, function(retVal) {
delete_database(mongoose, function(retVal) {
if (retVal) {
// move on to the restore process
restore_backup(mongoose, settings, backupPath, extractedPath, false);
restore_backup(mongoose, backupPath, extractedPath, false);
}
});
} else {
@@ -291,13 +288,13 @@ if (process.argv[2] != null && process.argv[2] != '') {
break;
default:
console.log('Process aborted. Nothing was restored.');
console.log(`${settings.localization.process_aborted}. ${settings.localization.nothing_was_restored}.`);
exit(null, 2);
}
});
} else {
// backup does not exist
console.log(`${backupPath} cannot be found`);
console.log(settings.localization.path_cannot_be_found.replace('{1}', backupPath));
exit(null, 2);
}
} else {
+1 -1
View File
@@ -47,7 +47,7 @@ if (validate_port(settings.webserver.port) == true) {
// send a kill signal to the process that is currently using the explorer's server port
exec(killcmd, (err, stdout, stderr) => {
// show shutdown msg
console.log('Explorer shutting down... Please wait...');
console.log(`${settings.localization.explorer_shutting_down}.. ${settings.localization.please_wait}..`);
process.exit(0);
});
} else {
+26 -12
View File
@@ -17,13 +17,13 @@ var stopSync = false;
// prevent stopping of the sync script to be able to gracefully shut down
process.on('SIGINT', () => {
console.log('Stopping sync process.. Please wait..');
console.log(`${settings.localization.stopping_sync_process}.. ${settings.localization.please_wait}..`);
stopSync = true;
});
// prevent killing of the sync script to be able to gracefully shut down
process.on('SIGTERM', () => {
console.log('Stopping sync process.. Please wait..');
console.log(`${settings.localization.stopping_sync_process}.. ${settings.localization.please_wait}..`);
stopSync = true;
});
@@ -513,7 +513,7 @@ function update_orphans(orphan_index, orphan_current, last_blockindex, timeout,
function get_earliest_orphan_block(orphan_index, orphan_current, last_blockindex, cb) {
// check if it is necessary to search for orphan data
if (orphan_index == null || orphan_index == 0) {
console.log('Finding the earliest orphaned blockindex.. Please wait..');
console.log(`${settings.localization.finding_earliest_orphan}.. ${settings.localization.please_wait}..`);
Tx.aggregate([
{ $match: {
@@ -1023,7 +1023,7 @@ function get_market_price(market_array) {
const coingecko = require('../lib/apis/coingecko');
const currency = lib.get_market_currency_code();
console.log('Calculating market price.. Please wait..');
console.log(`${settings.localization.calculating_market_price}.. ${settings.localization.please_wait}..`);
// get the market price from coingecko api
coingecko.get_market_prices(coingecko_id, currency, settings.markets_page.coingecko_api_key, function (err, last_price, last_usd_price) {
@@ -1050,7 +1050,7 @@ function get_market_price(market_array) {
});
} else {
// coingecko api returned an error
console.log(`Error: ${err}`);
console.log(`${settings.localization.ex_error}: ${err}`);
exit(1);
}
});
@@ -1060,7 +1060,7 @@ function get_market_price(market_array) {
}
});
} else {
console.log('Calculating market price.. Please wait..');
console.log(`${settings.localization.calculating_market_price}.. ${settings.localization.please_wait}..`);
// get the list of coins from coingecko
coingecko_coin_list_api(market_array, function (coin_err, coin_list) {
@@ -1350,10 +1350,24 @@ if (lib.is_locked([database]) == false) {
// check the backup, restore and delete locks since those functions would be problematic when updating data
if (lib.is_locked(['backup', 'restore', 'delete']) == false) {
// all tests passed. OK to run sync
console.log("Script launched with pid: " + process.pid);
console.log(`${settings.localization.script_launched }: ${process.pid}`);
if (mode == 'update')
console.log(`Syncing ${(database == 'index' ? 'blocks' : database)}.. Please wait..`);
if (mode == 'update') {
switch (database) {
case 'index':
console.log(`${settings.localization.syncing_blocks}.. ${settings.localization.please_wait}..`);
break;
case 'peers':
console.log(`${settings.localization.syncing_peers}.. ${settings.localization.please_wait}..`);
break;
case 'masternodes':
console.log(`${settings.localization.syncing_masternodes}.. ${settings.localization.please_wait}..`);
break;
default: // markets
console.log(`${settings.localization.syncing_markets}.. ${settings.localization.please_wait}..`);
break;
}
}
var dbString = 'mongodb://' + encodeURIComponent(settings.dbsettings.user);
dbString = dbString + ':' + encodeURIComponent(settings.dbsettings.password);
@@ -1396,7 +1410,7 @@ if (lib.is_locked([database]) == false) {
db.update_db(settings.coin.name, function(stats) {
// check if stats returned properly
if (stats !== false) {
console.log('Checking blocks.. Please wait..');
console.log(`${settings.localization.checking_blocks}.. ${settings.localization.please_wait}..`);
update_tx_db(settings.coin.name, block_start, stats.count, stats.txes, settings.sync.check_timeout, 1, function() {
// check if the script stopped prematurely
@@ -1466,7 +1480,7 @@ if (lib.is_locked([database]) == false) {
db.update_db(settings.coin.name, function(stats) {
// check if stats returned properly
if (stats !== false) {
console.log('Calculating tx count.. Please wait..');
console.log(`${settings.localization.calculating_tx_count}.. ${settings.localization.please_wait}..`);
// Resetting the transaction counter requires a single lookup on the txes collection to find all txes that have a positive or zero total and 1 or more vout
Tx.find({'total': {$gte: 0}, 'vout': { $gte: { $size: 1 }}}).countDocuments().then((count) => {
@@ -1493,7 +1507,7 @@ if (lib.is_locked([database]) == false) {
db.update_db(settings.coin.name, function(stats) {
// check if stats returned properly
if (stats !== false) {
console.log('Finding last blockindex.. Please wait..');
console.log(`${settings.localization.finding_last_blockindex}.. ${settings.localization.please_wait}..`);
// Resetting the last blockindex counter requires a single lookup on the txes collection to find the last indexed blockindex
Tx.find({}, {blockindex:1, _id:0}).sort({blockindex: -1}).limit(1).exec().then((tx) => {
+9 -10
View File
@@ -1,8 +1,8 @@
const { execSync } = require('child_process');
const fs = require('fs');
const argument = (process.argv[2] != null && process.argv[2] != '' && (process.argv[2] == 'explorer-only' || process.argv[2] == 'dependencies-only') ? process.argv[2] : '');
var reloadWebserver = false;
const settings = require('../lib/settings');
let reloadWebserver = false;
function exit() {
console.log('Explorer update complete');
@@ -33,7 +33,7 @@ if (argument == '' || argument == 'explorer-only') {
var commit = fs.readFileSync('./.git/refs/heads/master');
// update to newest explorer source
console.log('Downloading newest explorer code.. Please wait..\n');
console.log(`${settings.localization.downloading_newest_explorer_code}.. ${settings.localization.please_wait}..\n`);
try {
console.log('Git response:');
@@ -71,7 +71,7 @@ if (argument == '' || argument == 'dependencies-only') {
// check for outdated packages
try {
console.log((argument == 'dependencies-only' ? '' : '\n') +'Checking for outdated packages.. Please wait..');
console.log(`${(argument == 'dependencies-only' ? '' : '\n')}${settings.localization.check_outdated_packages}.. ${settings.localization.please_wait}..`);
execSync('npm outdated');
// all packages are up-to-date
@@ -86,7 +86,7 @@ if (argument == '' || argument == 'dependencies-only') {
// 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');
console.log(`${settings.localization.updating_explorer_packages}.. ${settings.localization.please_wait}..\n`);
execSync('npm update');
// check for outdated packages (again)
@@ -108,7 +108,7 @@ if (argument == '' || argument == 'dependencies-only') {
// check if the web server should be reloaded
if (reloadWebserver == true) {
console.log('Checking if webserver is running.. Please wait..\n');
console.log(`${settings.localization.checking_webserver_running}.. ${settings.localization.please_wait}..\n`);
const path = require('path');
const lib = require('../lib/explorer');
@@ -136,7 +136,7 @@ if (reloadWebserver == true) {
if (pidActive == true) {
// compile css and initialize database
init_database(function() {
console.log('\nReloading the explorer.. Please wait..\n');
console.log(`\n${settings.localization.reloading_explorer}.. ${settings.localization.please_wait}..\n`);
// reload pm2 using the zero-downtime reload function
execSync(`pm2 reload explorer`, {stdio : 'inherit'});
@@ -158,7 +158,7 @@ if (reloadWebserver == true) {
if (pidActive == true) {
// compile css and initialize database
init_database(function() {
console.log('\nReloading the explorer.. Please wait..\n');
console.log(`\n${settings.localization.reloading_explorer}.. ${settings.localization.please_wait}..\n`);
// reload forever using the restart function
execSync(`forever restart explorer`, {stdio : 'inherit'});
@@ -171,7 +171,6 @@ if (reloadWebserver == true) {
});
} 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) {
@@ -184,7 +183,7 @@ if (reloadWebserver == true) {
} else {
// compile css and initialize database
init_database(function() {
console.log('\nReloading the explorer.. Please wait..\n');
console.log(`\n${settings.localization.reloading_explorer}.. ${settings.localization.please_wait}..\n`);
// finish the script
exit();