Created
January 22, 2021 03:59
-
-
Save TroublesNCuddles/b20a60ca0a318df2e7273c886211269d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* | |
* DEPENDENCIES | |
* | |
*/ | |
const fs = require('fs'); | |
const path = require('path'); | |
const JustAnotterPlatform = require('../src/index.js'); | |
const {Logger, LEVELS: LogLevels} = require('../src/utilities/logger.js'); | |
const {mergeDeep} = require('../src/utilities/index.js'); | |
const NPMPackage = require('../package.json'); | |
/* | |
* | |
* CONSTANTS | |
* | |
*/ | |
const ENVIRONMENT_PREFIX = 'OTTERPLATFORM'; | |
const ENVIRONMENT_SEPARATOR = '__'; | |
const CONFIGURATIONS = ['modules.json', 'permissions.json']; | |
const CONFIGURATION_PATH = path.join('lib', 'configs'); | |
/* | |
* | |
* FUNCTIONS | |
* | |
*/ | |
/** | |
* | |
* This function takes your object of variables (Usually provided by process.env) and filters out any entries | |
* that doesn't start with the defined prefix and separator joined. | |
* | |
* @param environment_variables your object of variables to filter, usually from process.env | |
* @returns {{}} the input filtered | |
*/ | |
const filterEnvironmentVariables = (environment_variables) => { | |
const prefix = [ENVIRONMENT_PREFIX, ENVIRONMENT_SEPARATOR].join(''); | |
return Object.entries(environment_variables) | |
.filter(([key]) => key.toUpperCase().startsWith(prefix)) | |
.reduce((previous, [key, value]) => { | |
previous[key] = value; | |
return previous; | |
}, {}); | |
}; | |
/** | |
* | |
* This cleans up the variables (that should've been filtered) by removing the prefix | |
* | |
* @param environment_variables | |
* @returns {{}} | |
*/ | |
const removedPrefixFromEnvironmentVariables = (environment_variables) => { | |
const prefix = [ENVIRONMENT_PREFIX, ENVIRONMENT_SEPARATOR].join('').toUpperCase(); | |
return Object.entries(environment_variables) | |
.reduce((previous, [key, value]) => { | |
previous[key.toUpperCase().startsWith(prefix) ? key.slice(prefix.length) : key] = value; | |
return previous; | |
}, {}); | |
}; | |
/** | |
* | |
* This splits the path using ENVIRONMENT_SEPARATOR | |
* | |
* @param path | |
* @returns {*|string[]} | |
*/ | |
const splitEnvironmentPath = (path) => { | |
return path.split(ENVIRONMENT_SEPARATOR); | |
}; | |
/** | |
* | |
* This will build an options object from the provided set of variables. | |
* The variables should ideally be filtered and cleaned prior to running this. | |
* | |
* @param filtered_environment_variables | |
* @returns {{}} | |
*/ | |
const buildOptionsObject = (filtered_environment_variables) => { | |
return Object.entries(filtered_environment_variables).reduce((previous, [key, value]) => { | |
const key_parts = splitEnvironmentPath(key); | |
const first_key = key_parts[0]; | |
if (key_parts.length === 1) { | |
previous[first_key] = value; | |
return previous; | |
} | |
previous[first_key] = fillObject(key_parts.slice(1), value, previous[first_key] || {}); | |
return previous; | |
}, {}); | |
}; | |
/** | |
* | |
* This fills the object with the value using the keys as the path to aid in building the options object | |
* | |
* @param keys | |
* @param value | |
* @param obj | |
* @returns {*} | |
*/ | |
const fillObject = (keys, value, obj) => { | |
const first_key = keys[0]; | |
if (keys.length === 1) { | |
obj[first_key] = value; | |
return obj; | |
} | |
obj[first_key] = fillObject(keys.slice(1), value, obj[first_key] || {}); | |
return obj; | |
}; | |
/** | |
* | |
* This is used to get the working directory from either fivem's GetResourcePath or process.cwd | |
* | |
* @returns {string} the working directory. | |
*/ | |
const getWorkingDirectory = () => { | |
return typeof GetResourcePath === 'function' ? GetResourcePath(GetCurrentResourceName()) : process.cwd(); | |
}; | |
/** | |
* | |
* This is just a wrapper for fs.readFile, with the encoding set to utf8 and to return a profile. | |
* | |
* @param file | |
* @returns {Promise} | |
*/ | |
const readFile = file => { | |
return new Promise((resolve, reject) => { | |
fs.readFile(file, {encoding: 'utf8'}, (error, file) => { | |
if (error) { | |
return reject(error); | |
} | |
return resolve(file); | |
}); | |
}); | |
}; | |
/** | |
* | |
* This will read a configuration file and parse the JSON. | |
* | |
* @param configuration_file | |
* @returns {Promise<any>} | |
*/ | |
const loadConfiguration = async (configuration_file) => { | |
const content = await readFile(configuration_file); | |
return {[path.parse(configuration_file).name]: JSON.parse(content)}; | |
}; | |
/** | |
* | |
* This will get an ordered list of applicable configurations to load into memory | |
* | |
* @returns {(string)[]} | |
*/ | |
const getApplicableConfigFiles = (entry_configurations) => { | |
return [...entry_configurations, ...CONFIGURATIONS].filter(element => !!element); | |
}; | |
/** | |
* | |
* @param file_name | |
* @returns {string} | |
*/ | |
const getFullPathForConfiguration = (file_name) => { | |
return path.join(getWorkingDirectory(), CONFIGURATION_PATH, file_name); | |
}; | |
/* | |
* | |
* BOOTSTRAP | |
* | |
*/ | |
//Initialize the loggers | |
const logger_app = new Logger({ | |
level: LogLevels.INFO, | |
components: typeof GetCurrentResourceName === 'function' ? [GetCurrentResourceName()] : [] | |
}); | |
const logger_bootstrap = logger_app.createNewLogger('Boostrap'); | |
(async () => { | |
logger_bootstrap.info({ | |
message: 'Boostrapping %s v%s with working directory: "%s"', | |
message_data: [ | |
NPMPackage.name, | |
NPMPackage.version, | |
getWorkingDirectory() | |
] | |
}); | |
//Get filtered environment variables first | |
const filtered = filterEnvironmentVariables(process.env); | |
logger_bootstrap.info({ | |
message: 'Found %d environmental %s', | |
message_data: [Object.keys(filtered).length, Object.keys(filtered).length === 1 ? 'option' : 'options'] | |
}); | |
//Removing the prefix that was defined CONSTANTS/ENVIRONMENT_PREFIX | |
const no_prefix = removedPrefixFromEnvironmentVariables(filtered); | |
//Build the environmental options | |
const environmental_options = buildOptionsObject(no_prefix); | |
//Get a list of all of the applicable configurations that we should load | |
const config_files = getApplicableConfigFiles(((environmental_options || {}).config_files || '').split(',')); | |
logger_bootstrap.info({ | |
message: 'Loading the following configurations: %s', | |
message_data: [config_files.join(', ')] | |
}); | |
//Convert the configuration file names into full paths, followed up by reading their respective files. | |
const configs = await Promise.all( | |
config_files | |
.map(file_name => getFullPathForConfiguration(file_name)) | |
.map(config_file => loadConfiguration(config_file, config_file)) | |
); | |
//Merge all of the configurations together into one object. | |
const configuration = mergeDeep(...configs, environmental_options); | |
//Inform the Server Administrator of our configuration accomplishments | |
logger_bootstrap.info({ | |
message: 'Loaded configurations with %d PRIMARY key%s', | |
message_data: [Object.keys(configuration).length, Object.keys(configuration).length > 1 ? 's' : ''] | |
}); | |
//Check if there's a log level present | |
if (configuration.log_level) { | |
//Convert it to the number that our logger will understand | |
const log_level = logger_app.getLevelFromString(configuration.log_level); | |
//Verify that the level is valid before proceeding | |
if (log_level !== undefined) { | |
//Set the levels for the loggers | |
logger_app.setLevel(log_level); | |
logger_bootstrap.setLevel(log_level); | |
} | |
} | |
//Initialize our App and return the run promise to be handled by the bootstrap's promise handler. | |
return (new JustAnotterPlatform(configuration, logger_app)).run(); | |
})() | |
.then(() => { | |
logger_bootstrap.info('Completed'); | |
}) | |
.catch(e => { | |
logger_bootstrap.fatal(e); | |
return process.exit(1); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment