|
|
|
// Re-export the features modules in this folder, which each add, remove, and admininster one AO feature (imported in project index.js)
|
|
|
|
// Also contains the Features menus to control these features in this directory
|
|
|
|
import chalk from 'chalk'
|
|
|
|
import inquirer from 'inquirer'
|
|
|
|
import fs from 'fs'
|
|
|
|
import { lsFolder } from '../files.js'
|
|
|
|
import { fileURLToPath } from 'url'
|
|
|
|
import path from 'path'
|
|
|
|
import { headerStyle, greenChalk, styledStatus } from '../styles.js'
|
|
|
|
import { spinner } from '../console.js'
|
|
|
|
|
|
|
|
const __filename = fileURLToPath(import.meta.url)
|
|
|
|
const __dirname = path.dirname(__filename)
|
|
|
|
|
|
|
|
const loadFeatures = async () => {
|
|
|
|
|
|
|
|
let filenames = lsFolder(path.join(__dirname))
|
|
|
|
let features = {}
|
|
|
|
for(let i = 0; i < filenames.length; i++) {
|
|
|
|
const filename = filenames[i]
|
|
|
|
if(filename === 'index.js') continue
|
|
|
|
const moduleShortname = filename.replace(/\.js$/, '')
|
|
|
|
const path = './' + filename
|
|
|
|
features[moduleShortname] = (await import(path)).default
|
|
|
|
}
|
|
|
|
return features
|
|
|
|
}
|
|
|
|
|
|
|
|
const features = await loadFeatures()
|
|
|
|
export default features
|
|
|
|
|
|
|
|
// Prints the Configure AO Features menu and executes the user's choice
|
|
|
|
let featuresChoices
|
|
|
|
export async function featuresMenu(previousMenuChoice = 0) {
|
|
|
|
console.log(`\n${headerStyle('Configure AO Features')}`)
|
|
|
|
const stopSpinner = spinner('Loading status...')
|
|
|
|
let loadedFeatures = 0
|
|
|
|
if(!featuresChoices) {
|
|
|
|
featuresChoices = Object.entries(features).map(([featureKey, feature]) => {
|
|
|
|
let featureName = featureKey
|
|
|
|
if(feature.hasOwnProperty('name') && feature.name.length >= 1) {
|
|
|
|
featureName = feature.name
|
|
|
|
}
|
|
|
|
const nameColumn = featureName.padEnd(17)
|
|
|
|
const status = feature.status() || 'Unknown'
|
|
|
|
if(status !== 'Unknown') {
|
|
|
|
loadedFeatures++
|
|
|
|
}
|
|
|
|
const statusColumn = styledStatus(status) + ' '
|
|
|
|
const descriptionColumn = feature.description || ''
|
|
|
|
const choice = { name: nameColumn + statusColumn + descriptionColumn, value: featureKey, short: featureKey}
|
|
|
|
return choice
|
|
|
|
})
|
|
|
|
featuresChoices.push(
|
|
|
|
'Back to Main Menu'
|
|
|
|
)
|
|
|
|
} else {
|
|
|
|
loadedFeatures = featuresChoices.filter(feature => {
|
|
|
|
return typeof feature === 'object' && !feature.name.includes('Unknown')
|
|
|
|
}).length
|
|
|
|
}
|
|
|
|
stopSpinner('Loaded status for ' + loadedFeatures + '/' + (featuresChoices.length - 1) + ' features.')
|
|
|
|
let answer
|
|
|
|
try {
|
|
|
|
answer = await inquirer.prompt({
|
|
|
|
name: 'features_menu',
|
|
|
|
type: 'list',
|
|
|
|
message: 'Please choose:',
|
|
|
|
choices: featuresChoices,
|
|
|
|
default: previousMenuChoice,
|
|
|
|
pageSize: featuresChoices.length
|
|
|
|
})
|
|
|
|
} catch(error) {
|
|
|
|
if (error === 'EVENT_INTERRUPTED') {
|
|
|
|
console.log('\nESC')
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(answer.features_menu === 'Back to Main Menu') {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
const chosenFeature = features[answer.features_menu]
|
|
|
|
const chosenFeatureName = (chosenFeature.hasOwnProperty('name') && chosenFeature.name.length >= 1) ? chosenFeature.name : answer.features_menu
|
|
|
|
while(await oneFeatureMenu(chosenFeatureName, chosenFeature)) {}
|
|
|
|
return answer.features_menu
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prints the menu options for a specific feature.
|
|
|
|
// Each feature module can export functions for status, install, version, update and these will be listed in the menu based on context
|
|
|
|
// If the module also has a menu: field, these menu items will each be appended if the name is truthy when calculated
|
|
|
|
// Prints all the standard-named features plus features listed under 'menu'
|
|
|
|
export async function oneFeatureMenu(name, feature) {
|
|
|
|
console.log(`\n${headerStyle(name)}`)
|
|
|
|
if(feature.description && feature.description?.length >= 1) {
|
|
|
|
console.log('\n' + feature.description + '\n')
|
|
|
|
}
|
|
|
|
const featureChoices = []
|
|
|
|
const stopSpinner = spinner('Loading status...')
|
|
|
|
const status = feature?.status() || false
|
|
|
|
stopSpinner('Loaded ' + name + ' status: ' + (feature.status() || 'Unknown') + '\n')
|
|
|
|
if(!status) {
|
|
|
|
console.log("This AO feature module lacks a status() function, not sure which menu items to display.")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if(status === 'off') {
|
|
|
|
if(feature.hasOwnProperty('install')) {
|
|
|
|
featureChoices.push({ name: 'Install ' + name, value: 'install' })
|
|
|
|
menuItemCount++
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if(feature.hasOwnProperty('update')) {
|
|
|
|
featureChoices.push({ name: 'Update ' + name, value: 'update'})
|
|
|
|
}
|
|
|
|
if(feature.hasOwnProperty('uninstall')) {
|
|
|
|
featureChoices.push({ name: 'Uninstall ' + name, value: 'uninstall'})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(feature.hasOwnProperty('menu')) {
|
|
|
|
feature.menu.forEach(menuItem => {
|
|
|
|
const menuItemName = typeof menuItem.name === 'function' ? menuItem.name() : menuItem.name
|
|
|
|
if(!menuItemName) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
const menuItemValue = typeof menuItem.value === 'function' ? menuItem.value() : menuItem.value
|
|
|
|
// todo: uninstall option will go here also
|
|
|
|
featureChoices.push({ name: menuItemName, value: menuItemValue })
|
|
|
|
})
|
|
|
|
}
|
|
|
|
if(featureChoices.length < 1) {
|
|
|
|
console.log("Nothing to do yet on this feature, please check back soon.")
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
featureChoices.push(
|
|
|
|
'Back to Features'
|
|
|
|
)
|
|
|
|
let answer
|
|
|
|
try {
|
|
|
|
answer = await inquirer.prompt({
|
|
|
|
name: 'feature_menu',
|
|
|
|
type: 'list',
|
|
|
|
message: 'Please choose:',
|
|
|
|
choices: featureChoices
|
|
|
|
})
|
|
|
|
} catch(error) {
|
|
|
|
if (error === 'EVENT_INTERRUPTED') {
|
|
|
|
console.log('\nESC')
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(answer.feature_menu === 'Back to Features') {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if(Object.keys(feature).includes(answer.feature_menu)) {
|
|
|
|
await feature[answer.feature_menu]()
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
console.log('Not yet implemented')
|
|
|
|
return true
|
|
|
|
}
|