Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@flowfuse/device-agent

Package Overview
Dependencies
Maintainers
3
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@flowfuse/device-agent - npm Package Compare versions

Comparing version 2.1.0 to 2.2.0

lib/auditLogger/index.js

6

CHANGELOG.md

@@ -0,1 +1,7 @@

#### 2.2.0: Release
- Wire up Node-RED instance audit events to FF (#232) @Steve-Mcl
- Implement deferred stop like nr-launcher (#236) @Steve-Mcl
- Fix theme following org name change (#235) @Steve-Mcl
#### 2.1.0: Release

@@ -2,0 +8,0 @@

93

lib/launcher.js

@@ -5,3 +5,4 @@ const childProcess = require('child_process')

const path = require('path')
const { info, debug, warn, NRlog } = require('./logging/log')
const { log, info, debug, warn, NRlog } = require('./logging/log')
const { copyDir } = require('./utils')

@@ -11,2 +12,5 @@ const MIN_RESTART_TIME = 10000 // 10 seconds

/** How long wait for Node-RED to cleanly stop before killing */
const NODE_RED_STOP_TIMEOUT = 10000
const packageJSONTemplate = {

@@ -34,2 +38,8 @@ name: 'flowfuse-project',

this.auditLogURL = `${this.config.forgeURL}/logging/device/${this.config.deviceId}/audit`
// A callback function that will be set if the launcher is waiting
// for Node-RED to exit
this.exitCallback = null
this.projectDir = path.join(this.config.dir, 'project')

@@ -43,3 +53,3 @@

userSettings: path.join(this.projectDir, 'settings.json'),
themeDir: path.join(this.projectDir, 'node_modules/@flowforge/nr-theme'),
themeDir: path.join(this.projectDir, 'node_modules', '@flowfuse', 'nr-theme'),
npmrc: path.join(this.projectDir, '.npmrc')

@@ -183,2 +193,8 @@ }

teamID,
deviceId: this.config.deviceId,
auditLogger: {
url: this.auditLogURL,
token: this.config.token,
bin: path.join(__dirname, 'auditLogger', 'index.js')
},
projectLink

@@ -282,6 +298,6 @@ }

info('Updating theme files')
const sourceDir1 = path.join(__dirname, '..', 'node_modules', '@flowforge', 'nr-theme')
const sourceDir1 = path.join(__dirname, '..', 'node_modules', '@flowfuse', 'nr-theme')
const sourceDir2 = path.join(__dirname, '..', '..', 'nr-theme')
const sourceDir = existsSync(sourceDir1) ? sourceDir1 : sourceDir2
const targetDir = path.join(this.projectDir, 'node_modules', '@flowforge', 'nr-theme')
const targetDir = path.join(this.projectDir, 'node_modules', '@flowfuse', 'nr-theme')
try {

@@ -295,3 +311,3 @@ if (!existsSync(sourceDir)) {

}
await fs.cp(sourceDir, targetDir, { recursive: true })
await copyDir(sourceDir, targetDir, { recursive: true })
} catch (error) {

@@ -303,2 +319,5 @@ info(`Could not write theme files to disk: '${targetDir}'`)

async start () {
if (this.deferredStop) {
await this.deferredStop
}
this.state = 'starting'

@@ -368,2 +387,3 @@ if (!existsSync(this.projectDir) ||

debug(`CMD: ${execPath} ${processArgs.join(' ')}`)
/** @type {childProcess.ChildProcess} */
this.proc = childProcess.spawn(

@@ -404,2 +424,8 @@ execPath,

}
} else {
// is really shutting down (i.e. was commanded by the
// agent, so we won't be doing an auto restart)
if (this.exitCallback) {
this.exitCallback()
}
}

@@ -428,18 +454,51 @@ })

info('Stopping Node-RED')
// something wrong here, want to wait until child is dead
if (this.deferredStop) {
// A stop request is already inflight - return the existing deferred object
return this.deferredStop
}
/** Operations that should be performed after the process has exited */
const postShutdownOps = async () => {
if (clean) {
info('Cleaning instance directory')
try {
await fs.rm(this.projectDir, { force: true, recursive: true })
} catch (err) {
warn('Error cleaning instance directory', err)
}
}
}
if (this.proc) {
this.shuttingDown = true
const exitPromise = new Promise(resolve => {
this.proc.on('exit', resolve)
// Setup a promise that will resolve once the process has really exited
this.deferredStop = new Promise((resolve, reject) => {
// Setup a timeout so we can more forcefully kill Node-RED
this.exitTimeout = setTimeout(async () => {
log('Node-RED stop timed-out. Sending SIGKILL', 'system')
if (this.proc) {
this.proc.kill('SIGKILL')
}
}, NODE_RED_STOP_TIMEOUT)
// Setup a callback for when the process has actually exited
this.exitCallback = async () => {
clearTimeout(this.exitTimeout)
this.exitCallback = null
this.deferredStop = null
this.exitTimeout = null
this.proc && this.proc.unref()
this.proc = undefined
await postShutdownOps()
resolve()
}
// Send a kill signal. On Linux this will be a SIGTERM and
// allow Node-RED to shutdown cleanly. Windows looks like it does
// it more forcefully by default.
this.proc.kill()
this.state = 'stopped'
})
this.proc.kill('SIGINT')
await exitPromise
info('Stopped Node-RED')
return this.deferredStop
} else {
this.state = 'stopped'
await postShutdownOps()
}
this.state = 'stopped'
if (clean) {
info('Cleaning instance directory')
await fs.rm(this.projectDir, { force: true, recursive: true })
}
}

@@ -446,0 +505,0 @@ }

@@ -122,2 +122,16 @@ const settings = require('./settings.json')

if (settings.flowforge.auditLogger?.bin && settings.flowforge.auditLogger?.url) {
try {
runtimeSettings.logging.auditLogger = {
level: 'off',
audit: true,
handler: require(settings.flowforge.auditLogger.bin),
loggingURL: settings.flowforge.auditLogger.url,
token: settings.flowforge.auditLogger.token
}
} catch (e) {
console.warn('Could not initialise device audit logging. Audit events will not be logged to the platform')
}
}
if (settings.https) {

@@ -124,0 +138,0 @@ ;['key', 'ca', 'cert'].forEach(key => {

@@ -0,1 +1,4 @@

const path = require('path')
const fs = require('fs').promises
module.exports = {

@@ -5,3 +8,4 @@ compareNodeRedData,

isObject,
hasProperty
hasProperty,
copyDir
}

@@ -81,1 +85,34 @@

}
/**
* Copy a directory from one location to another
* @param {string} src - source directory
* @param {string} dest - destination directory
* @param {Object} [options] - options
* @param {boolean} [options.recursive=true] - whether to copy recursively (default: true)
*/
async function copyDir (src, dest, { recursive = true } = {}) {
// for nodejs v 16.7.0 and later, fs.cp will be available
if (fs.cp && typeof fs.cp === 'function') {
await fs.cp(src, dest, { recursive })
return
}
// fallback to own implementation of recursive copy (for Node.js 14)
// TODO: remove this when Node.js 14 is no longer supported by the device agent
const cp = async (src, dest) => {
const lstat = await fs.lstat(src).catch(_err => { })
if (!lstat) {
// do nothing
} else if (lstat.isFile()) {
await fs.copyFile(src, dest)
} else if (lstat.isDirectory()) {
await fs.mkdir(dest).catch(_err => { })
if (recursive) {
for (const f of await fs.readdir(src)) {
await cp(path.join(src, f), path.join(dest, f))
}
}
}
}
await cp(src, dest)
}

5

package.json
{
"name": "@flowfuse/device-agent",
"version": "2.1.0",
"version": "2.2.0",
"description": "An Edge Agent for running Node-RED instances deployed from the FlowFuse Platform",
"exports": {
"./libraryPlugin": "./lib/plugins/libraryPlugin.js"
"./libraryPlugin": "./lib/plugins/libraryPlugin.js",
"./auditLogger": "./lib/auditLogger/index.js"
},

@@ -8,0 +9,0 @@ "main": "index.js",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc