Skip to content
793 changes: 628 additions & 165 deletions index.js

Large diffs are not rendered by default.

31 changes: 12 additions & 19 deletions lib/AgentManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,6 @@ class AgentManager {
forgeURL: this.options.ffUrl,
token: Buffer.from(this.options.otc).toString('base64')
}
let success = false
let postResponse = null

const url = new URL('/api/v1/devices/', provisioningConfig.forgeURL)
Expand Down Expand Up @@ -290,30 +289,24 @@ class AgentManager {
json: { setup: true, agentHost: host || ip },
agent: getHTTPProxyAgent(provisioningConfig.forgeURL, { timeout: 10000 })
})

if (postResponse?.statusCode !== 200) {
throw new Error(`${postResponse.statusMessage} (${postResponse.statusCode})`)
}
success = true
} catch (err) {
warn(`Problem encountered during provisioning: ${err.toString()}`)
success = false
}

if (!success) {
return false
// err is an error from GOT. What is the status code of the response?
const statusCode = err?.response?.statusCode
if (statusCode === 401) {
throw new Error('One-Time Code is invalid or has already been used. Please generate a new one and try again.')
} else {
throw new Error(`Problem encountered during provisioning: ${err.toString()}`)
}
}

try {
// * At this point, the one-time-code is spent (deleted) and we have all the info we need to update the config
const provisioningData = JSON.parse(postResponse.body)
provisioningData.forgeURL = provisioningData.forgeURL || provisioningConfig.forgeURL
await this._provisionDevice(provisioningData)
return provisioningData
} catch (err) {
warn(`Error provisioning device: ${err.toString()}`)
throw err
}
// * At this point, the one-time-code is spent (deleted) and we have all the info we need to update the config
const provisioningData = JSON.parse(postResponse.body)
provisioningData.forgeURL = provisioningData.forgeURL || provisioningConfig.forgeURL
await this._provisionDevice(provisioningData)
return provisioningData
Comment thread
knolleary marked this conversation as resolved.
}

async canBeProvisioned (provisioningConfig) {
Expand Down
7 changes: 7 additions & 0 deletions lib/cli/args.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,5 +151,12 @@ module.exports = [
type: Boolean,
alias: 'j',
group: 'global'
},
{
name: 'no-interactive',
description: 'disable interactive prompts',
type: Boolean,
defaultValue: false,
group: 'global'
}
]
30 changes: 22 additions & 8 deletions lib/cli/flowsImporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,21 @@ const chalk = require('yoctocolors-cjs') // switch to the lighter yoctocolors-cj
const { createRawItem } = require('./fileSelector/utils/item.js')
const select = require('@inquirer/select').default
const figures = require('@inquirer/figures').default
const print = (message, /** @type {figures} */ figure = chalk.gray(figures.lineBold)) => console.info(figure ?? chalk.gray(figures.lineBold), message)
const fileSelector = require('./fileSelector/index.js').fileSelector

const CHOICES = {
SKIP: false, // skip import
BROWSE: -2 // browse filesystem
}

const defaultInquirerTheme = {
prefix: '',
style: {
// Give some spacing to the messages
message: (text, status) => { return '\n' + chalk.bold(text) + '\n' }
}
}

/**
* Asks the user if they want to import existing Node-RED flows
*
Expand Down Expand Up @@ -88,16 +95,17 @@ async function askImport (suggestedDirs) {
value: CHOICES.BROWSE,
description: 'Press <enter> to browse the filesystem for Node-RED flows files'
})
console.info()

const message = choices.length > 2 ? 'We have found existing Node-RED flows. Do you want to import these into your new instance?' : 'Do you want to import existing Node-RED flows into your new instance?'
// Present the options to the user
choice = await select({
message: 'Import existing Node-RED flows?',
message,
choices,
default: defaultChoice,
pageSize: 10,
instructions: { navigation: chalk.gray('Use arrow keys to select an option, press <enter> to confirm') }
})
instructions: { navigation: chalk.gray('Use arrow keys to select an option, press <enter> to confirm') },
theme: defaultInquirerTheme
}, { clearPromptOnDone: true })
const suggestedFlowChosen = choice?.isFlowsFile === true
const suggestedDirChosen = choice && !suggestedFlowChosen && suggestedDirDetails.find(dir => dir.userDir === choice.userDir && dir.valid)
if (choice === CHOICES.BROWSE || suggestedDirChosen || suggestedFlowChosen) {
Expand Down Expand Up @@ -284,12 +292,14 @@ async function flowImport (suggestedDirs) {
}
const selectedFlows = getFlowsFileDetails(importDetails.dir, importDetails.name)
if (selectedFlows) {
console.info()
console.info('Importing flows:')
console.info(` ${figures.bullet} Flow file: ${chalk.cyan(selectedFlows.flowsFile)}`)
const userDir = selectedFlows.userDir
selectedFlows.credentials = {} // default to empty credentials
selectedFlows.credentialSecret = null // default to null
selectedFlows.packageFile = importDetails.packageFile
if (selectedFlows.credsFile && existsSync(selectedFlows.credsFile)) {
print(`Importing credentials '${selectedFlows.credsFile}'...`)
selectedFlows.credentials = await fsPromises.readFile(selectedFlows.credsFile, 'utf8')
selectedFlows.credentials = JSON.parse(selectedFlows.credentials)
// now, see if we can locate the credentialSecret for the creds file
Expand Down Expand Up @@ -318,13 +328,17 @@ async function flowImport (suggestedDirs) {
}

if (!selectedFlows.credentialSecret) {
print('Could not determine the credentials secret. Flows will be imported without credentials.', chalk.yellow(figures.warning))
// Not strictly true, but at this point, not much the user can do
console.info(` ${figures.bullet} No credentials file found`)
selectedFlows.credentials = {}
} else {
console.info(` ${figures.bullet} Credentials file: ${chalk.cyan(selectedFlows.credsFile)}`)
}
} else {
console.info(` ${figures.bullet} No credentials file found`)
selectedFlows.credentials = {}
}
print(`Importing package '${selectedFlows.packageFile}'...`)
console.info(` ${figures.bullet} Package file: ${chalk.cyan(selectedFlows.packageFile)}`)
selectedFlows.package = utils.getPackageData(selectedFlows.packageFile, { convertFileModulesToLatest: true })
return selectedFlows
}
Expand Down
Loading