mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Compare commits
2 Commits
4101-merge
...
quick-add-
Author | SHA1 | Date | |
---|---|---|---|
|
37524e05bb | ||
|
3fbfd47089 |
14
.github/workflows/tests.yml
vendored
14
.github/workflows/tests.yml
vendored
@@ -19,9 +19,9 @@ jobs:
|
||||
matrix:
|
||||
node-version: [16, 18]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install Dependencies
|
||||
@@ -29,8 +29,8 @@ jobs:
|
||||
- name: Run tests
|
||||
run: |
|
||||
npm run test
|
||||
# - name: Publish to coveralls.io
|
||||
# if: ${{ matrix.node-version == 16 }}
|
||||
# uses: coverallsapp/github-action@v1.1.2
|
||||
# with:
|
||||
# github-token: ${{ github.token }}
|
||||
- name: Publish to coveralls.io
|
||||
if: ${{ matrix.node-version == 16 }}
|
||||
uses: coverallsapp/github-action@v1.1.2
|
||||
with:
|
||||
github-token: ${{ github.token }}
|
||||
|
51
CHANGELOG.md
51
CHANGELOG.md
@@ -1,55 +1,6 @@
|
||||
#### 3.1.0-beta.2: Beta Release
|
||||
|
||||
Editor
|
||||
|
||||
- NEW: Add change icon to tabs (#4068) @knolleary
|
||||
- NEW: Complete overhaul of Group UX (#4079) @knolleary
|
||||
- NEW: Add link to node help in node edit dialog footer (#4065) @knolleary
|
||||
- NEW: Added editor feature for connecting multiple nodes to single node (#4051) @sonntam
|
||||
- NEW: Increase workspace size to 8000x8000 (#4094) @knolleary
|
||||
- Ensure node buttons are redrawn when flow lock state is changed (#4091) @knolleary
|
||||
- Prevent loops being created with junction nodes (#4087) @knolleary
|
||||
- Prevent opening locked node's edit dialog (#4069) @knolleary
|
||||
- Reverse direction of tab scroll to expected direction (#4064) @knolleary
|
||||
- Add cancel operation to editableList (#4077) @HiroyasuNishiyama
|
||||
- Apply Mermaid diagram for project settings UI (#4054) @kazuhitoyokoi
|
||||
- Add tooltip for show/hide button on info sidebar (#4050) @kazuhitoyokoi
|
||||
- Fix align nodes on locked tab (#4072) @HiroyasuNishiyama
|
||||
- Fix importing connected link nodes into a subflow (#4082) @knolleary
|
||||
- Fix to add empty marker to empty group (#4060) @HiroyasuNishiyama
|
||||
- Fix image URLs for v3.0 tour (#4053) @kazuhitoyokoi
|
||||
- Show scrollbar in notification dialog only when needed (#4048) @kazuhitoyokoi
|
||||
- Update-monaco-and-typings (#4089) @Steve-Mcl
|
||||
- Update jquery UI (#4088) @knolleary
|
||||
- Support i18n of lock/unlock buttons in flow property UI (#4049) @kazuhitoyokoi
|
||||
- Translation kr (#3895) @hae-iotplatform
|
||||
- Translation zhcn (!!请懂中文的帮忙review) (#3952) @cliyr
|
||||
- Add French translation of nodes (#3964) @GogoVega
|
||||
- Add French translation (#3962) @GogoVega
|
||||
- Portuguese Brazilian (pt-BR) translation (#3804) @FabsMuller
|
||||
|
||||
|
||||
Runtime
|
||||
|
||||
- NEW: Generate stable ids for subflow instance internal nodes (#4093) @knolleary
|
||||
- NEW: Change default file name to flows.json in project feature (#4073) @kazuhitoyokoi
|
||||
- NEW: Deprecate synchronous access to jsonata (#4090) @knolleary
|
||||
- Add Node 18 to test matrix (#4084) @knolleary
|
||||
- Bump minimum nodejs version supported to match documented value (#4086) @knolleary
|
||||
- Update monaco docs link in settings.js (#4075) @Steve-Mcl
|
||||
- Remove duplicated messages in the message catalog (#4066) @kazuhitoyokoi
|
||||
- Ensure errors in preDeliver callback are handled (#3911) @knolleary
|
||||
- Fix "EADDRINUSE" error (#4046) @bggbr
|
||||
|
||||
Nodes
|
||||
|
||||
- Link Call: Clear link-call timeouts when node is closed (#4085) @knolleary
|
||||
- Join: ensure inflight status is cleared when in auto mode (#4083) @knolleary
|
||||
- File Out: Fix extra newline append for multipart file write (#3915) @dceejay
|
||||
- Add validators for complete and link call nodes (#4056) @kazuhitoyokoi
|
||||
|
||||
#### 3.1.0-beta.1: Beta Release
|
||||
|
||||
|
||||
Editor
|
||||
|
||||
- NEW: Locking Flows (#3938) @knolleary
|
||||
|
@@ -2,7 +2,8 @@
|
||||
|
||||
http://nodered.org
|
||||
|
||||
[](https://github.com/node-red/node-red/actions?query=branch%3Amaster)
|
||||
[](https://travis-ci.org/node-red/node-red)
|
||||
[](https://coveralls.io/r/node-red/node-red?branch=master)
|
||||
|
||||
Low-code programming for event-driven applications.
|
||||
|
||||
|
20
package.json
20
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "3.1.0-beta.2",
|
||||
"version": "3.1.0-beta.1",
|
||||
"description": "Low-code programming for event-driven applications",
|
||||
"homepage": "http://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -44,8 +44,8 @@
|
||||
"express": "4.18.2",
|
||||
"express-session": "1.17.3",
|
||||
"form-data": "4.0.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"got": "12.6.0",
|
||||
"fs-extra": "10.1.0",
|
||||
"got": "11.8.6",
|
||||
"hash-sum": "2.0.0",
|
||||
"hpagent": "1.2.0",
|
||||
"https-proxy-agent": "5.0.1",
|
||||
@@ -60,7 +60,7 @@
|
||||
"memorystore": "1.6.7",
|
||||
"mime": "3.0.0",
|
||||
"moment": "2.29.4",
|
||||
"moment-timezone": "0.5.43",
|
||||
"moment-timezone": "0.5.41",
|
||||
"mqtt": "4.3.7",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"mustache": "4.2.0",
|
||||
@@ -73,13 +73,13 @@
|
||||
"passport-http-bearer": "1.0.1",
|
||||
"passport-oauth2-client-password": "0.1.2",
|
||||
"raw-body": "2.5.2",
|
||||
"semver": "7.5.0",
|
||||
"semver": "7.3.8",
|
||||
"tar": "6.1.13",
|
||||
"tough-cookie": "4.1.2",
|
||||
"uglify-js": "3.17.4",
|
||||
"uuid": "9.0.0",
|
||||
"ws": "7.5.6",
|
||||
"xml2js": "0.5.0"
|
||||
"xml2js": "0.4.23"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"bcrypt": "5.1.0"
|
||||
@@ -108,14 +108,14 @@
|
||||
"i18next-http-backend": "1.4.1",
|
||||
"jquery-i18next": "1.2.1",
|
||||
"jsdoc-nr-template": "github:node-red/jsdoc-nr-template",
|
||||
"marked": "4.3.0",
|
||||
"mermaid": "^9.4.3",
|
||||
"marked": "4.2.12",
|
||||
"mermaid": "^9.3.0",
|
||||
"minami": "1.2.3",
|
||||
"mocha": "9.2.2",
|
||||
"node-red-node-test-helper": "^0.3.1",
|
||||
"node-red-node-test-helper": "^0.3.0",
|
||||
"nodemon": "2.0.20",
|
||||
"proxy": "^1.0.2",
|
||||
"sass": "1.62.1",
|
||||
"sass": "1.58.3",
|
||||
"should": "13.2.3",
|
||||
"sinon": "11.1.2",
|
||||
"stoppable": "^1.1.0",
|
||||
|
@@ -14,6 +14,8 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var express = require("express");
|
||||
|
||||
var nodes = require("./nodes");
|
||||
var flows = require("./flows");
|
||||
var flow = require("./flow");
|
||||
@@ -35,9 +37,18 @@ module.exports = {
|
||||
plugins.init(runtimeAPI);
|
||||
diagnostics.init(settings, runtimeAPI);
|
||||
|
||||
const needsPermission = auth.needsPermission;
|
||||
var needsPermission = auth.needsPermission;
|
||||
|
||||
var adminApp = express();
|
||||
|
||||
var defaultServerSettings = {
|
||||
"x-powered-by": false
|
||||
}
|
||||
var serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{});
|
||||
for (var eOption in serverSettings) {
|
||||
adminApp.set(eOption, serverSettings[eOption]);
|
||||
}
|
||||
|
||||
const adminApp = apiUtil.createExpressApp(settings)
|
||||
|
||||
// Flows
|
||||
adminApp.get("/flows",needsPermission("flows.read"),flows.get,apiUtil.errorHandler);
|
||||
|
@@ -46,15 +46,14 @@ module.exports = {
|
||||
runtimeAPI = _runtimeAPI;
|
||||
needsPermission = auth.needsPermission;
|
||||
if (!settings.disableEditor) {
|
||||
info.init(settings, runtimeAPI);
|
||||
info.init(runtimeAPI);
|
||||
comms.init(server,settings,runtimeAPI);
|
||||
|
||||
var ui = require("./ui");
|
||||
|
||||
ui.init(runtimeAPI);
|
||||
|
||||
const editorApp = apiUtil.createExpressApp(settings)
|
||||
|
||||
var editorApp = express();
|
||||
if (settings.requireHttps === true) {
|
||||
editorApp.enable('trust proxy');
|
||||
editorApp.use(function (req, res, next) {
|
||||
@@ -87,7 +86,7 @@ module.exports = {
|
||||
|
||||
//Projects
|
||||
var projects = require("./projects");
|
||||
projects.init(settings, runtimeAPI);
|
||||
projects.init(runtimeAPI);
|
||||
editorApp.use("/projects",projects.app());
|
||||
|
||||
// Locales
|
||||
|
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
var express = require("express");
|
||||
var apiUtils = require("../util");
|
||||
|
||||
var settings;
|
||||
var runtimeAPI;
|
||||
var needsPermission = require("../auth").needsPermission;
|
||||
|
||||
@@ -77,12 +77,11 @@ function getProjectRemotes(req,res) {
|
||||
})
|
||||
}
|
||||
module.exports = {
|
||||
init: function(_settings, _runtimeAPI) {
|
||||
settings = _settings;
|
||||
init: function(_runtimeAPI) {
|
||||
runtimeAPI = _runtimeAPI;
|
||||
},
|
||||
app: function() {
|
||||
var app = apiUtils.createExpressApp(settings)
|
||||
var app = express();
|
||||
|
||||
app.use(function(req,res,next) {
|
||||
runtimeAPI.projects.available().then(function(available) {
|
||||
|
@@ -18,9 +18,9 @@ var runtimeAPI;
|
||||
var sshkeys = require("./sshkeys");
|
||||
|
||||
module.exports = {
|
||||
init: function(settings, _runtimeAPI) {
|
||||
init: function(_runtimeAPI) {
|
||||
runtimeAPI = _runtimeAPI;
|
||||
sshkeys.init(settings, runtimeAPI);
|
||||
sshkeys.init(runtimeAPI);
|
||||
},
|
||||
userSettings: function(req, res) {
|
||||
var opts = {
|
||||
|
@@ -17,15 +17,13 @@
|
||||
var apiUtils = require("../util");
|
||||
var express = require("express");
|
||||
var runtimeAPI;
|
||||
var settings;
|
||||
|
||||
module.exports = {
|
||||
init: function(_settings, _runtimeAPI) {
|
||||
init: function(_runtimeAPI) {
|
||||
runtimeAPI = _runtimeAPI;
|
||||
settings = _settings;
|
||||
},
|
||||
app: function() {
|
||||
const app = apiUtils.createExpressApp(settings);
|
||||
var app = express();
|
||||
|
||||
// List all SSH keys
|
||||
app.get("/", function(req,res) {
|
||||
|
@@ -19,7 +19,6 @@ var util = require("util");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
var clone = require("clone");
|
||||
const apiUtil = require("../util")
|
||||
|
||||
var defaultContext = {
|
||||
page: {
|
||||
@@ -28,7 +27,8 @@ var defaultContext = {
|
||||
tabicon: {
|
||||
icon: "red/images/node-red-icon-black.svg",
|
||||
colour: "#8f0000"
|
||||
}
|
||||
},
|
||||
version: require(path.join(__dirname,"../../package.json")).version
|
||||
},
|
||||
header: {
|
||||
title: "Node-RED",
|
||||
@@ -40,7 +40,6 @@ var defaultContext = {
|
||||
vendorMonaco: ""
|
||||
}
|
||||
};
|
||||
var settings;
|
||||
|
||||
var theme = null;
|
||||
var themeContext = clone(defaultContext);
|
||||
@@ -93,8 +92,7 @@ function serveFilesFromTheme(themeValue, themeApp, directory, baseDirectory) {
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: function(_settings, _runtimeAPI) {
|
||||
settings = _settings;
|
||||
init: function(settings, _runtimeAPI) {
|
||||
runtimeAPI = _runtimeAPI;
|
||||
themeContext = clone(defaultContext);
|
||||
if (process.env.NODE_ENV == "development") {
|
||||
@@ -115,15 +113,7 @@ module.exports = {
|
||||
var url;
|
||||
themeSettings = {};
|
||||
|
||||
themeApp = apiUtil.createExpressApp(settings);
|
||||
|
||||
const defaultServerSettings = {
|
||||
"x-powered-by": false
|
||||
}
|
||||
const serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{});
|
||||
for (const eOption in serverSettings) {
|
||||
themeApp.set(eOption, serverSettings[eOption]);
|
||||
}
|
||||
themeApp = express();
|
||||
|
||||
if (theme.page) {
|
||||
|
||||
|
@@ -37,6 +37,7 @@ var adminApp;
|
||||
var server;
|
||||
var editor;
|
||||
|
||||
|
||||
/**
|
||||
* Initialise the module.
|
||||
* @param {Object} settings The runtime settings
|
||||
@@ -48,7 +49,7 @@ var editor;
|
||||
function init(settings,_server,storage,runtimeAPI) {
|
||||
server = _server;
|
||||
if (settings.httpAdminRoot !== false) {
|
||||
adminApp = apiUtil.createExpressApp(settings);
|
||||
adminApp = express();
|
||||
|
||||
var cors = require('cors');
|
||||
var corsHandler = cors({
|
||||
@@ -63,6 +64,14 @@ function init(settings,_server,storage,runtimeAPI) {
|
||||
}
|
||||
}
|
||||
|
||||
var defaultServerSettings = {
|
||||
"x-powered-by": false
|
||||
}
|
||||
var serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{});
|
||||
for (var eOption in serverSettings) {
|
||||
adminApp.set(eOption, serverSettings[eOption]);
|
||||
}
|
||||
|
||||
auth.init(settings,storage);
|
||||
|
||||
var maxApiRequestSize = settings.apiMaxLength || '5mb';
|
||||
@@ -127,11 +136,10 @@ async function stop() {
|
||||
editor.stop();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init,
|
||||
start,
|
||||
stop,
|
||||
init: init,
|
||||
start: start,
|
||||
stop: stop,
|
||||
|
||||
/**
|
||||
* @memberof @node-red/editor-api
|
||||
|
@@ -14,9 +14,10 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
const express = require("express");
|
||||
|
||||
const { log, i18n } = require("@node-red/util");
|
||||
var log = require("@node-red/util").log; // TODO: separate module
|
||||
var i18n = require("@node-red/util").i18n; // TODO: separate module
|
||||
|
||||
|
||||
module.exports = {
|
||||
errorHandler: function(err,req,res,next) {
|
||||
@@ -63,17 +64,5 @@ module.exports = {
|
||||
path: req.path,
|
||||
ip: (req.headers && req.headers['x-forwarded-for']) || (req.connection && req.connection.remoteAddress) || undefined
|
||||
}
|
||||
},
|
||||
createExpressApp: function(settings) {
|
||||
const app = express();
|
||||
|
||||
const defaultServerSettings = {
|
||||
"x-powered-by": false
|
||||
}
|
||||
const serverSettings = Object.assign({},defaultServerSettings,settings.httpServerOptions||{});
|
||||
for (let eOption in serverSettings) {
|
||||
app.set(eOption, serverSettings[eOption]);
|
||||
}
|
||||
return app
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-api",
|
||||
"version": "3.1.0-beta.2",
|
||||
"version": "3.1.0-beta.1",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,8 +16,8 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "3.1.0-beta.2",
|
||||
"@node-red/editor-client": "3.1.0-beta.2",
|
||||
"@node-red/util": "3.1.0-beta.1",
|
||||
"@node-red/editor-client": "3.1.0-beta.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"body-parser": "1.20.2",
|
||||
"clone": "2.1.2",
|
||||
|
@@ -504,7 +504,6 @@
|
||||
"unassigned": "Unassigned",
|
||||
"global": "global",
|
||||
"workspace": "workspace",
|
||||
"editor": "edit dialog",
|
||||
"selectAll": "Select all",
|
||||
"selectNone": "Select none",
|
||||
"selectAllConnected": "Select connected",
|
||||
|
@@ -504,7 +504,6 @@
|
||||
"unassigned": "未割当",
|
||||
"global": "グローバル",
|
||||
"workspace": "ワークスペース",
|
||||
"editor": "編集ダイアログ",
|
||||
"selectAll": "全てのノードを選択",
|
||||
"selectNone": "選択を外す",
|
||||
"selectAllConnected": "接続されたノードを選択",
|
||||
@@ -1204,7 +1203,7 @@
|
||||
"fr": "フランス語",
|
||||
"ja": "日本語",
|
||||
"ko": "韓国語",
|
||||
"pt-BR": "ポルトガル語",
|
||||
"pt-BR":"ポルトガル語",
|
||||
"ru": "ロシア語",
|
||||
"zh-CN": "中国語(簡体)",
|
||||
"zh-TW": "中国語(繁体)"
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/editor-client",
|
||||
"version": "3.1.0-beta.2",
|
||||
"version": "3.1.0-beta.1",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@@ -1468,7 +1468,7 @@ RED.nodes = (function() {
|
||||
}
|
||||
}
|
||||
if (node.type !== "subflow") {
|
||||
var convertedNode = RED.nodes.convertNode(node, { credentials: false });
|
||||
var convertedNode = RED.nodes.convertNode(node);
|
||||
for (var d in node._def.defaults) {
|
||||
if (node._def.defaults[d].type) {
|
||||
var nodeList = node[d];
|
||||
@@ -1501,7 +1501,7 @@ RED.nodes = (function() {
|
||||
nns = nns.concat(createExportableNodeSet(node.nodes, exportedIds, exportedSubflows, exportedConfigNodes));
|
||||
}
|
||||
} else {
|
||||
var convertedSubflow = convertSubflow(node, { credentials: false });
|
||||
var convertedSubflow = convertSubflow(node);
|
||||
nns.push(convertedSubflow);
|
||||
}
|
||||
}
|
||||
@@ -2201,27 +2201,16 @@ RED.nodes = (function() {
|
||||
} else if (n.type.substring(0,7) === "subflow") {
|
||||
var parentId = n.type.split(":")[1];
|
||||
var subflow = subflow_denylist[parentId]||subflow_map[parentId]||getSubflow(parentId);
|
||||
if (!subflow){
|
||||
node._def = {
|
||||
color:"#fee",
|
||||
defaults: {},
|
||||
label: "unknown: "+n.type,
|
||||
labelStyle: "red-ui-flow-node-label-italic",
|
||||
outputs: n.outputs|| (n.wires && n.wires.length) || 0,
|
||||
set: registry.getNodeSet("node-red/unknown")
|
||||
}
|
||||
} else {
|
||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||
parentId = subflow.id;
|
||||
node.type = "subflow:"+parentId;
|
||||
node._def = registry.getNodeType(node.type);
|
||||
delete node.i;
|
||||
}
|
||||
node.name = n.name;
|
||||
node.outputs = subflow.out.length;
|
||||
node.inputs = subflow.in.length;
|
||||
node.env = n.env;
|
||||
if (createNewIds || options.importMap[n.id] === "copy") {
|
||||
parentId = subflow.id;
|
||||
node.type = "subflow:"+parentId;
|
||||
node._def = registry.getNodeType(node.type);
|
||||
delete node.i;
|
||||
}
|
||||
node.name = n.name;
|
||||
node.outputs = subflow.out.length;
|
||||
node.inputs = subflow.in.length;
|
||||
node.env = n.env;
|
||||
} else if (n.type === 'junction') {
|
||||
node._def = {defaults:{}}
|
||||
node._config.x = node.x
|
||||
|
@@ -33,8 +33,8 @@ RED.settings = (function () {
|
||||
if (!hasLocalStorage()) {
|
||||
return;
|
||||
}
|
||||
if (key.startsWith("auth-tokens")) {
|
||||
localStorage.setItem(key+this.authTokensSuffix, JSON.stringify(value));
|
||||
if (key === "auth-tokens") {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
} else {
|
||||
RED.utils.setMessageProperty(userSettings,key,value);
|
||||
saveUserSettings();
|
||||
@@ -52,8 +52,8 @@ RED.settings = (function () {
|
||||
if (!hasLocalStorage()) {
|
||||
return undefined;
|
||||
}
|
||||
if (key.startsWith("auth-tokens")) {
|
||||
return JSON.parse(localStorage.getItem(key+this.authTokensSuffix));
|
||||
if (key === "auth-tokens") {
|
||||
return JSON.parse(localStorage.getItem(key));
|
||||
} else {
|
||||
var v;
|
||||
try { v = RED.utils.getMessageProperty(userSettings,key); } catch(err) {}
|
||||
@@ -71,8 +71,8 @@ RED.settings = (function () {
|
||||
if (!hasLocalStorage()) {
|
||||
return;
|
||||
}
|
||||
if (key.startsWith("auth-tokens")) {
|
||||
localStorage.removeItem(key+this.authTokensSuffix);
|
||||
if (key === "auth-tokens") {
|
||||
localStorage.removeItem(key);
|
||||
} else {
|
||||
delete userSettings[key];
|
||||
saveUserSettings();
|
||||
@@ -99,8 +99,6 @@ RED.settings = (function () {
|
||||
|
||||
var init = function (options, done) {
|
||||
var accessTokenMatch = /[?&]access_token=(.*?)(?:$|&)/.exec(window.location.search);
|
||||
var path=window.location.pathname.slice(0,-1);
|
||||
RED.settings.authTokensSuffix=path.replace(/\//g, '-');
|
||||
if (accessTokenMatch) {
|
||||
var accessToken = accessTokenMatch[1];
|
||||
RED.settings.set("auth-tokens",{access_token: accessToken});
|
||||
|
@@ -47,7 +47,7 @@ RED.actionList = (function() {
|
||||
var searchDiv = $("<div>",{class:"red-ui-search-container"}).appendTo(dialog);
|
||||
searchInput = $('<input type="text" data-i18n="[placeholder]keyboard.filterActions">').appendTo(searchDiv).searchBox({
|
||||
change: function() {
|
||||
filterTerm = $(this).val().trim().toLowerCase();
|
||||
filterTerm = $(this).val().trim();
|
||||
filterTerms = filterTerm.split(" ");
|
||||
searchResults.editableList('filter');
|
||||
searchResults.find("li.selected").removeClass("selected");
|
||||
|
@@ -37,13 +37,13 @@ RED.clipboard = (function() {
|
||||
// IE11 workaround
|
||||
// IE does not support data uri scheme for downloading data
|
||||
var blob = new Blob([data], {
|
||||
type: "data:application/json;charset=utf-8"
|
||||
type: "data:text/plain;charset=utf-8"
|
||||
});
|
||||
navigator.msSaveBlob(blob, file);
|
||||
}
|
||||
else {
|
||||
var element = document.createElement('a');
|
||||
element.setAttribute('href', 'data:application/json;charset=utf-8,' + encodeURIComponent(data));
|
||||
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(data));
|
||||
element.setAttribute('download', file);
|
||||
element.style.display = 'none';
|
||||
document.body.appendChild(element);
|
||||
@@ -731,7 +731,7 @@ RED.clipboard = (function() {
|
||||
nodes.unshift(parentNode);
|
||||
nodes = RED.nodes.createExportableNodeSet(nodes);
|
||||
} else if (type === 'full') {
|
||||
nodes = RED.nodes.createCompleteNodeSet({ credentials: false });
|
||||
nodes = RED.nodes.createCompleteNodeSet(false);
|
||||
}
|
||||
if (nodes !== null) {
|
||||
if (format === "red-ui-clipboard-dialog-export-fmt-full") {
|
||||
|
@@ -45,13 +45,11 @@ RED.editor = (function() {
|
||||
var hasChanged;
|
||||
if (node.type.indexOf("subflow:")===0) {
|
||||
subflow = RED.nodes.subflow(node.type.substring(8));
|
||||
if (subflow){
|
||||
isValid = subflow.valid;
|
||||
isValid = subflow.valid;
|
||||
hasChanged = subflow.changed;
|
||||
if (isValid === undefined) {
|
||||
isValid = validateNode(subflow);
|
||||
hasChanged = subflow.changed;
|
||||
if (isValid === undefined) {
|
||||
isValid = validateNode(subflow);
|
||||
hasChanged = subflow.changed;
|
||||
}
|
||||
}
|
||||
validationErrors = validateNodeProperties(node, node._def.defaults, node);
|
||||
node.valid = isValid && validationErrors.length === 0;
|
||||
|
@@ -108,7 +108,7 @@ RED.editor.codeEditor.monaco = (function() {
|
||||
"node-red-util": {package: "node-red", module: "util", path: "node-red/util.d.ts" },
|
||||
"node-red-func": {package: "node-red", module: "func", path: "node-red/func.d.ts" },
|
||||
}
|
||||
const defaultServerSideTypes = [ knownModules["node-red-util"], knownModules["node-red-func"], knownModules["globals"], knownModules["console"], knownModules["buffer"], knownModules["timers"] , knownModules["util"] ];
|
||||
const defaultServerSideTypes = [ knownModules["node-red-util"], knownModules["node-red-func"], knownModules["globals"], knownModules["console"], knownModules["buffer"] , knownModules["util"] ];
|
||||
|
||||
const modulesCache = {};
|
||||
|
||||
|
@@ -294,7 +294,7 @@
|
||||
}
|
||||
|
||||
try {
|
||||
expr.evaluate(legacyMode?{msg:parsedData}:parsedData, null, (err, result) => {
|
||||
expr.evaluate(legacyMode?{msg:parsedData}:parsedData, (err, result) => {
|
||||
if (err) {
|
||||
testResultEditor.setValue(RED._("expressionEditor.errors.eval",{message:err.message}),-1);
|
||||
} else {
|
||||
|
@@ -50,11 +50,7 @@ RED.envVar = (function() {
|
||||
var new_env = [];
|
||||
var items = list.editableList('items');
|
||||
var credentials = gconf ? gconf.credentials : null;
|
||||
if (!gconf && list.editableList('length') === 0) {
|
||||
// No existing global-config node and nothing in the list,
|
||||
// so no need to do anything more
|
||||
return
|
||||
}
|
||||
|
||||
if (!credentials) {
|
||||
credentials = {
|
||||
_ : {},
|
||||
@@ -82,12 +78,6 @@ RED.envVar = (function() {
|
||||
if (gconf === null) {
|
||||
gconf = getGlobalConf(true);
|
||||
}
|
||||
if (!gconf.credentials) {
|
||||
gconf.credentials = {
|
||||
_ : {},
|
||||
map: {}
|
||||
};
|
||||
}
|
||||
if ((JSON.stringify(new_env) !== JSON.stringify(gconf.env)) ||
|
||||
(JSON.stringify(credentials) !== JSON.stringify(gconf.credentials))) {
|
||||
gconf.env = new_env;
|
||||
|
@@ -401,7 +401,7 @@ RED.group = (function() {
|
||||
}
|
||||
}
|
||||
var existingGroup;
|
||||
var mergedEnv = {}
|
||||
|
||||
// Second pass, ungroup any groups in the selection and add their contents
|
||||
// to the selection
|
||||
for (var i=0; i<selection.nodes.length; i++) {
|
||||
@@ -410,11 +410,6 @@ RED.group = (function() {
|
||||
if (!existingGroup) {
|
||||
existingGroup = n;
|
||||
}
|
||||
if (n.env && n.env.length > 0) {
|
||||
n.env.forEach(env => {
|
||||
mergedEnv[env.name] = env
|
||||
})
|
||||
}
|
||||
ungroupHistoryEvent.groups.push(n);
|
||||
nodes = nodes.concat(ungroup(n));
|
||||
} else {
|
||||
@@ -432,7 +427,6 @@ RED.group = (function() {
|
||||
group.style = existingGroup.style;
|
||||
group.name = existingGroup.name;
|
||||
}
|
||||
group.env = Object.values(mergedEnv)
|
||||
RED.view.select({nodes:[group]})
|
||||
}
|
||||
historyEvent.events.push({
|
||||
|
@@ -491,11 +491,7 @@ RED.keyboard = (function() {
|
||||
okButton.attr("disabled",!valid);
|
||||
});
|
||||
|
||||
var scopeSelect = $('<select>'+
|
||||
'<option value="*" data-i18n="keyboard.global"></option>'+
|
||||
'<option value="red-ui-workspace" data-i18n="keyboard.workspace"></option>'+
|
||||
'<option value="red-ui-editor-stack" data-i18n="keyboard.editor"></option>'+
|
||||
'</select>').appendTo(scope);
|
||||
var scopeSelect = $('<select><option value="*" data-i18n="keyboard.global"></option><option value="red-ui-workspace" data-i18n="keyboard.workspace"></option></select>').appendTo(scope);
|
||||
scopeSelect.i18n();
|
||||
if (object.scope === "workspace") {
|
||||
object.scope = "red-ui-workspace";
|
||||
|
@@ -171,15 +171,23 @@ RED.palette = (function() {
|
||||
}
|
||||
metaData += type;
|
||||
|
||||
const safeType = type.replace(/'/g,"\\'");
|
||||
const searchType = type.indexOf(' ') > -1 ? '"' + type + '"' : type
|
||||
|
||||
if (/^subflow:/.test(type)) {
|
||||
$('<button type="button" onclick="RED.workspaces.show(\''+type.substring(8).replace(/'/g,"\\'")+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-pencil"></i></button>').appendTo(popOverContent)
|
||||
}
|
||||
|
||||
$('<button type="button" onclick="RED.search.show(\'type:'+searchType+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-search"></i></button>').appendTo(popOverContent)
|
||||
const safeType = type.replace(/'/g,"\\'");
|
||||
const wrapStr = function (str) {
|
||||
if(str.indexOf(' ') >= 0) {
|
||||
return '"' + str + '"'
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
$('<button type="button"; return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-search"></i></button>')
|
||||
.appendTo(popOverContent)
|
||||
.on('click', function() {
|
||||
RED.search.show('type:' + wrapStr(safeType))
|
||||
})
|
||||
$('<button type="button" onclick="RED.sidebar.help.show(\''+safeType+'\'); return false;" class="red-ui-button red-ui-button-small" style="float: right; margin-left: 5px;"><i class="fa fa-book"></i></button>').appendTo(popOverContent)
|
||||
|
||||
$('<p>',{style:"font-size: 0.8em"}).text(metaData).appendTo(popOverContent);
|
||||
|
@@ -747,14 +747,14 @@ RED.projects = (function() {
|
||||
var row = $('<div class="form-row"></div>').appendTo(body);
|
||||
$('<label for="red-ui-projects-dialog-screen-create-project-file">'+RED._("projects.default-files.flow-file")+'</label>').appendTo(row);
|
||||
var subrow = $('<div style="position:relative;"></div>').appendTo(row);
|
||||
var defaultFlowFile = (createProjectOptions.files &&createProjectOptions.files.flow) || (RED.settings.files && RED.settings.files.flow) || "flows.json";
|
||||
var defaultFlowFile = (createProjectOptions.files &&createProjectOptions.files.flow) || (RED.settings.files && RED.settings.files.flow)||"flow.json";
|
||||
projectFlowFileInput = $('<input id="red-ui-projects-dialog-screen-create-project-file" type="text">').val(defaultFlowFile)
|
||||
.on("change keyup paste",validateForm)
|
||||
.appendTo(subrow);
|
||||
$('<div class="red-ui-projects-dialog-screen-input-status"></div>').appendTo(subrow);
|
||||
$('<label class="red-ui-projects-edit-form-sublabel"><small>*.json</small></label>').appendTo(row);
|
||||
|
||||
var defaultCredentialsFile = (createProjectOptions.files &&createProjectOptions.files.credentials) || (RED.settings.files && RED.settings.files.credentials) || "flows_cred.json";
|
||||
var defaultCredentialsFile = (createProjectOptions.files &&createProjectOptions.files.credentials) || (RED.settings.files && RED.settings.files.credentials)||"flow_cred.json";
|
||||
row = $('<div class="form-row"></div>').appendTo(body);
|
||||
$('<label for="red-ui-projects-dialog-screen-create-project-credfile">'+RED._("projects.default-files.credentials-file")+'</label>').appendTo(row);
|
||||
subrow = $('<div style="position:relative;"></div>').appendTo(row);
|
||||
@@ -1257,7 +1257,7 @@ RED.projects = (function() {
|
||||
row = $('<div class="form-row red-ui-projects-dialog-screen-create-row red-ui-projects-dialog-screen-create-row-empty"></div>').appendTo(container);
|
||||
$('<label for="red-ui-projects-dialog-screen-create-project-file">'+RED._("projects.create.flow-file")+'</label>').appendTo(row);
|
||||
subrow = $('<div style="position:relative;"></div>').appendTo(row);
|
||||
projectFlowFileInput = $('<input id="red-ui-projects-dialog-screen-create-project-file" type="text">').val("flows.json")
|
||||
projectFlowFileInput = $('<input id="red-ui-projects-dialog-screen-create-project-file" type="text">').val("flow.json")
|
||||
.on("change keyup paste",validateForm)
|
||||
.appendTo(subrow);
|
||||
$('<div class="red-ui-projects-dialog-screen-input-status"></div>').appendTo(subrow);
|
||||
|
@@ -681,23 +681,24 @@ RED.subflow = (function() {
|
||||
var candidateOutputs = [];
|
||||
var candidateInputNodes = {};
|
||||
|
||||
var boundingBox = [nodeList[0].x-(nodeList[0].w/2),
|
||||
nodeList[0].y-(nodeList[0].h/2),
|
||||
nodeList[0].x+(nodeList[0].w/2),
|
||||
nodeList[0].y+(nodeList[0].h/2)];
|
||||
var boundingBox = [nodeList[0].x,
|
||||
nodeList[0].y,
|
||||
nodeList[0].x,
|
||||
nodeList[0].y];
|
||||
|
||||
for (i=0;i<nodeList.length;i++) {
|
||||
n = nodeList[i];
|
||||
nodes[n.id] = {n:n,outputs:{}};
|
||||
boundingBox = [
|
||||
Math.min(boundingBox[0],n.x-(n.w/2)),
|
||||
Math.min(boundingBox[1],n.y-(n.h/2)),
|
||||
Math.max(boundingBox[2],n.x+(n.w/2)),
|
||||
Math.max(boundingBox[3],n.y+(n.h/2))
|
||||
Math.min(boundingBox[0],n.x),
|
||||
Math.min(boundingBox[1],n.y),
|
||||
Math.max(boundingBox[2],n.x),
|
||||
Math.max(boundingBox[3],n.y)
|
||||
]
|
||||
}
|
||||
var offsetX = snapToGrid(boundingBox[0] - 140);
|
||||
var offsetY = snapToGrid(boundingBox[1] - 60);
|
||||
var offsetX = snapToGrid(boundingBox[0] - 200);
|
||||
var offsetY = snapToGrid(boundingBox[1] - 80);
|
||||
|
||||
|
||||
var center = [
|
||||
snapToGrid((boundingBox[2]+boundingBox[0]) / 2),
|
||||
|
@@ -17,9 +17,9 @@
|
||||
|
||||
RED.view.navigator = (function() {
|
||||
|
||||
var nav_scale = 50;
|
||||
var nav_width = 8000/nav_scale;
|
||||
var nav_height = 8000/nav_scale;
|
||||
var nav_scale = 25;
|
||||
var nav_width = 5000/nav_scale;
|
||||
var nav_height = 5000/nav_scale;
|
||||
|
||||
var navContainer;
|
||||
var navBox;
|
||||
|
@@ -30,8 +30,8 @@
|
||||
*/
|
||||
|
||||
RED.view = (function() {
|
||||
var space_width = 8000,
|
||||
space_height = 8000,
|
||||
var space_width = 5000,
|
||||
space_height = 5000,
|
||||
lineCurveScale = 0.75,
|
||||
scaleFactor = 1,
|
||||
node_width = 100,
|
||||
@@ -189,13 +189,7 @@ RED.view = (function() {
|
||||
set.unshift(...removed)
|
||||
}
|
||||
},
|
||||
find: function(func) { return set.find(func) },
|
||||
dump: function () {
|
||||
console.log('MovingSet Contents')
|
||||
api.forEach((n, i) => {
|
||||
console.log(`${i+1}\t${n.n.id}\t${n.n.type}`)
|
||||
})
|
||||
}
|
||||
find: function(func) { return set.find(func) }
|
||||
}
|
||||
return api;
|
||||
})();
|
||||
@@ -232,63 +226,6 @@ RED.view = (function() {
|
||||
return api
|
||||
})();
|
||||
|
||||
const selectedGroups = (function() {
|
||||
let groups = new Set()
|
||||
const api = {
|
||||
add: function(g, includeNodes, addToMovingSet) {
|
||||
groups.add(g)
|
||||
if (!g.selected) {
|
||||
g.selected = true;
|
||||
g.dirty = true;
|
||||
}
|
||||
if (addToMovingSet !== false) {
|
||||
movingSet.add(g);
|
||||
}
|
||||
if (includeNodes) {
|
||||
var currentSet = new Set(movingSet.nodes());
|
||||
var allNodes = RED.group.getNodes(g,true);
|
||||
allNodes.forEach(function(n) {
|
||||
if (!currentSet.has(n)) {
|
||||
movingSet.add(n)
|
||||
}
|
||||
n.dirty = true;
|
||||
})
|
||||
}
|
||||
selectedLinks.clearUnselected()
|
||||
},
|
||||
remove: function(g) {
|
||||
groups.delete(g)
|
||||
if (g.selected) {
|
||||
g.selected = false;
|
||||
g.dirty = true;
|
||||
}
|
||||
const allNodes = RED.group.getNodes(g,true);
|
||||
const nodeSet = new Set(allNodes);
|
||||
nodeSet.add(g);
|
||||
for (let i = movingSet.length()-1; i >= 0; i -= 1) {
|
||||
const msn = movingSet.get(i);
|
||||
if (nodeSet.has(msn.n) || msn.n === g) {
|
||||
msn.n.selected = false;
|
||||
msn.n.dirty = true;
|
||||
movingSet.remove(msn.n,i)
|
||||
}
|
||||
}
|
||||
selectedLinks.clearUnselected()
|
||||
},
|
||||
length: () => groups.length,
|
||||
forEach: (func) => { groups.forEach(func) },
|
||||
toArray: () => [...groups],
|
||||
clear: function () {
|
||||
groups.forEach(g => {
|
||||
g.selected = false
|
||||
g.dirty = true
|
||||
})
|
||||
groups.clear()
|
||||
}
|
||||
}
|
||||
return api
|
||||
})()
|
||||
|
||||
|
||||
function init() {
|
||||
|
||||
@@ -1193,7 +1130,7 @@ RED.view = (function() {
|
||||
var touchTrigger = options.touchTrigger;
|
||||
|
||||
if (targetGroup) {
|
||||
selectedGroups.add(targetGroup,false);
|
||||
selectGroup(targetGroup,false);
|
||||
RED.view.redraw();
|
||||
}
|
||||
|
||||
@@ -1518,9 +1455,6 @@ RED.view = (function() {
|
||||
// auto select dropped node - so info shows (if visible)
|
||||
clearSelection();
|
||||
nn.selected = true;
|
||||
if (targetGroup) {
|
||||
selectedGroups.add(targetGroup,false);
|
||||
}
|
||||
movingSet.add(nn);
|
||||
updateActiveNodes();
|
||||
updateSelection();
|
||||
@@ -1968,6 +1902,7 @@ RED.view = (function() {
|
||||
}
|
||||
showQuickAddDialog({ position: point, group: clickedGroup });
|
||||
}
|
||||
|
||||
hideDragLines();
|
||||
}
|
||||
if (lasso) {
|
||||
@@ -1983,7 +1918,10 @@ RED.view = (function() {
|
||||
if (!movingSet.has(n) && !n.selected) {
|
||||
// group entirely within lasso
|
||||
if (n.x > x && n.y > y && n.x + n.w < x2 && n.y + n.h < y2) {
|
||||
selectedGroups.add(n, true)
|
||||
n.selected = true
|
||||
n.dirty = true
|
||||
var groupNodes = RED.group.getNodes(n,true);
|
||||
groupNodes.forEach(gn => movingSet.add(gn))
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -2333,7 +2271,7 @@ RED.view = (function() {
|
||||
clearSelection();
|
||||
activeGroups.forEach(function(g) {
|
||||
if (!g.g) {
|
||||
selectedGroups.add(g, true);
|
||||
selectGroup(g, true);
|
||||
if (!g.selected) {
|
||||
g.selected = true;
|
||||
g.dirty = true;
|
||||
@@ -2403,7 +2341,10 @@ RED.view = (function() {
|
||||
}
|
||||
movingSet.clear();
|
||||
selectedLinks.clear();
|
||||
selectedGroups.clear();
|
||||
activeGroups.forEach(function(g) {
|
||||
g.selected = false;
|
||||
g.dirty = true;
|
||||
})
|
||||
}
|
||||
|
||||
var lastSelection = null;
|
||||
@@ -2660,16 +2601,6 @@ RED.view = (function() {
|
||||
var result = RED.nodes.removeJunction(node)
|
||||
removedJunctions.push(node);
|
||||
removedLinks = removedLinks.concat(result.links);
|
||||
if (node.g) {
|
||||
var group = RED.nodes.group(node.g);
|
||||
if (selectedGroups.indexOf(group) === -1) {
|
||||
// Don't use RED.group.removeFromGroup as that emits
|
||||
// a change event on the node - but we're deleting it
|
||||
var index = group.nodes.indexOf(node);
|
||||
group.nodes.splice(index,1);
|
||||
RED.group.markDirty(group);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (node.direction === "out") {
|
||||
removedSubflowOutputs.push(node);
|
||||
@@ -3492,7 +3423,7 @@ RED.view = (function() {
|
||||
if (!groupNodeSelectPrimed && !d.selected && d.g && RED.nodes.group(d.g).selected) {
|
||||
clearSelection();
|
||||
|
||||
selectedGroups.add(RED.nodes.group(d.g), false);
|
||||
selectGroup(RED.nodes.group(d.g), false);
|
||||
|
||||
mousedown_node.selected = true;
|
||||
movingSet.add(mousedown_node);
|
||||
@@ -3913,14 +3844,14 @@ RED.view = (function() {
|
||||
lastClickNode = g;
|
||||
|
||||
if (g.selected && (d3.event.ctrlKey||d3.event.metaKey)) {
|
||||
selectedGroups.remove(g);
|
||||
deselectGroup(g);
|
||||
d3.event.stopPropagation();
|
||||
} else {
|
||||
if (!g.selected) {
|
||||
if (!d3.event.ctrlKey && !d3.event.metaKey) {
|
||||
clearSelection();
|
||||
}
|
||||
selectedGroups.add(g,true);//!wasSelected);
|
||||
selectGroup(g,true);//!wasSelected);
|
||||
}
|
||||
|
||||
if (d3.event.button != 2) {
|
||||
@@ -3936,6 +3867,45 @@ RED.view = (function() {
|
||||
d3.event.stopPropagation();
|
||||
}
|
||||
|
||||
function selectGroup(g, includeNodes, addToMovingSet) {
|
||||
if (!g.selected) {
|
||||
g.selected = true;
|
||||
g.dirty = true;
|
||||
}
|
||||
if (addToMovingSet !== false) {
|
||||
movingSet.add(g);
|
||||
}
|
||||
if (includeNodes) {
|
||||
var currentSet = new Set(movingSet.nodes());
|
||||
var allNodes = RED.group.getNodes(g,true);
|
||||
allNodes.forEach(function(n) {
|
||||
if (!currentSet.has(n)) {
|
||||
movingSet.add(n)
|
||||
}
|
||||
n.dirty = true;
|
||||
})
|
||||
}
|
||||
selectedLinks.clearUnselected()
|
||||
}
|
||||
|
||||
function deselectGroup(g) {
|
||||
if (g.selected) {
|
||||
g.selected = false;
|
||||
g.dirty = true;
|
||||
}
|
||||
const allNodes = RED.group.getNodes(g,true);
|
||||
const nodeSet = new Set(allNodes);
|
||||
nodeSet.add(g);
|
||||
for (let i = movingSet.length()-1; i >= 0; i -= 1) {
|
||||
const msn = movingSet.get(i);
|
||||
if (nodeSet.has(msn.n) || msn.n === g) {
|
||||
msn.n.selected = false;
|
||||
msn.n.dirty = true;
|
||||
movingSet.remove(msn.n,i)
|
||||
}
|
||||
}
|
||||
selectedLinks.clearUnselected()
|
||||
}
|
||||
function getGroupAt(x, y, ignoreSelected) {
|
||||
// x,y expected to be in node-co-ordinate space
|
||||
var candidateGroups = {};
|
||||
@@ -5916,10 +5886,11 @@ RED.view = (function() {
|
||||
if (movingSet.length() > 0) {
|
||||
movingSet.forEach(function(n) {
|
||||
if (n.n.type !== 'group') {
|
||||
allNodes.add(n.n);
|
||||
allNodes.add(n.n);
|
||||
}
|
||||
});
|
||||
}
|
||||
var selectedGroups = activeGroups.filter(function(g) { return g.selected });
|
||||
selectedGroups.forEach(function(g) {
|
||||
var groupNodes = RED.group.getNodes(g,true);
|
||||
groupNodes.forEach(function(n) {
|
||||
@@ -6128,7 +6099,7 @@ RED.view = (function() {
|
||||
n.dirty = true;
|
||||
movingSet.add(n);
|
||||
} else {
|
||||
selectedGroups.add(n,true);
|
||||
selectGroup(n,true);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -126,7 +126,7 @@
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding:0;
|
||||
overflow-wrap: anywhere;
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
padding:0;
|
||||
|
@@ -55,7 +55,7 @@
|
||||
.red-ui-palette-search {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: var(--red-ui-form-input-background);
|
||||
background: var(--red-ui-secondary-background);
|
||||
text-align: center;
|
||||
height: 35px;
|
||||
padding: 3px;
|
||||
|
@@ -35,7 +35,6 @@
|
||||
padding: 8px;
|
||||
border-radius: 2px;
|
||||
background: var(--red-ui-popover-background);
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.red-ui-popover:after, .red-ui-popover:before {
|
||||
border: solid transparent;
|
||||
|
@@ -108,8 +108,6 @@
|
||||
}
|
||||
.red-ui-search-result-node-label {
|
||||
color: var(--red-ui-secondary-text-color);
|
||||
width: 240px;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -31,7 +31,6 @@
|
||||
> span {
|
||||
display: inline-block;
|
||||
margin-left: 5px;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
border-bottom: 1px solid var(--red-ui-secondary-border-color);
|
||||
}
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 4.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.9 KiB |
@@ -1,90 +1,15 @@
|
||||
export default {
|
||||
version: "3.1.0-beta.2",
|
||||
version: "3.1.0-beta.1",
|
||||
steps: [
|
||||
{
|
||||
titleIcon: "fa fa-map-o",
|
||||
title: {
|
||||
"en-US": "Welcome to Node-RED 3.1 Beta 2!",
|
||||
"ja": "Node-RED 3.1 ベータ2へようこそ!"
|
||||
"en-US": "Welcome to Node-RED 3.1 Beta 1!",
|
||||
"ja": "Node-RED 3.1 ベータ1へようこそ!"
|
||||
},
|
||||
description: {
|
||||
"en-US": "<p>This is the second beta release for 3.1.0 and we have a few new features to tell you about.</p>",
|
||||
"ja": "<p>これは3.1.0の2回目のベータリリースです。いくつかの新機能について説明します。</p>"
|
||||
}
|
||||
},
|
||||
{
|
||||
title: {
|
||||
"en-US": "New ways to work with groups",
|
||||
"ja": "グループの新たな操作方法"
|
||||
},
|
||||
description: {
|
||||
"en-US": `<p>We have changed how you interact with groups in the editor.</p>
|
||||
<ul>
|
||||
<li>They don't get in the way when clicking on a node</li>
|
||||
<li>They can be reordered using the Moving Forwards and Move Backwards actions</li>
|
||||
<li>Multiple nodes can be dragged into a group in one go</li>
|
||||
<li>Holding <code>Alt</code> when dragging a node will *remove* it from its group</li>
|
||||
</ul>`,
|
||||
"ja": `<p>エディタ上のグループの操作が変更されました。</p>
|
||||
<ul>
|
||||
<li>グループ内のノードをクリックする時に、グループが邪魔をすることが無くなりました。</li>
|
||||
<li>「前面へ移動」と「背面へ移動」の動作を用いて、複数のグループの表示順序を変えることができます。</li>
|
||||
<li>グループ内へ一度に複数のノードをドラッグできるようになりました。</li>
|
||||
<li><code>Alt</code> を押したまま、グループ内のノードをドラッグすると、そのグループから *除く* ことができます。</li>
|
||||
</ul>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: {
|
||||
"en-US": "Change notification on tabs",
|
||||
"ja": "タブ上の変更通知"
|
||||
},
|
||||
image: 'images/tab-changes.png',
|
||||
description: {
|
||||
"en-US": `<p>When a tab contains undeployed changes it now shows the
|
||||
same style of change icon used by nodes.</p>
|
||||
<p>This will make it much easier to track down changes when you're
|
||||
working across multiple flows.</p>`,
|
||||
"ja": `<p>タブ内にデプロイされていない変更が存在する時は、ノードと同じスタイルで変更の印が表示されるようになりました。</p>
|
||||
<p>これによって複数のフローを編集している時に、変更を見つけるのが簡単になりました。</p>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: {
|
||||
"en-US": "A bigger canvas to work with",
|
||||
"ja": "より広くなった作業キャンバス"
|
||||
},
|
||||
description: {
|
||||
"en-US": `<p>The default canvas size has been increased so you can fit more
|
||||
into one flow.</p>
|
||||
<p>We still recommend using tools such as subflows and Link Nodes to help
|
||||
keep things organised, but now you have more room to work in.</p>`,
|
||||
"ja": `<p>標準のキャンバスが広くなったため、1つのフローに沢山のものを含めることができるようになりました。</p>
|
||||
<p>引き続き、サブフローやリンクノードなどの方法を用いて整理することをお勧めしますが、作業できる場所が増えました。</p>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: {
|
||||
"en-US": "Finding help",
|
||||
"ja": "ヘルプを見つける"
|
||||
},
|
||||
image: 'images/node-help.png',
|
||||
description: {
|
||||
"en-US": `<p>All node edit dialogs now include a link to that node's help
|
||||
in the footer.</p>
|
||||
<p>Clicking it will open up the Help sidebar showing the help for that node.</p>`,
|
||||
"ja": `<p>全てのノードの編集ダイアログの下に、ノードのヘルプへのリンクが追加されました。</p>
|
||||
<p>これをクリックすると、ノードのヘルプサイドバーが表示されます。</p>`
|
||||
}
|
||||
},
|
||||
{
|
||||
title: {
|
||||
"en-US": "And lots more...",
|
||||
"ja": "そしてさらに沢山あります..."
|
||||
},
|
||||
description: {
|
||||
"en-US": `<p>Of course we have everything from 3.1.0-beta.1 as well....</p>`,
|
||||
"ja": `<p>もちろん3.1.0 ベータ1の全ての機能があります....</p>`
|
||||
"en-US": "<p>This is the first beta release for 3.1.0 and we have a few new features to tell you about.</p>",
|
||||
"ja": "<p>これは3.1.0の最初のベータリリースです。いくつかの新機能について説明します。</p>"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -153,13 +78,13 @@ export default {
|
||||
{
|
||||
title: {
|
||||
"en-US": "Adding Mermaid Diagrams",
|
||||
"ja": "Mermaid図を追加"
|
||||
"ja": "Mermaidの図を追加"
|
||||
},
|
||||
image: 'images/mermaid.png',
|
||||
description: {
|
||||
"en-US": `<p>You can also add <a href="https://github.com/mermaid-js/mermaid">Mermaid</a> diagrams directly into your node or flow descriptions.</p>
|
||||
<p>This gives you much richer options for documenting your flows.</p>`,
|
||||
"ja": `<p>ノードやフローの説明に、<a href="https://github.com/mermaid-js/mermaid">Mermaid</a>図を直接追加することもできます。</p>
|
||||
"ja": `<p>ノードやフローの説明に、<a href="https://github.com/mermaid-js/mermaid">Mermaid</a>の図を直接追加することもできます。</p>
|
||||
<p>これによって、フローを説明する文書作成の選択肢がより多くなります。</p>`
|
||||
},
|
||||
},
|
||||
|
@@ -35,11 +35,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
else { node.previous = {}; }
|
||||
}
|
||||
var value;
|
||||
try {
|
||||
value = RED.util.getMessageProperty(msg,node.property);
|
||||
}
|
||||
catch(e) { }
|
||||
var value = RED.util.getMessageProperty(msg,node.property);
|
||||
if (value !== undefined) {
|
||||
var t = "_no_topic";
|
||||
if (node.septopics) { t = topic || t; }
|
||||
|
@@ -249,12 +249,6 @@
|
||||
<span id="node-config-input-cleansession-label" data-i18n="mqtt.label.cleansession"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-row mqtt-persistence">
|
||||
<label for="node-config-input-autoUnsubscribe" style="width: auto;">
|
||||
<input type="checkbox" id="node-config-input-autoUnsubscribe" style="position: relative;vertical-align: bottom; top: -2px; width: 15px;height: 15px;">
|
||||
<span id="node-config-input-autoUnsubscribe-label" data-i18n="mqtt.label.autoUnsubscribe"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-row mqtt5">
|
||||
<label style="width:auto" for="node-config-input-sessionExpiry"><span data-i18n="mqtt.label.sessionExpiry"></span></label>
|
||||
<input type="number" min="0" id="node-config-input-sessionExpiry" style="width: 100px" >
|
||||
@@ -489,23 +483,17 @@
|
||||
tls: {type:"tls-config",required: false,
|
||||
label:RED._("node-red:mqtt.label.use-tls") },
|
||||
clientid: {value:"", validate: function(v, opt) {
|
||||
let ok = true;
|
||||
var ok = false;
|
||||
if ($("#node-config-input-clientid").length) {
|
||||
// Currently editing the node
|
||||
let needClientId = !$("#node-config-input-cleansession").is(":checked") || !$("#node-config-input-autoUnsubscribe").is(":checked")
|
||||
if (needClientId) {
|
||||
ok = (v||"").length > 0;
|
||||
}
|
||||
ok = $("#node-config-input-cleansession").is(":checked") || (v||"").length > 0;
|
||||
} else {
|
||||
let needClientId = !(this.cleansession===undefined || this.cleansession) || this.autoUnsubscribe;
|
||||
if (needClientId) {
|
||||
ok = (v||"").length > 0;
|
||||
}
|
||||
ok = (this.cleansession===undefined || this.cleansession) || (v||"").length > 0;
|
||||
}
|
||||
if (!ok) {
|
||||
return RED._("node-red:mqtt.errors.invalid-client-id");
|
||||
if (ok) {
|
||||
return ok;
|
||||
}
|
||||
return true;
|
||||
return RED._("node-red:mqtt.errors.invalid-client-id");
|
||||
}},
|
||||
autoConnect: {value: true},
|
||||
usetls: {value: false},
|
||||
@@ -517,7 +505,6 @@
|
||||
label: RED._("node-red:mqtt.label.keepalive"),
|
||||
validate:RED.validators.number(false)},
|
||||
cleansession: {value: true},
|
||||
autoUnsubscribe: {value: true},
|
||||
birthTopic: {value:"", validate:validateMQTTPublishTopic},
|
||||
birthQos: {value:"0"},
|
||||
birthRetain: {value:"false"},
|
||||
@@ -633,10 +620,6 @@
|
||||
this.cleansession = true;
|
||||
$("#node-config-input-cleansession").prop("checked",true);
|
||||
}
|
||||
if (typeof this.autoUnsubscribe === 'undefined') {
|
||||
this.autoUnsubscribe = true;
|
||||
$("#node-config-input-autoUnsubscribe").prop("checked",true);
|
||||
}
|
||||
if (typeof this.usetls === 'undefined') {
|
||||
this.usetls = false;
|
||||
$("#node-config-input-usetls").prop("checked",false);
|
||||
@@ -652,14 +635,6 @@
|
||||
if (typeof this.protocolVersion === 'undefined') {
|
||||
this.protocolVersion = 4;
|
||||
}
|
||||
$("#node-config-input-cleansession").on("change", function() {
|
||||
const useCleanSession = $("#node-config-input-cleansession").is(':checked');
|
||||
if(useCleanSession) {
|
||||
$("div.form-row.mqtt-persistence").hide();
|
||||
} else {
|
||||
$("div.form-row.mqtt-persistence").show();
|
||||
}
|
||||
});
|
||||
$("#node-config-input-protocolVersion").on("change", function() {
|
||||
var v5 = $("#node-config-input-protocolVersion").val() == "5";
|
||||
if(v5) {
|
||||
|
@@ -482,7 +482,6 @@ module.exports = function(RED) {
|
||||
setIfHasProperty(opts, node, "protocolVersion", init);
|
||||
setIfHasProperty(opts, node, "keepalive", init);
|
||||
setIfHasProperty(opts, node, "cleansession", init);
|
||||
setIfHasProperty(opts, node, "autoUnsubscribe", init);
|
||||
setIfHasProperty(opts, node, "topicAliasMaximum", init);
|
||||
setIfHasProperty(opts, node, "maximumPacketSize", init);
|
||||
setIfHasProperty(opts, node, "receiveMaximum", init);
|
||||
@@ -591,9 +590,6 @@ module.exports = function(RED) {
|
||||
if (typeof node.cleansession === 'undefined') {
|
||||
node.cleansession = true;
|
||||
}
|
||||
if (typeof node.autoUnsubscribe === 'undefined') {
|
||||
node.autoUnsubscribe = true;
|
||||
}
|
||||
|
||||
//use url or build a url from usetls://broker:port
|
||||
if (node.url && node.brokerurl !== node.url) {
|
||||
@@ -664,7 +660,6 @@ module.exports = function(RED) {
|
||||
node.options.password = node.password;
|
||||
node.options.keepalive = node.keepalive;
|
||||
node.options.clean = node.cleansession;
|
||||
node.options.autoUnsubscribe = node.autoUnsubscribe;
|
||||
node.options.clientId = node.clientid || 'nodered_' + RED.util.generateId();
|
||||
node.options.reconnectPeriod = RED.settings.mqttReconnectTime||5000;
|
||||
delete node.options.protocolId; //V4+ default
|
||||
@@ -1233,16 +1228,12 @@ module.exports = function(RED) {
|
||||
node.on('close', function(removed, done) {
|
||||
if (node.brokerConn) {
|
||||
if(node.isDynamic) {
|
||||
if (node.brokerConn.options.autoUnsubscribe) {
|
||||
Object.keys(node.dynamicSubs).forEach(function (topic) {
|
||||
node.brokerConn.unsubscribe(topic, node.id, removed);
|
||||
});
|
||||
node.dynamicSubs = {};
|
||||
}
|
||||
Object.keys(node.dynamicSubs).forEach(function (topic) {
|
||||
node.brokerConn.unsubscribe(topic, node.id, removed);
|
||||
});
|
||||
node.dynamicSubs = {};
|
||||
} else {
|
||||
if (node.brokerConn.options.autoUnsubscribe) {
|
||||
node.brokerConn.unsubscribe(node.topic, node.id, removed);
|
||||
}
|
||||
node.brokerConn.unsubscribe(node.topic,node.id, removed);
|
||||
}
|
||||
node.brokerConn.deregister(node, done, removed);
|
||||
node.brokerConn = null;
|
||||
|
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
**/
|
||||
|
||||
module.exports = async function(RED) {
|
||||
module.exports = function(RED) {
|
||||
"use strict";
|
||||
const { got } = await import('got')
|
||||
const got = require("got");
|
||||
const {CookieJar} = require("tough-cookie");
|
||||
const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent');
|
||||
const FormData = require('form-data');
|
||||
@@ -210,24 +210,24 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
// set defaultport, else when using HttpsProxyAgent, it's defaultPort of 443 will be used :(.
|
||||
// Had to remove this to get http->https redirect to work
|
||||
// opts.defaultPort = isHttps?443:80;
|
||||
opts.timeout = { request: node.reqTimeout || 5000 };
|
||||
opts.timeout = node.reqTimeout;
|
||||
opts.throwHttpErrors = false;
|
||||
// TODO: add UI option to auto decompress. Setting to false for 1.x compatibility
|
||||
opts.decompress = false;
|
||||
opts.method = method;
|
||||
opts.retry = { limit: 0 };
|
||||
opts.retry = 0;
|
||||
opts.responseType = 'buffer';
|
||||
opts.maxRedirects = 21;
|
||||
opts.cookieJar = new CookieJar();
|
||||
opts.ignoreInvalidCookies = true;
|
||||
// opts.forever = nodeHTTPPersistent;
|
||||
opts.forever = nodeHTTPPersistent;
|
||||
if (msg.requestTimeout !== undefined) {
|
||||
if (isNaN(msg.requestTimeout)) {
|
||||
node.warn(RED._("httpin.errors.timeout-isnan"));
|
||||
} else if (msg.requestTimeout < 1) {
|
||||
node.warn(RED._("httpin.errors.timeout-isnegative"));
|
||||
} else {
|
||||
opts.timeout = { request: msg.requestTimeout };
|
||||
opts.timeout = msg.requestTimeout;
|
||||
}
|
||||
}
|
||||
const originalHeaderMap = {};
|
||||
@@ -245,12 +245,9 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
delete options.headers[h];
|
||||
}
|
||||
})
|
||||
|
||||
if (node.insecureHTTPParser) {
|
||||
// Setting the property under _unixOptions as pretty
|
||||
// much the only hack available to get got to apply
|
||||
// a core http option it doesn't think we should be
|
||||
// allowed to set
|
||||
options._unixOptions = { ...options.unixOptions, insecureHTTPParser: true }
|
||||
options.insecureHTTPParser = true
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -406,16 +403,15 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
return response
|
||||
}
|
||||
const requestUrl = new URL(response.request.requestUrl);
|
||||
const options = { headers: {} }
|
||||
const options = response.request.options;
|
||||
const normalisedHeaders = {};
|
||||
Object.keys(response.headers).forEach(k => {
|
||||
normalisedHeaders[k.toLowerCase()] = response.headers[k]
|
||||
})
|
||||
if (normalisedHeaders['www-authenticate']) {
|
||||
let authHeader = buildDigestHeader(digestCreds.user,digestCreds.password, response.request.options.method, requestUrl.pathname, normalisedHeaders['www-authenticate'])
|
||||
let authHeader = buildDigestHeader(digestCreds.user,digestCreds.password, options.method, requestUrl.pathname, normalisedHeaders['www-authenticate'])
|
||||
options.headers.Authorization = authHeader;
|
||||
}
|
||||
// response.request.options.merge(options)
|
||||
sentCreds = true;
|
||||
return retry(options);
|
||||
}
|
||||
@@ -703,43 +699,25 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
});
|
||||
|
||||
const md5 = (value) => { return crypto.createHash('md5').update(value).digest('hex') }
|
||||
const sha256 = (value) => { return crypto.createHash('sha256').update(value).digest('hex') }
|
||||
const sha512 = (value) => { return crypto.createHash('sha512').update(value).digest('hex') }
|
||||
|
||||
function digestCompute(algorithm, value) {
|
||||
var lowercaseAlgorithm = ""
|
||||
if (algorithm) {
|
||||
lowercaseAlgorithm = algorithm.toLowerCase().replace(/-sess$/, '')
|
||||
}
|
||||
|
||||
if (lowercaseAlgorithm === "sha-256") {
|
||||
return sha256(value)
|
||||
} else if (lowercaseAlgorithm === "sha-512-256") {
|
||||
var hash = sha512(value)
|
||||
return hash.slice(0, 64) // Only use the first 256 bits
|
||||
} else {
|
||||
return md5(value)
|
||||
}
|
||||
}
|
||||
|
||||
function ha1Compute(algorithm, user, realm, pass, nonce, cnonce) {
|
||||
/**
|
||||
* RFC 2617: handle both standard and -sess algorithms.
|
||||
* RFC 2617: handle both MD5 and MD5-sess algorithms.
|
||||
*
|
||||
* If the algorithm directive's value ends with "-sess", then HA1 is
|
||||
* HA1=digestCompute(digestCompute(username:realm:password):nonce:cnonce)
|
||||
*
|
||||
* If the algorithm directive's value does not end with "-sess", then HA1 is
|
||||
* HA1=digestCompute(username:realm:password)
|
||||
* If the algorithm directive's value is "MD5" or unspecified, then HA1 is
|
||||
* HA1=MD5(username:realm:password)
|
||||
* If the algorithm directive's value is "MD5-sess", then HA1 is
|
||||
* HA1=MD5(MD5(username:realm:password):nonce:cnonce)
|
||||
*/
|
||||
var ha1 = digestCompute(algorithm, user + ':' + realm + ':' + pass)
|
||||
if (algorithm && /-sess$/i.test(algorithm)) {
|
||||
return digestCompute(algorithm, ha1 + ':' + nonce + ':' + cnonce)
|
||||
var ha1 = md5(user + ':' + realm + ':' + pass)
|
||||
if (algorithm && algorithm.toLowerCase() === 'md5-sess') {
|
||||
return md5(ha1 + ':' + nonce + ':' + cnonce)
|
||||
} else {
|
||||
return ha1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function buildDigestHeader(user, pass, method, path, authHeader) {
|
||||
var challenge = {}
|
||||
var re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi
|
||||
@@ -754,10 +732,10 @@ in your Node-RED user directory (${RED.settings.userDir}).
|
||||
var nc = qop && '00000001'
|
||||
var cnonce = qop && uuid().replace(/-/g, '')
|
||||
var ha1 = ha1Compute(challenge.algorithm, user, challenge.realm, pass, challenge.nonce, cnonce)
|
||||
var ha2 = digestCompute(challenge.algorithm, method + ':' + path)
|
||||
var ha2 = md5(method + ':' + path)
|
||||
var digestResponse = qop
|
||||
? digestCompute(challenge.algorithm, ha1 + ':' + challenge.nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2)
|
||||
: digestCompute(challenge.algorithm, ha1 + ':' + challenge.nonce + ':' + ha2)
|
||||
? md5(ha1 + ':' + challenge.nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2)
|
||||
: md5(ha1 + ':' + challenge.nonce + ':' + ha2)
|
||||
var authValues = {
|
||||
username: user,
|
||||
realm: challenge.realm,
|
||||
|
@@ -33,13 +33,7 @@ module.exports = function(RED) {
|
||||
parseString(value, options, function (err, result) {
|
||||
if (err) { done(err); }
|
||||
else {
|
||||
// TODO: With xml2js@0.5.0, they return an object with
|
||||
// a null prototype. This could cause unexpected
|
||||
// issues. So for now, we have to reconstruct
|
||||
// the object with a proper prototype.
|
||||
// Once https://github.com/Leonidas-from-XIV/node-xml2js/pull/674
|
||||
// is merged, we can revisit and hopefully remove this hack
|
||||
value = fixObj(result)
|
||||
value = result;
|
||||
RED.util.setMessageProperty(msg,node.property,value);
|
||||
send(msg);
|
||||
done();
|
||||
@@ -52,18 +46,4 @@ module.exports = function(RED) {
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("xml",XMLNode);
|
||||
|
||||
|
||||
function fixObj(obj) {
|
||||
const res = {}
|
||||
const keys = Object.keys(obj)
|
||||
keys.forEach(k => {
|
||||
if (typeof obj[k] === 'object' && obj[k]) {
|
||||
res[k] = fixObj(obj[k])
|
||||
} else {
|
||||
res[k] = obj[k]
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
@@ -117,9 +117,7 @@ module.exports = function(RED) {
|
||||
}
|
||||
if (typeof data === "boolean") { data = data.toString(); }
|
||||
if (typeof data === "number") { data = data.toString(); }
|
||||
var aflg = true;
|
||||
if (msg.hasOwnProperty("parts") && msg.parts.type === "string" && (msg.parts.count === msg.parts.index + 1)) { aflg = false; }
|
||||
if ((node.appendNewline) && (!Buffer.isBuffer(data)) && aflg) { data += os.EOL; }
|
||||
if ((node.appendNewline) && (!Buffer.isBuffer(data))) { data += os.EOL; }
|
||||
var buf;
|
||||
if (node.encoding === "setbymsg") {
|
||||
buf = encode(data, msg.encoding || "none");
|
||||
@@ -316,6 +314,7 @@ module.exports = function(RED) {
|
||||
});
|
||||
filename = filename || "";
|
||||
var fullFilename = filename;
|
||||
var filePath = "";
|
||||
if (filename && RED.settings.fileWorkingDirectory && !path.isAbsolute(filename)) {
|
||||
fullFilename = path.resolve(path.join(RED.settings.fileWorkingDirectory,filename));
|
||||
}
|
||||
|
@@ -362,7 +362,6 @@
|
||||
"port": "Port",
|
||||
"keepalive": "Keep-Alive",
|
||||
"cleansession": "Bereinigte Sitzung (clean session) verwenden",
|
||||
"autoUnsubscribe": "Abonnement bei Verbindungsende automatisch beenden",
|
||||
"cleanstart": "Verwende bereinigten Start",
|
||||
"use-tls": "TLS",
|
||||
"tls-config": "TLS-Konfiguration",
|
||||
|
@@ -414,7 +414,6 @@
|
||||
"port": "Port",
|
||||
"keepalive": "Keep Alive",
|
||||
"cleansession": "Use clean session",
|
||||
"autoUnsubscribe": "Automatically unsubscribe when disconnecting",
|
||||
"cleanstart": "Use clean start",
|
||||
"use-tls": "Use TLS",
|
||||
"tls-config": "TLS Configuration",
|
||||
|
@@ -414,7 +414,6 @@
|
||||
"port": "ポート",
|
||||
"keepalive": "キープアライブ時間",
|
||||
"cleansession": "セッションの初期化",
|
||||
"autoUnsubscribe": "切断時に購読を自動解除",
|
||||
"cleanstart": "クリーンスタート",
|
||||
"use-tls": "TLSを使用",
|
||||
"tls-config": "TLS設定",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/nodes",
|
||||
"version": "3.1.0-beta.2",
|
||||
"version": "3.1.0-beta.1",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -27,8 +27,8 @@
|
||||
"cronosjs": "1.7.1",
|
||||
"denque": "2.1.0",
|
||||
"form-data": "4.0.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"got": "12.6.0",
|
||||
"fs-extra": "10.1.0",
|
||||
"got": "11.8.6",
|
||||
"hash-sum": "2.0.0",
|
||||
"hpagent": "1.2.0",
|
||||
"https-proxy-agent": "5.0.1",
|
||||
@@ -44,7 +44,7 @@
|
||||
"tough-cookie": "4.1.2",
|
||||
"uuid": "9.0.0",
|
||||
"ws": "7.5.6",
|
||||
"xml2js": "0.5.0",
|
||||
"xml2js": "0.4.23",
|
||||
"iconv-lite": "0.6.3"
|
||||
}
|
||||
}
|
||||
|
@@ -242,68 +242,63 @@ async function ensureModuleDir() {
|
||||
}
|
||||
}
|
||||
|
||||
let installLock = Promise.resolve()
|
||||
async function installModule(moduleDetails) {
|
||||
const result = installLock.then(async () => {
|
||||
let installSpec = moduleDetails.module;
|
||||
if (!registryUtil.checkModuleAllowed( moduleDetails.module, moduleDetails.version,installAllowList,installDenyList)) {
|
||||
const e = new Error("Install not allowed");
|
||||
e.code = "install_not_allowed";
|
||||
let installSpec = moduleDetails.module;
|
||||
if (!registryUtil.checkModuleAllowed( moduleDetails.module, moduleDetails.version,installAllowList,installDenyList)) {
|
||||
const e = new Error("Install not allowed");
|
||||
e.code = "install_not_allowed";
|
||||
throw e;
|
||||
}
|
||||
if (moduleDetails.version) {
|
||||
installSpec = installSpec+"@"+moduleDetails.version;
|
||||
}
|
||||
log.info(log._("server.install.installing",{name: moduleDetails.module,version: moduleDetails.version||"latest"}));
|
||||
const installDir = getInstallDir();
|
||||
|
||||
await ensureModuleDir();
|
||||
|
||||
let triggerPayload = {
|
||||
"module": moduleDetails.module,
|
||||
"version": moduleDetails.version,
|
||||
"dir": installDir,
|
||||
"args": ["--production","--engine-strict"]
|
||||
}
|
||||
return hooks.trigger("preInstall", triggerPayload).then((result) => {
|
||||
// preInstall passed
|
||||
// - run install
|
||||
if (result !== false) {
|
||||
let extraArgs = triggerPayload.args || [];
|
||||
let args = ['install', ...extraArgs, installSpec]
|
||||
log.trace(NPM_COMMAND + JSON.stringify(args));
|
||||
return exec.run(NPM_COMMAND, args, { cwd: installDir },true)
|
||||
} else {
|
||||
log.trace("skipping npm install");
|
||||
}
|
||||
}).then(() => {
|
||||
return hooks.trigger("postInstall", triggerPayload)
|
||||
}).then(() => {
|
||||
log.info(log._("server.install.installed", { name: installSpec }));
|
||||
const runtimeInstalledModules = settings.get("modules") || {};
|
||||
runtimeInstalledModules[moduleDetails.module] = moduleDetails;
|
||||
settings.set("modules",runtimeInstalledModules)
|
||||
}).catch(result => {
|
||||
var output = result.stderr || result.toString();
|
||||
var e;
|
||||
if (/E404/.test(output) || /ETARGET/.test(output)) {
|
||||
log.error(log._("server.install.install-failed-not-found",{name:installSpec}));
|
||||
e = new Error("Module not found");
|
||||
e.code = 404;
|
||||
throw e;
|
||||
} else {
|
||||
log.error(log._("server.install.install-failed-long",{name:installSpec}));
|
||||
log.error("------------------------------------------");
|
||||
log.error(output);
|
||||
log.error("------------------------------------------");
|
||||
e = new Error(log._("server.install.install-failed"));
|
||||
e.code = "unexpected_error";
|
||||
throw e;
|
||||
}
|
||||
if (moduleDetails.version) {
|
||||
installSpec = installSpec+"@"+moduleDetails.version;
|
||||
}
|
||||
log.info(log._("server.install.installing",{name: moduleDetails.module,version: moduleDetails.version||"latest"}));
|
||||
const installDir = getInstallDir();
|
||||
|
||||
await ensureModuleDir();
|
||||
|
||||
let triggerPayload = {
|
||||
"module": moduleDetails.module,
|
||||
"version": moduleDetails.version,
|
||||
"dir": installDir,
|
||||
"args": ["--production","--engine-strict"]
|
||||
}
|
||||
return hooks.trigger("preInstall", triggerPayload).then((result) => {
|
||||
// preInstall passed
|
||||
// - run install
|
||||
if (result !== false) {
|
||||
let extraArgs = triggerPayload.args || [];
|
||||
let args = ['install', ...extraArgs, installSpec]
|
||||
log.trace(NPM_COMMAND + JSON.stringify(args));
|
||||
return exec.run(NPM_COMMAND, args, { cwd: installDir },true)
|
||||
} else {
|
||||
log.trace("skipping npm install");
|
||||
}
|
||||
}).then(() => {
|
||||
return hooks.trigger("postInstall", triggerPayload)
|
||||
}).then(() => {
|
||||
log.info(log._("server.install.installed", { name: installSpec }));
|
||||
const runtimeInstalledModules = settings.get("modules") || {};
|
||||
runtimeInstalledModules[moduleDetails.module] = moduleDetails;
|
||||
settings.set("modules",runtimeInstalledModules)
|
||||
}).catch(result => {
|
||||
var output = result.stderr || result.toString();
|
||||
var e;
|
||||
if (/E404/.test(output) || /ETARGET/.test(output)) {
|
||||
log.error(log._("server.install.install-failed-not-found",{name:installSpec}));
|
||||
e = new Error("Module not found");
|
||||
e.code = 404;
|
||||
throw e;
|
||||
} else {
|
||||
log.error(log._("server.install.install-failed-long",{name:installSpec}));
|
||||
log.error("------------------------------------------");
|
||||
log.error(output);
|
||||
log.error("------------------------------------------");
|
||||
e = new Error(log._("server.install.install-failed"));
|
||||
e.code = "unexpected_error";
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
})
|
||||
installLock = result.catch(() => {})
|
||||
return result
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/registry",
|
||||
"version": "3.1.0-beta.2",
|
||||
"version": "3.1.0-beta.1",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,10 +16,10 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/util": "3.1.0-beta.2",
|
||||
"@node-red/util": "3.1.0-beta.1",
|
||||
"clone": "2.1.2",
|
||||
"fs-extra": "11.1.1",
|
||||
"semver": "7.5.0",
|
||||
"fs-extra": "10.1.0",
|
||||
"semver": "7.3.8",
|
||||
"tar": "6.1.13",
|
||||
"uglify-js": "3.17.4"
|
||||
}
|
||||
|
@@ -818,16 +818,6 @@ function handlePreRoute(flow, sendEvent, reportError) {
|
||||
})
|
||||
}
|
||||
|
||||
function deliverMessageToDestination(sendEvent) {
|
||||
if (sendEvent?.destination?.node) {
|
||||
try {
|
||||
sendEvent.destination.node.receive(sendEvent.msg);
|
||||
} catch(err) {
|
||||
Log.error(`Error delivering message to node:${sendEvent.destination.node._path} [${sendEvent.destination.node.type}]`)
|
||||
Log.error(err.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
function handlePreDeliver(flow,sendEvent, reportError) {
|
||||
// preDeliver - the local router has identified the node it is going to send to. At this point, the message has been cloned if needed.
|
||||
hooks.trigger("preDeliver",sendEvent,(err) => {
|
||||
@@ -837,10 +827,15 @@ function handlePreDeliver(flow,sendEvent, reportError) {
|
||||
} else if (err !== false) {
|
||||
if (asyncMessageDelivery) {
|
||||
setImmediate(function() {
|
||||
deliverMessageToDestination(sendEvent)
|
||||
if (sendEvent.destination.node) {
|
||||
sendEvent.destination.node.receive(sendEvent.msg);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
deliverMessageToDestination(sendEvent)
|
||||
if (sendEvent.destination.node) {
|
||||
sendEvent.destination.node.receive(sendEvent.msg);
|
||||
|
||||
}
|
||||
}
|
||||
// postDeliver - the message has been dispatched to be delivered asynchronously (unless the sync delivery flag is set, in which case it would be continue as synchronous delivery)
|
||||
hooks.trigger("postDeliver", sendEvent, function(err) {
|
||||
|
@@ -474,7 +474,7 @@ class Subflow extends Flow {
|
||||
*/
|
||||
function createNodeInSubflow(subflowInstanceId, def) {
|
||||
let node = clone(def);
|
||||
let nid = `${subflowInstanceId}-${node.id}` //redUtil.generateId();
|
||||
let nid = redUtil.generateId();
|
||||
// console.log("Create Node In subflow",node._alias, "--->",nid, "(",node.type,")")
|
||||
// node_map[node.id] = node;
|
||||
node._alias = node.id;
|
||||
|
@@ -201,9 +201,7 @@ function parseConfig(config) {
|
||||
if (subflowDetails) {
|
||||
var subflowType = subflowDetails[1]
|
||||
n.subflow = subflowType;
|
||||
if (flow.subflows[subflowType]) {
|
||||
flow.subflows[subflowType].instances.push(n)
|
||||
}
|
||||
flow.subflows[subflowType].instances.push(n)
|
||||
}
|
||||
if (container) {
|
||||
container.nodes[n.id] = n;
|
||||
|
@@ -89,15 +89,6 @@ function init(userSettings,httpServer,_adminApi) {
|
||||
|
||||
nodeApp = express();
|
||||
adminApp = express();
|
||||
const defaultServerSettings = {
|
||||
"x-powered-by": false
|
||||
}
|
||||
const serverSettings = Object.assign({},defaultServerSettings,userSettings.httpServerOptions||{});
|
||||
for (let eOption in serverSettings) {
|
||||
nodeApp.set(eOption, serverSettings[eOption]);
|
||||
adminApp.set(eOption, serverSettings[eOption]);
|
||||
}
|
||||
|
||||
|
||||
if (_adminApi) {
|
||||
adminApi = _adminApi;
|
||||
|
@@ -589,28 +589,17 @@ function deleteContext(id,flowId) {
|
||||
* If flowConfig is undefined, all flow/node contexts will be removed
|
||||
**/
|
||||
function clean(flowConfig) {
|
||||
flowConfig = flowConfig || { allNodes: {}, subflows: {} };
|
||||
const knownNodes = new Set(Object.keys(flowConfig.allNodes))
|
||||
|
||||
// We need to alias all of the subflow instance contents
|
||||
for (const subflow of Object.values(flowConfig.subflows || {})) {
|
||||
subflow.instances.forEach(instance => {
|
||||
for (const nodeId of Object.keys(subflow.nodes || {})) {
|
||||
knownNodes.add(`${instance.id}-${nodeId}`)
|
||||
}
|
||||
for (const nodeId of Object.keys(subflow.configs || {})) {
|
||||
knownNodes.add(`${instance.id}-${nodeId}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
flowConfig = flowConfig || { allNodes: {} };
|
||||
var promises = [];
|
||||
for (const store of Object.values(stores)){
|
||||
promises.push(store.clean(Array.from(knownNodes)));
|
||||
for(var plugin in stores){
|
||||
if(stores.hasOwnProperty(plugin)){
|
||||
promises.push(stores[plugin].clean(Object.keys(flowConfig.allNodes)));
|
||||
}
|
||||
}
|
||||
for (const id of Object.keys(contexts)) {
|
||||
if (id !== "global") {
|
||||
for (var id in contexts) {
|
||||
if (contexts.hasOwnProperty(id) && id !== "global") {
|
||||
var idParts = id.split(":");
|
||||
if (!knownNodes.has(idParts[0])) {
|
||||
if (!flowConfig.allNodes.hasOwnProperty(idParts[0])) {
|
||||
delete contexts[id];
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/runtime",
|
||||
"version": "3.1.0-beta.2",
|
||||
"version": "3.1.0-beta.1",
|
||||
"license": "Apache-2.0",
|
||||
"main": "./lib/index.js",
|
||||
"repository": {
|
||||
@@ -16,12 +16,12 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/registry": "3.1.0-beta.2",
|
||||
"@node-red/util": "3.1.0-beta.2",
|
||||
"@node-red/registry": "3.1.0-beta.1",
|
||||
"@node-red/util": "3.1.0-beta.1",
|
||||
"async-mutex": "0.4.0",
|
||||
"clone": "2.1.2",
|
||||
"express": "4.18.2",
|
||||
"fs-extra": "11.1.1",
|
||||
"fs-extra": "10.1.0",
|
||||
"json-stringify-safe": "5.0.1"
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@node-red/util",
|
||||
"version": "3.1.0-beta.2",
|
||||
"version": "3.1.0-beta.1",
|
||||
"license": "Apache-2.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -15,12 +15,12 @@
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"fs-extra": "11.1.1",
|
||||
"fs-extra": "10.1.0",
|
||||
"i18next": "21.10.0",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"jsonata": "1.8.6",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"moment": "2.29.4",
|
||||
"moment-timezone": "0.5.43"
|
||||
"moment-timezone": "0.5.41"
|
||||
}
|
||||
}
|
||||
|
14
packages/node_modules/node-red/package.json
vendored
14
packages/node_modules/node-red/package.json
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "3.1.0-beta.2",
|
||||
"version": "3.1.0-beta.1",
|
||||
"description": "Low-code programming for event-driven applications",
|
||||
"homepage": "http://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@@ -31,17 +31,17 @@
|
||||
"flow"
|
||||
],
|
||||
"dependencies": {
|
||||
"@node-red/editor-api": "3.1.0-beta.2",
|
||||
"@node-red/runtime": "3.1.0-beta.2",
|
||||
"@node-red/util": "3.1.0-beta.2",
|
||||
"@node-red/nodes": "3.1.0-beta.2",
|
||||
"@node-red/editor-api": "3.1.0-beta.1",
|
||||
"@node-red/runtime": "3.1.0-beta.1",
|
||||
"@node-red/util": "3.1.0-beta.1",
|
||||
"@node-red/nodes": "3.1.0-beta.1",
|
||||
"basic-auth": "2.0.1",
|
||||
"bcryptjs": "2.4.3",
|
||||
"express": "4.18.2",
|
||||
"fs-extra": "11.1.1",
|
||||
"fs-extra": "10.1.0",
|
||||
"node-red-admin": "^3.0.0",
|
||||
"nopt": "5.0.0",
|
||||
"semver": "7.5.0"
|
||||
"semver": "7.3.8"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"bcrypt": "5.1.0"
|
||||
|
2
packages/node_modules/node-red/red.js
vendored
2
packages/node_modules/node-red/red.js
vendored
@@ -458,7 +458,7 @@ httpsPromise.then(function(startupHttps) {
|
||||
RED.start().then(function() {
|
||||
if (settings.httpAdminRoot !== false || settings.httpNodeRoot !== false || settings.httpStatic) {
|
||||
server.on('error', function(err) {
|
||||
if (err.code === "EADDRINUSE") {
|
||||
if (err.errno === "EADDRINUSE") {
|
||||
RED.log.error(RED.log._("server.unable-to-listen", {listenpath:getListenPath()}));
|
||||
RED.log.error(RED.log._("server.port-in-use"));
|
||||
} else {
|
||||
|
2
packages/node_modules/node-red/settings.js
vendored
2
packages/node_modules/node-red/settings.js
vendored
@@ -416,7 +416,7 @@ module.exports = {
|
||||
*/
|
||||
// theme: "vs",
|
||||
/** other overrides can be set e.g. fontSize, fontFamily, fontLigatures etc.
|
||||
* for the full list, see https://microsoft.github.io/monaco-editor/docs.html#interfaces/editor.IStandaloneEditorConstructionOptions.html
|
||||
* for the full list, see https://microsoft.github.io/monaco-editor/api/interfaces/monaco.editor.IStandaloneEditorConstructionOptions.html
|
||||
*/
|
||||
//fontSize: 14,
|
||||
//fontFamily: "Cascadia Code, Fira Code, Consolas, 'Courier New', monospace",
|
||||
|
@@ -854,7 +854,7 @@ describe('inject node', function() {
|
||||
});
|
||||
n1.on("call:error", function(err) {
|
||||
count++;
|
||||
if (count == 1) {
|
||||
if (count == 2) {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
@@ -31,7 +31,6 @@ var multer = require("multer");
|
||||
var RED = require("nr-test-utils").require("node-red/lib/red");
|
||||
var fs = require('fs-extra');
|
||||
var auth = require('basic-auth');
|
||||
var crypto = require("crypto");
|
||||
const { version } = require("os");
|
||||
const net = require('net')
|
||||
|
||||
@@ -164,100 +163,6 @@ describe('HTTP Request Node', function() {
|
||||
delete process.env.NO_PROXY;
|
||||
}
|
||||
|
||||
function getDigestPassword() {
|
||||
return 'digest-test-password';
|
||||
}
|
||||
|
||||
function getDigest(algorithm, value) {
|
||||
var hash;
|
||||
if (algorithm === 'SHA-256') {
|
||||
hash = crypto.createHash('sha256');
|
||||
} else if (algorithm === 'SHA-512-256') {
|
||||
hash = crypto.createHash('sha512');
|
||||
} else {
|
||||
hash = crypto.createHash('md5');
|
||||
}
|
||||
|
||||
var hex = hash.update(value).digest('hex');
|
||||
if (algorithm === 'SHA-512-256') {
|
||||
hex = hex.slice(0, 64);
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
function getDigestResponse(req, algorithm, sess, realm, username, nonce, nc, cnonce, qop) {
|
||||
var ha1 = getDigest(algorithm, username + ':' + realm + ':' + getDigestPassword());
|
||||
if (sess) {
|
||||
ha1 = getDigest(algorithm, ha1 + ':' + nonce + ':' + cnonce)
|
||||
}
|
||||
let ha2 = getDigest(algorithm, req.method + ':' + req.path);
|
||||
return qop
|
||||
? getDigest(algorithm, ha1 + ':' + nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2)
|
||||
: getDigest(algorithm, ha1 + ':' + nonce + ':' + ha2);
|
||||
}
|
||||
|
||||
function handleDigestResponse(req, res, algorithm, sess, qop) {
|
||||
let realm = "node-red";
|
||||
let nonce = "123456";
|
||||
let nc = '00000001';
|
||||
let algorithmValue = sess ? `${algorithm}-sess` : algorithm;
|
||||
|
||||
let authHeader = req.headers['authorization'];
|
||||
if (!authHeader) {
|
||||
let qopField = qop ? `qop="${qop}", ` : '';
|
||||
|
||||
res.setHeader(
|
||||
'WWW-Authenticate',
|
||||
`Digest ${qopField}realm="${realm}", nonce="${nonce}", algorithm="${algorithmValue}"`
|
||||
);
|
||||
res.status(401).end();
|
||||
return;
|
||||
}
|
||||
|
||||
var authFields = {};
|
||||
let re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi;
|
||||
for (;;) {
|
||||
var match = re.exec(authHeader);
|
||||
if (!match) {
|
||||
break;
|
||||
}
|
||||
authFields[match[1]] = match[2] || match[3];
|
||||
}
|
||||
// console.log(JSON.stringify(authFields));
|
||||
|
||||
if (qop && authFields['qop'] != qop) {
|
||||
console.log('test1');
|
||||
res.status(401).end();
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
!authFields['username'] ||
|
||||
!authFields['response'] ||
|
||||
authFields['realm'] != realm ||
|
||||
authFields['nonce'] != nonce ||
|
||||
authFields['algorithm'] != algorithmValue
|
||||
) {
|
||||
console.log('test2');
|
||||
res.status(401).end();
|
||||
return;
|
||||
}
|
||||
|
||||
let username = authFields['username'];
|
||||
let response = authFields['response'];
|
||||
let cnonce = authFields['cnonce'] || '';
|
||||
let expectedResponse = getDigestResponse(
|
||||
req, algorithm, sess, realm, username, nonce, nc, cnonce, qop
|
||||
);
|
||||
if (!response || expectedResponse.toLowerCase() !== response.toLowerCase()) {
|
||||
console.log('test3', response, expectedResponse);
|
||||
res.status(401).end();
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(201).end();
|
||||
}
|
||||
|
||||
before(function(done) {
|
||||
|
||||
testApp = express();
|
||||
@@ -317,21 +222,6 @@ describe('HTTP Request Node', function() {
|
||||
}
|
||||
res.json(result);
|
||||
});
|
||||
testApp.get('/authenticate-digest-md5', function(req, res){
|
||||
handleDigestResponse(req, res, "MD5", false, false);
|
||||
});
|
||||
testApp.get('/authenticate-digest-md5-sess', function(req, res){
|
||||
handleDigestResponse(req, res, "MD5", true, 'auth');
|
||||
});
|
||||
testApp.get('/authenticate-digest-md5-qop', function(req, res){
|
||||
handleDigestResponse(req, res, "MD5", false, 'auth');
|
||||
});
|
||||
testApp.get('/authenticate-digest-sha-256', function(req, res){
|
||||
handleDigestResponse(req, res, "SHA-256", false, 'auth');
|
||||
});
|
||||
testApp.get('/authenticate-digest-sha-512-256', function(req, res){
|
||||
handleDigestResponse(req, res, "SHA-512-256", false, 'auth');
|
||||
});
|
||||
testApp.get('/proxyAuthenticate', function(req, res){
|
||||
// var user = auth.parse(req.headers['proxy-authorization']);
|
||||
var result = {
|
||||
@@ -2128,100 +2018,6 @@ describe('HTTP Request Node', function() {
|
||||
});
|
||||
*/
|
||||
|
||||
it('should authenticate on server - digest MD5', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",authType:"digest", url:getTestURL('/authenticate-digest-md5')},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(httpRequestNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n1.credentials = {user:'xxxuser', password:getDigestPassword()};
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('statusCode',201);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:"foo"});
|
||||
});
|
||||
});
|
||||
|
||||
it('should authenticate on server - digest MD5 sess', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",authType:"digest", url:getTestURL('/authenticate-digest-md5-sess')},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(httpRequestNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n1.credentials = {user:'xxxuser', password:getDigestPassword()};
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('statusCode',201);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:"foo"});
|
||||
});
|
||||
});
|
||||
|
||||
it('should authenticate on server - digest MD5 qop', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",authType:"digest", url:getTestURL('/authenticate-digest-md5-qop')},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(httpRequestNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n1.credentials = {user:'xxxuser', password:getDigestPassword()};
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('statusCode',201);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:"foo"});
|
||||
});
|
||||
});
|
||||
|
||||
it('should authenticate on server - digest SHA-256', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",authType:"digest", url:getTestURL('/authenticate-digest-sha-256')},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(httpRequestNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n1.credentials = {user:'xxxuser', password:getDigestPassword()};
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('statusCode',201);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:"foo"});
|
||||
});
|
||||
});
|
||||
|
||||
it('should authenticate on server - digest SHA-512-256', function(done) {
|
||||
var flow = [{id:"n1",type:"http request",wires:[["n2"]],method:"GET",ret:"obj",authType:"digest", url:getTestURL('/authenticate-digest-sha-512-256')},
|
||||
{id:"n2", type:"helper"}];
|
||||
helper.load(httpRequestNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
var n2 = helper.getNode("n2");
|
||||
n1.credentials = {user:'xxxuser', password:getDigestPassword()};
|
||||
n2.on("input", function(msg) {
|
||||
try {
|
||||
msg.should.have.property('statusCode',201);
|
||||
done();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
}
|
||||
});
|
||||
n1.receive({payload:"foo"});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('file-upload', function() {
|
||||
|
@@ -53,7 +53,7 @@ describe('MQTT Nodes', function () {
|
||||
mqttBroker.should.have.property('broker', BROKER_HOST);
|
||||
mqttBroker.should.have.property('port', BROKER_PORT);
|
||||
mqttBroker.should.have.property('brokerurl');
|
||||
mqttBroker.should.have.property('autoUnsubscribe', true); //default: true
|
||||
// mqttBroker.should.have.property('autoUnsubscribe', true);//default: true
|
||||
mqttBroker.should.have.property('autoConnect', false);//Set "autoConnect:false" in brokerOptions
|
||||
mqttBroker.should.have.property('options');
|
||||
mqttBroker.options.should.have.property('clean', true);
|
||||
@@ -96,8 +96,8 @@ describe('MQTT Nodes', function () {
|
||||
mqttBroker.should.have.property('broker', BROKER_HOST);
|
||||
mqttBroker.should.have.property('port', BROKER_PORT);
|
||||
mqttBroker.should.have.property('brokerurl');
|
||||
mqttBroker.should.have.property('autoUnsubscribe', true);
|
||||
mqttBroker.should.have.property('autoConnect', false); //Set "autoConnect:false" in brokerOptions
|
||||
// mqttBroker.should.have.property('autoUnsubscribe', true);//default: true
|
||||
mqttBroker.should.have.property('autoConnect', false);//Set "autoConnect:false" in brokerOptions
|
||||
mqttBroker.should.have.property('options');
|
||||
mqttBroker.options.should.have.property('clean', false);
|
||||
mqttBroker.options.should.have.property('clientId', 'clientid');
|
||||
|
@@ -194,55 +194,6 @@ describe('file Nodes', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('should append to a file and add newline, except last line of multipart input', function(done) {
|
||||
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":true, "overwriteFile":false, wires: [["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper"}];
|
||||
try {
|
||||
fs.unlinkSync(fileToTest);
|
||||
} catch(err) {
|
||||
}
|
||||
helper.load(fileNode, flow, function() {
|
||||
var n1 = helper.getNode("fileNode1");
|
||||
var n2 = helper.getNode("helperNode1");
|
||||
var count = 0;
|
||||
//var data = ["Line1", "Line2"];
|
||||
|
||||
n2.on("input", function (msg) {
|
||||
try {
|
||||
msg.should.have.property("payload");
|
||||
//data.should.containDeep([msg.payload]);
|
||||
if (count === 3) {
|
||||
var f = fs.readFileSync(fileToTest).toString();
|
||||
if (os.type() !== "Windows_NT") {
|
||||
f.should.have.length(23);
|
||||
f.should.equal("Line1\nLine2\nLine3\nLine4");
|
||||
}
|
||||
else {
|
||||
f.should.have.length(23);
|
||||
f.should.equal("Line1\r\nLine2\r\nLine3\r\nLine4");
|
||||
}
|
||||
done();
|
||||
}
|
||||
count++;
|
||||
}
|
||||
catch (e) {
|
||||
done(e);
|
||||
}
|
||||
});
|
||||
|
||||
n1.receive({payload:"Line1",parts:{index:0,type:"string"}}); // string
|
||||
setTimeout(function() {
|
||||
n1.receive({payload:"Line2",parts:{index:1,type:"string"}}); // string
|
||||
},30);
|
||||
setTimeout(function() {
|
||||
n1.receive({payload:"Line3",parts:{index:2,type:"string"}}); // string
|
||||
},60);
|
||||
setTimeout(function() {
|
||||
n1.receive({payload:"Line4",parts:{index:3,type:"string",count:4}}); // string
|
||||
},90);
|
||||
});
|
||||
});
|
||||
|
||||
it('should append to a file after it has been deleted ', function(done) {
|
||||
var flow = [{id:"fileNode1", type:"file", name: "fileNode", "filename":fileToTest, "appendNewline":false, "overwriteFile":false, wires: [["helperNode1"]]},
|
||||
{id:"helperNode1", type:"helper"}];
|
||||
|
@@ -61,14 +61,12 @@ describe("api/editor/index", function() {
|
||||
sinon.stub(NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/"+m),"init").callsFake(function(){});
|
||||
});
|
||||
sinon.stub(NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme"),"app").callsFake(function(){ return express()});
|
||||
sinon.stub(NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/settings"),"sshkeys").callsFake(function(){ return express()});
|
||||
});
|
||||
after(function() {
|
||||
mockList.forEach(function(m) {
|
||||
NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/"+m).init.restore();
|
||||
})
|
||||
NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/theme").app.restore();
|
||||
NR_TEST_UTILS.require("@node-red/editor-api/lib/editor/settings").sshkeys.restore();
|
||||
auth.needsPermission.restore();
|
||||
log.error.restore();
|
||||
});
|
||||
|
@@ -41,7 +41,7 @@ describe("api/editor/settings", function() {
|
||||
});
|
||||
|
||||
it('returns the user settings', function(done) {
|
||||
info.init({}, {
|
||||
info.init({
|
||||
settings: {
|
||||
getUserSettings: function(opts) {
|
||||
if (opts.user !== "fred") {
|
||||
@@ -67,7 +67,7 @@ describe("api/editor/settings", function() {
|
||||
});
|
||||
it('updates the user settings', function(done) {
|
||||
var update;
|
||||
info.init({}, {
|
||||
info.init({
|
||||
settings: {
|
||||
updateUserSettings: function(opts) {
|
||||
if (opts.user !== "fred") {
|
||||
|
@@ -34,7 +34,7 @@ describe("api/editor/sshkeys", function() {
|
||||
}
|
||||
}
|
||||
before(function() {
|
||||
sshkeys.init({}, mockRuntime);
|
||||
sshkeys.init(mockRuntime);
|
||||
app = express();
|
||||
app.use(bodyParser.json());
|
||||
app.use("/settings/user/keys", sshkeys.app());
|
||||
|
Reference in New Issue
Block a user