mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge branch 'node-red:dev' into dev
This commit is contained in:
@@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
119
packages/node_modules/@node-red/runtime/lib/multiplayer/index.js
vendored
Normal file
119
packages/node_modules/@node-red/runtime/lib/multiplayer/index.js
vendored
Normal 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
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -7,5 +7,6 @@ module.exports = {
|
||||
getPluginsByType: registry.getPluginsByType,
|
||||
getPluginList: registry.getPluginList,
|
||||
getPluginConfigs: registry.getPluginConfigs,
|
||||
getPluginConfig: registry.getPluginConfig,
|
||||
exportPluginSettings: registry.exportPluginSettings
|
||||
}
|
||||
@@ -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__",
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user