Merge branch 'node-red:dev' into dev

This commit is contained in:
anshumanr
2024-04-22 02:40:18 -04:00
committed by GitHub
197 changed files with 44266 additions and 2833 deletions

View File

@@ -36,7 +36,7 @@ var connections = [];
const events = require("@node-red/util").events;
function handleCommsEvent(event) {
publish(event.topic,event.data,event.retain);
publish(event.topic,event.data,event.retain,event.session,event.excludeSession);
}
function handleStatusEvent(event) {
if (!event.status) {
@@ -74,13 +74,17 @@ function handleEventLog(event) {
publish("event-log/"+event.id,event.payload||{});
}
function publish(topic,data,retain) {
function publish(topic, data, retain, session, excludeSession) {
if (retain) {
retained[topic] = data;
} else {
delete retained[topic];
}
connections.forEach(connection => connection.send(topic,data))
connections.forEach(connection => {
if ((!session || connection.session === session) && (!excludeSession || connection.session !== excludeSession)) {
connection.send(topic,data)
}
})
}
@@ -109,6 +113,10 @@ var api = module.exports = {
*/
addConnection: async function(opts) {
connections.push(opts.client);
events.emit('comms:connection-added', {
session: opts.client.session,
user: opts.client.user
})
},
/**
@@ -126,6 +134,9 @@ var api = module.exports = {
break;
}
}
events.emit('comms:connection-removed', {
session: opts.client.session
})
},
/**
@@ -157,5 +168,23 @@ var api = module.exports = {
* @return {Promise<Object>} - resolves when complete
* @memberof @node-red/runtime_comms
*/
unsubscribe: async function(opts) {}
unsubscribe: async function(opts) {},
/**
* @param {Object} opts
* @param {User} opts.user - the user calling the api
* @param {CommsConnection} opts.client - the client connection
* @param {String} opts.topic - the message topic
* @param {String} opts.data - the message data
* @return {Promise<Object>} - resolves when complete
*/
receive: async function (opts) {
if (opts.topic) {
events.emit('comms:message:' + opts.topic, {
session: opts.client.session,
user: opts.user,
data: opts.data
})
}
}
};

View File

@@ -65,6 +65,25 @@ var api = module.exports = {
runtime.log.audit({event: "plugins.configs.get"}, opts.req);
return runtime.plugins.getPluginConfigs(opts.lang);
},
/**
* Gets the editor content for one registered plugin
* @param {Object} opts
* @param {User} opts.user - the user calling the api
* @param {User} opts.user - the user calling the api
* @param {Object} opts.req - the request to log (optional)
* @return {Promise<NodeInfo>} - the plugin information
* @memberof @node-red/runtime_plugins
*/
getPluginConfig: async function(opts) {
if (/[^0-9a-z=\-\*]/i.test(opts.lang)) {
throw new Error("Invalid language: "+opts.lang)
return;
}
runtime.log.audit({event: "plugins.configs.get"}, opts.req);
return runtime.plugins.getPluginConfig(opts.module, opts.lang);
},
/**
* Gets all registered module message catalogs
* @param {Object} opts

View File

@@ -106,14 +106,22 @@ async function evaluateEnvProperties(flow, env, credentials) {
result = { value: result, __clone__: true}
}
evaluatedEnv[name] = result
} else {
evaluatedEnv[name] = undefined
flow.error(`Error evaluating env property '${name}': ${err.toString()}`)
}
resolve()
});
}))
} else {
value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null);
if (typeof value === 'object') {
value = { value: value, __clone__: true}
try {
value = redUtil.evaluateNodeProperty(value, type, {_flow: flow}, null, null);
if (typeof value === 'object') {
value = { value: value, __clone__: true}
}
} catch (err) {
value = undefined
flow.error(`Error evaluating env property '${name}': ${err.toString()}`)
}
}
evaluatedEnv[name] = value

View File

@@ -22,6 +22,7 @@ var storage = require("./storage");
var library = require("./library");
var plugins = require("./plugins");
var settings = require("./settings");
const multiplayer = require("./multiplayer");
var express = require("express");
var path = require('path');
@@ -135,6 +136,7 @@ function start() {
.then(function() { return storage.init(runtime)})
.then(function() { return settings.load(storage)})
.then(function() { return library.init(runtime)})
.then(function() { return multiplayer.init(runtime)})
.then(function() {
if (settings.available()) {
if (settings.get('instanceId') === undefined) {

View File

@@ -0,0 +1,119 @@
let runtime
/**
* Active sessions, mapped by multiplayer session ids
*/
const sessions = new Map()
/**
* Active connections, mapping comms session to multiplayer session
*/
const connections = new Map()
function getSessionsList() {
return Array.from(sessions.values()).filter(session => session.active)
}
module.exports = {
init: function(_runtime) {
runtime = _runtime
runtime.events.on('comms:connection-removed', (opts) => {
const existingSessionId = connections.get(opts.session)
if (existingSessionId) {
connections.delete(opts.session)
const session = sessions.get(existingSessionId)
session.active = false
session.idleTimeout = setTimeout(() => {
sessions.delete(existingSessionId)
}, 30000)
runtime.events.emit('comms', {
topic: "multiplayer/connection-removed",
data: { session: existingSessionId }
})
}
})
runtime.events.on('comms:message:multiplayer/connect', (opts) => {
let session
if (!sessions.has(opts.data.session)) {
// Brand new session
let user = opts.user
if (!user || user.anonymous) {
user = user || { anonymous: true }
user.username = `Anon ${Math.floor(Math.random()*100)}`
}
session = {
session: opts.data.session,
user,
active: true
}
sessions.set(opts.data.session, session)
connections.set(opts.session, opts.data.session)
runtime.log.trace(`multiplayer new session:${opts.data.session} user:${user.username}`)
} else {
// Reconnected connection - keep existing state
connections.set(opts.session, opts.data.session)
// const existingConnection = connections.get(opts.data.session)
session = sessions.get(opts.data.session)
session.active = true
runtime.log.trace(`multiplayer reconnected session:${opts.data.session} user:${session.user.username}`)
clearTimeout(session.idleTimeout)
}
// Tell existing sessions about the new connection
runtime.events.emit('comms', {
topic: "multiplayer/connection-added",
excludeSession: opts.session,
data: session
})
// Send init info to new connection
const initPacket = {
topic: "multiplayer/init",
data: getSessionsList(),
session: opts.session
}
// console.log('<<', initPacket)
runtime.events.emit('comms', initPacket)
})
runtime.events.on('comms:message:multiplayer/disconnect', (opts) => {
const existingSessionId = connections.get(opts.session)
connections.delete(opts.session)
sessions.delete(existingSessionId)
runtime.events.emit('comms', {
topic: "multiplayer/connection-removed",
data: { session: existingSessionId, disconnected: true }
})
})
runtime.events.on('comms:message:multiplayer/location', (opts) => {
// console.log('>>>', opts.user, opts.data)
const sessionId = connections.get(opts.session)
const session = sessions.get(sessionId)
if (opts.user) {
if (session.user.anonymous !== opts.user.anonymous) {
session.user = opts.user
runtime.events.emit('comms', {
topic: 'multiplayer/connection-added',
excludeSession: opts.session,
data: session
})
}
}
session.location = opts.data
const payload = {
session: sessionId,
workspace: opts.data.workspace,
node: opts.data.node
}
runtime.events.emit('comms', {
topic: 'multiplayer/location',
data: payload,
excludeSession: opts.session
})
})
}
}

View File

@@ -173,7 +173,11 @@ function installModule(module,version,url) {
if (info.pending_version) {
events.emit("runtime-event",{id:"node/upgraded",retain:false,payload:{module:info.name,version:info.pending_version}});
} else {
events.emit("runtime-event",{id:"node/added",retain:false,payload:info.nodes});
if (!info.nodes.length && info.plugins.length) {
events.emit("runtime-event",{id:"plugin/added",retain:false,payload:info.plugins});
} else {
events.emit("runtime-event",{id:"node/added",retain:false,payload:info.nodes});
}
}
return info;
});

View File

@@ -7,5 +7,6 @@ module.exports = {
getPluginsByType: registry.getPluginsByType,
getPluginList: registry.getPluginList,
getPluginConfigs: registry.getPluginConfigs,
getPluginConfig: registry.getPluginConfig,
exportPluginSettings: registry.exportPluginSettings
}

View File

@@ -25,6 +25,7 @@
"removing-modules": "Removing modules from config",
"added-types": "Added node types:",
"removed-types": "Removed node types:",
"removed-plugins": "Removed plugins:",
"install": {
"invalid": "Invalid module name",
"installing": "Installing module: __name__, version: __version__",

View File

@@ -1,6 +1,6 @@
{
"name": "@node-red/runtime",
"version": "4.0.0-beta.1",
"version": "4.0.0-beta.2",
"license": "Apache-2.0",
"main": "./lib/index.js",
"repository": {
@@ -16,11 +16,11 @@
}
],
"dependencies": {
"@node-red/registry": "4.0.0-beta.1",
"@node-red/util": "4.0.0-beta.1",
"@node-red/registry": "4.0.0-beta.2",
"@node-red/util": "4.0.0-beta.2",
"async-mutex": "0.4.0",
"clone": "2.1.2",
"express": "4.18.2",
"express": "4.19.2",
"fs-extra": "11.1.1",
"json-stringify-safe": "5.0.1"
}