mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Merge 0.18.5
This commit is contained in:
commit
0c7f4e2168
51
CHANGELOG.md
51
CHANGELOG.md
@ -1,3 +1,54 @@
|
|||||||
|
#### 0.18.5: Maintenance Release
|
||||||
|
|
||||||
|
Projects
|
||||||
|
|
||||||
|
- Add clone project to welcome screen
|
||||||
|
- Handle cloning a project without package.json
|
||||||
|
- Keep remote branch state in sync between editor and runtime
|
||||||
|
|
||||||
|
New Features
|
||||||
|
|
||||||
|
- Add type checks to switch node options (#1714)
|
||||||
|
- add output property select to HTML parse node (#1701)
|
||||||
|
- Add Prevent Following Redirect to HTTP Request node (#615) (#1684)
|
||||||
|
- Add debug and trace functions to function node (#1654)
|
||||||
|
- Enable user defined icon for subflow
|
||||||
|
- Add MQTT disconnect message and rework broker node UI (#1719)
|
||||||
|
- Japanese message catalogue updates (#1723)
|
||||||
|
- Show node load errors in the Palette Manager view
|
||||||
|
|
||||||
|
Editor Fixes
|
||||||
|
|
||||||
|
- Highlight subflow node when log msg comes from inside Fixes #1698
|
||||||
|
- Ensure node wires array is not longer than outputs value Fixes #1678
|
||||||
|
- Allow importing an unknown config node to be undone Fixes #1681
|
||||||
|
- Ensure keyboard shortcuts get saved in runtime settings Fixes #1696
|
||||||
|
- Don't mark a subflow changed when actually modified nothing (#1665)
|
||||||
|
|
||||||
|
Node Fixes
|
||||||
|
|
||||||
|
- bind to correct port when doing udp broadcast/multicast (#1686)
|
||||||
|
- Provide full error stack in Function node log message (#1700)
|
||||||
|
- Fix http request doc type Fixes #1690
|
||||||
|
- Make debug slightly larger to pass WCAG AA rating
|
||||||
|
- Make core nodes labels more consistent, to close #1673
|
||||||
|
- Allow template node to be updated more than once Fixes #1671
|
||||||
|
- Fix the problem that output labels of switch node sometimes disappear (#1664)
|
||||||
|
- Chinese translations for core nodes (#1607)
|
||||||
|
|
||||||
|
Runtime Fixes
|
||||||
|
|
||||||
|
- Handle and display for invalid flow credentials when project is disabled #1689 (#1694)
|
||||||
|
- node-red-pi: fix behavior with old bash version (#1713)
|
||||||
|
- Fix ENOENT error on first start when no user dir (#1711)
|
||||||
|
- Handle null error object in Flow.handleError Fixes #1721
|
||||||
|
- update settings comments to describe how to setup for ipv6 (#1675)
|
||||||
|
- Remove credential props after diffing flow to prevent future false positives Fixes #1359
|
||||||
|
- Log error if settings unavailable when saving user settings Fixes #1645
|
||||||
|
- Keep backup of .config.json
|
||||||
|
- Add warning if using \_credentialSecret from .config.json
|
||||||
|
- Filter req.user in /settings to prevent potentially leaking info
|
||||||
|
|
||||||
#### 0.18.4: Maintenance Release
|
#### 0.18.4: Maintenance Release
|
||||||
|
|
||||||
Projects
|
Projects
|
||||||
|
@ -31,7 +31,7 @@ done
|
|||||||
# Find the real location of this script
|
# Find the real location of this script
|
||||||
CURRENT_PATH=`pwd`
|
CURRENT_PATH=`pwd`
|
||||||
SCRIPT_PATH="${BASH_SOURCE[0]}";
|
SCRIPT_PATH="${BASH_SOURCE[0]}";
|
||||||
while([ -h "${SCRIPT_PATH}" ]); do
|
while [ -h "${SCRIPT_PATH}" ]; do
|
||||||
cd "`dirname "${SCRIPT_PATH}"`"
|
cd "`dirname "${SCRIPT_PATH}"`"
|
||||||
SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
|
SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
|
||||||
done
|
done
|
||||||
|
@ -13,9 +13,10 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
$(function() {
|
|
||||||
if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) {
|
$(function() {
|
||||||
document.title = document.title+" : "+window.location.hostname;
|
if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) {
|
||||||
}
|
document.title = document.title+" : "+window.location.hostname;
|
||||||
RED.init();
|
}
|
||||||
});
|
RED.init();
|
||||||
|
});
|
||||||
|
@ -214,6 +214,18 @@ var RED = (function() {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
} else if (msg.error === "missing_package_file") {
|
||||||
|
if (RED.user.hasPermission("projects.write")) {
|
||||||
|
options.buttons = [
|
||||||
|
{
|
||||||
|
text: "Create default package file",
|
||||||
|
click: function() {
|
||||||
|
persistentNotifications[notificationId].hideNotification();
|
||||||
|
RED.projects.createDefaultPackageFile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
} else if (msg.error === "project_empty") {
|
} else if (msg.error === "project_empty") {
|
||||||
if (RED.user.hasPermission("projects.write")) {
|
if (RED.user.hasPermission("projects.write")) {
|
||||||
options.buttons = [
|
options.buttons = [
|
||||||
|
@ -208,6 +208,8 @@ RED.palette.editor = (function() {
|
|||||||
if (nodeEntry) {
|
if (nodeEntry) {
|
||||||
var activeTypeCount = 0;
|
var activeTypeCount = 0;
|
||||||
var typeCount = 0;
|
var typeCount = 0;
|
||||||
|
var errorCount = 0;
|
||||||
|
nodeEntry.errorList.empty();
|
||||||
nodeEntries[module].totalUseCount = 0;
|
nodeEntries[module].totalUseCount = 0;
|
||||||
nodeEntries[module].setUseCount = {};
|
nodeEntries[module].setUseCount = {};
|
||||||
|
|
||||||
@ -216,7 +218,10 @@ RED.palette.editor = (function() {
|
|||||||
var inUseCount = 0;
|
var inUseCount = 0;
|
||||||
var set = moduleInfo.sets[setName];
|
var set = moduleInfo.sets[setName];
|
||||||
var setElements = nodeEntry.sets[setName];
|
var setElements = nodeEntry.sets[setName];
|
||||||
|
if (set.err) {
|
||||||
|
errorCount++;
|
||||||
|
$("<li>").text(set.err).appendTo(nodeEntry.errorList);
|
||||||
|
}
|
||||||
if (set.enabled) {
|
if (set.enabled) {
|
||||||
activeTypeCount += set.types.length;
|
activeTypeCount += set.types.length;
|
||||||
}
|
}
|
||||||
@ -255,6 +260,13 @@ RED.palette.editor = (function() {
|
|||||||
setElements.setRow.toggleClass("palette-module-set-disabled",!set.enabled);
|
setElements.setRow.toggleClass("palette-module-set-disabled",!set.enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (errorCount === 0) {
|
||||||
|
nodeEntry.errorRow.hide()
|
||||||
|
} else {
|
||||||
|
nodeEntry.errorRow.show();
|
||||||
|
}
|
||||||
|
|
||||||
var nodeCount = (activeTypeCount === typeCount)?typeCount:activeTypeCount+" / "+typeCount;
|
var nodeCount = (activeTypeCount === typeCount)?typeCount:activeTypeCount+" / "+typeCount;
|
||||||
nodeEntry.setCount.html(RED._('palette.editor.nodeCount',{count:typeCount,label:nodeCount}));
|
nodeEntry.setCount.html(RED._('palette.editor.nodeCount',{count:typeCount,label:nodeCount}));
|
||||||
|
|
||||||
@ -586,6 +598,9 @@ RED.palette.editor = (function() {
|
|||||||
$('<span>').html(entry.name).appendTo(titleRow);
|
$('<span>').html(entry.name).appendTo(titleRow);
|
||||||
var metaRow = $('<div class="palette-module-meta palette-module-version"><i class="fa fa-tag"></i></div>').appendTo(headerRow);
|
var metaRow = $('<div class="palette-module-meta palette-module-version"><i class="fa fa-tag"></i></div>').appendTo(headerRow);
|
||||||
var versionSpan = $('<span>').html(entry.version).appendTo(metaRow);
|
var versionSpan = $('<span>').html(entry.version).appendTo(metaRow);
|
||||||
|
|
||||||
|
var errorRow = $('<div class="palette-module-meta palette-module-errors"><i class="fa fa-warning"></i></div>').hide().appendTo(headerRow);
|
||||||
|
var errorList = $('<ul class="palette-module-error-list"></ul>').appendTo(errorRow);
|
||||||
var buttonRow = $('<div>',{class:"palette-module-meta"}).appendTo(headerRow);
|
var buttonRow = $('<div>',{class:"palette-module-meta"}).appendTo(headerRow);
|
||||||
var setButton = $('<a href="#" class="editor-button editor-button-small palette-module-set-button"><i class="fa fa-angle-right palette-module-node-chevron"></i> </a>').appendTo(buttonRow);
|
var setButton = $('<a href="#" class="editor-button editor-button-small palette-module-set-button"><i class="fa fa-angle-right palette-module-node-chevron"></i> </a>').appendTo(buttonRow);
|
||||||
var setCount = $('<span>').appendTo(setButton);
|
var setCount = $('<span>').appendTo(setButton);
|
||||||
@ -620,6 +635,8 @@ RED.palette.editor = (function() {
|
|||||||
updateButton: updateButton,
|
updateButton: updateButton,
|
||||||
removeButton: removeButton,
|
removeButton: removeButton,
|
||||||
enableButton: enableButton,
|
enableButton: enableButton,
|
||||||
|
errorRow: errorRow,
|
||||||
|
errorList: errorList,
|
||||||
setCount: setCount,
|
setCount: setCount,
|
||||||
container: container,
|
container: container,
|
||||||
shade: shade,
|
shade: shade,
|
||||||
@ -651,7 +668,6 @@ RED.palette.editor = (function() {
|
|||||||
typeSwatches[t] = $('<span>',{class:"palette-module-type-swatch"}).appendTo(typeDiv);
|
typeSwatches[t] = $('<span>',{class:"palette-module-type-swatch"}).appendTo(typeDiv);
|
||||||
$('<span>',{class:"palette-module-type-node"}).html(t).appendTo(typeDiv);
|
$('<span>',{class:"palette-module-type-node"}).html(t).appendTo(typeDiv);
|
||||||
})
|
})
|
||||||
|
|
||||||
var enableButton = $('<a href="#" class="editor-button editor-button-small"></a>').appendTo(buttonGroup);
|
var enableButton = $('<a href="#" class="editor-button editor-button-small"></a>').appendTo(buttonGroup);
|
||||||
enableButton.click(function(evt) {
|
enableButton.click(function(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
@ -1255,6 +1255,14 @@ RED.projects.settings = (function() {
|
|||||||
text: 'Delete remote',
|
text: 'Delete remote',
|
||||||
click: function() {
|
click: function() {
|
||||||
notification.close();
|
notification.close();
|
||||||
|
|
||||||
|
if (activeProject.git.branches.remote && activeProject.git.branches.remote.indexOf(entry.name+"/") === 0) {
|
||||||
|
delete activeProject.git.branches.remote;
|
||||||
|
}
|
||||||
|
if (activeProject.git.branches.remoteAlt && activeProject.git.branches.remoteAlt.indexOf(entry.name+"/") === 0) {
|
||||||
|
delete activeProject.git.branches.remoteAlt;
|
||||||
|
}
|
||||||
|
|
||||||
var url = "projects/"+activeProject.name+"/remotes/"+entry.name;
|
var url = "projects/"+activeProject.name+"/remotes/"+entry.name;
|
||||||
var options = {
|
var options = {
|
||||||
url: url,
|
url: url,
|
||||||
@ -1276,6 +1284,7 @@ RED.projects.settings = (function() {
|
|||||||
activeProject.git.remotes[name] = remote;
|
activeProject.git.remotes[name] = remote;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
delete activeProject.git.branches.remoteAlt;
|
||||||
RED.sidebar.versionControl.refresh();
|
RED.sidebar.versionControl.refresh();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -80,6 +80,26 @@ RED.projects = (function() {
|
|||||||
$('<p>').text("To get started you can create your first project or clone an existing project from a git repository.").appendTo(body);
|
$('<p>').text("To get started you can create your first project or clone an existing project from a git repository.").appendTo(body);
|
||||||
$('<p>').text("If you are not sure, you can skip this for now. You will still be able to create your first project from the 'Projects' menu at any time.").appendTo(body);
|
$('<p>').text("If you are not sure, you can skip this for now. You will still be able to create your first project from the 'Projects' menu at any time.").appendTo(body);
|
||||||
|
|
||||||
|
var row = $('<div style="text-align: center"></div>').appendTo(body);
|
||||||
|
var createAsEmpty = $('<button data-type="empty" class="editor-button projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>Create Project</button>').appendTo(row);
|
||||||
|
var createAsClone = $('<button data-type="clone" class="editor-button projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>Clone Repository</button>').appendTo(row);
|
||||||
|
|
||||||
|
createAsEmpty.click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
createProjectOptions = {
|
||||||
|
action: "create"
|
||||||
|
}
|
||||||
|
show('git-config');
|
||||||
|
})
|
||||||
|
|
||||||
|
createAsClone.click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
createProjectOptions = {
|
||||||
|
action: "clone"
|
||||||
|
}
|
||||||
|
show('git-config');
|
||||||
|
})
|
||||||
|
|
||||||
return container;
|
return container;
|
||||||
},
|
},
|
||||||
buttons: [
|
buttons: [
|
||||||
@ -90,13 +110,6 @@ RED.projects = (function() {
|
|||||||
createProjectOptions = {};
|
createProjectOptions = {};
|
||||||
$( this ).dialog( "close" );
|
$( this ).dialog( "close" );
|
||||||
}
|
}
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Create your first project", // TODO: nls
|
|
||||||
class: "primary",
|
|
||||||
click: function() {
|
|
||||||
show('git-config');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -170,7 +183,11 @@ RED.projects = (function() {
|
|||||||
currentGitSettings.user.name = gitUsernameInput.val();
|
currentGitSettings.user.name = gitUsernameInput.val();
|
||||||
currentGitSettings.user.email = gitEmailInput.val();
|
currentGitSettings.user.email = gitEmailInput.val();
|
||||||
RED.settings.set('git', currentGitSettings);
|
RED.settings.set('git', currentGitSettings);
|
||||||
show('project-details');
|
if (createProjectOptions.action === "create") {
|
||||||
|
show('project-details');
|
||||||
|
} else if (createProjectOptions.action === "clone") {
|
||||||
|
show('clone-project');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -303,6 +320,366 @@ RED.projects = (function() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
})(),
|
})(),
|
||||||
|
'clone-project': (function() {
|
||||||
|
var projectNameInput;
|
||||||
|
var projectSummaryInput;
|
||||||
|
var projectFlowFileInput;
|
||||||
|
var projectSecretInput;
|
||||||
|
var projectSecretSelect;
|
||||||
|
var copyProject;
|
||||||
|
var projectRepoInput;
|
||||||
|
var projectCloneSecret;
|
||||||
|
var emptyProjectCredentialInput;
|
||||||
|
var projectRepoUserInput;
|
||||||
|
var projectRepoPasswordInput;
|
||||||
|
var projectNameSublabel;
|
||||||
|
var projectRepoSSHKeySelect;
|
||||||
|
var projectRepoPassphrase;
|
||||||
|
var projectRepoRemoteName
|
||||||
|
var projectRepoBranch;
|
||||||
|
var selectedProject;
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: function(options) {
|
||||||
|
var container = $('<div class="projects-dialog-screen-start"></div>');
|
||||||
|
migrateProjectHeader.appendTo(container);
|
||||||
|
var body = $('<div class="projects-dialog-screen-start-body"></div>').appendTo(container);
|
||||||
|
$('<p>').text("Clone a project").appendTo(body);
|
||||||
|
$('<p>').text("If you already have a git repository containing a project, you can clone it to get started.").appendTo(body);
|
||||||
|
|
||||||
|
var projectList = null;
|
||||||
|
var pendingFormValidation = false;
|
||||||
|
$.getJSON("projects", function(data) {
|
||||||
|
projectList = {};
|
||||||
|
data.projects.forEach(function(p) {
|
||||||
|
projectList[p] = true;
|
||||||
|
if (pendingFormValidation) {
|
||||||
|
pendingFormValidation = false;
|
||||||
|
validateForm();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var validateForm = function() {
|
||||||
|
var projectName = projectNameInput.val();
|
||||||
|
var valid = true;
|
||||||
|
if (projectNameInputChanged) {
|
||||||
|
if (projectList === null) {
|
||||||
|
pendingFormValidation = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
projectNameStatus.empty();
|
||||||
|
if (!/^[a-zA-Z0-9\-_]+$/.test(projectName) || projectList[projectName]) {
|
||||||
|
projectNameInput.addClass("input-error");
|
||||||
|
$('<i style="margin-top: 8px;" class="fa fa-exclamation-triangle"></i>').appendTo(projectNameStatus);
|
||||||
|
projectNameValid = false;
|
||||||
|
valid = false;
|
||||||
|
if (projectList[projectName]) {
|
||||||
|
projectNameSublabel.text("Project already exists");
|
||||||
|
} else {
|
||||||
|
projectNameSublabel.text("Must contain only A-Z 0-9 _ -");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
projectNameInput.removeClass("input-error");
|
||||||
|
$('<i style="margin-top: 8px;" class="fa fa-check"></i>').appendTo(projectNameStatus);
|
||||||
|
projectNameSublabel.text("Must contain only A-Z 0-9 _ -");
|
||||||
|
projectNameValid = true;
|
||||||
|
}
|
||||||
|
projectNameLastChecked = projectName;
|
||||||
|
}
|
||||||
|
valid = projectNameValid;
|
||||||
|
|
||||||
|
var repo = projectRepoInput.val();
|
||||||
|
|
||||||
|
// var validRepo = /^(?:file|git|ssh|https?|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?[\w\.@:\/~_-]+(?:\/?|\#[\d\w\.\-_]+?)$/.test(repo);
|
||||||
|
var validRepo = repo.length > 0 && !/\s/.test(repo);
|
||||||
|
if (/^https?:\/\/[^/]+@/i.test(repo)) {
|
||||||
|
$("#projects-dialog-screen-create-project-repo-label small").text("Do not include the username/password in the url");
|
||||||
|
validRepo = false;
|
||||||
|
}
|
||||||
|
if (!validRepo) {
|
||||||
|
if (projectRepoChanged) {
|
||||||
|
projectRepoInput.addClass("input-error");
|
||||||
|
}
|
||||||
|
valid = false;
|
||||||
|
} else {
|
||||||
|
projectRepoInput.removeClass("input-error");
|
||||||
|
}
|
||||||
|
if (/^https?:\/\//.test(repo)) {
|
||||||
|
$(".projects-dialog-screen-create-row-creds").show();
|
||||||
|
$(".projects-dialog-screen-create-row-sshkey").hide();
|
||||||
|
} else if (/^(?:ssh|[\S]+?@[\S]+?):(?:\/\/)?/.test(repo)) {
|
||||||
|
$(".projects-dialog-screen-create-row-creds").hide();
|
||||||
|
$(".projects-dialog-screen-create-row-sshkey").show();
|
||||||
|
// if ( !getSelectedSSHKey(projectRepoSSHKeySelect) ) {
|
||||||
|
// valid = false;
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
$(".projects-dialog-screen-create-row-creds").hide();
|
||||||
|
$(".projects-dialog-screen-create-row-sshkey").hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#projects-dialog-clone-project").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
var row;
|
||||||
|
|
||||||
|
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-empty projects-dialog-screen-create-row-clone"></div>').appendTo(body);
|
||||||
|
$('<label for="projects-dialog-screen-create-project-name">Project name</label>').appendTo(row);
|
||||||
|
|
||||||
|
var subrow = $('<div style="position:relative;"></div>').appendTo(row);
|
||||||
|
projectNameInput = $('<input id="projects-dialog-screen-create-project-name" type="text"></input>').appendTo(subrow);
|
||||||
|
var projectNameStatus = $('<div class="projects-dialog-screen-input-status"></div>').appendTo(subrow);
|
||||||
|
|
||||||
|
var projectNameInputChanged = false;
|
||||||
|
var projectNameLastChecked = "";
|
||||||
|
var projectNameValid;
|
||||||
|
var checkProjectName;
|
||||||
|
var autoInsertedName = "";
|
||||||
|
|
||||||
|
|
||||||
|
projectNameInput.on("change keyup paste",function() {
|
||||||
|
projectNameInputChanged = (projectNameInput.val() !== projectNameLastChecked);
|
||||||
|
if (checkProjectName) {
|
||||||
|
clearTimeout(checkProjectName);
|
||||||
|
} else if (projectNameInputChanged) {
|
||||||
|
projectNameStatus.empty();
|
||||||
|
$('<img src="red/images/spin.svg"/>').appendTo(projectNameStatus);
|
||||||
|
if (projectNameInput.val() === '') {
|
||||||
|
validateForm();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
checkProjectName = setTimeout(function() {
|
||||||
|
validateForm();
|
||||||
|
checkProjectName = null;
|
||||||
|
},300)
|
||||||
|
});
|
||||||
|
projectNameSublabel = $('<label class="projects-edit-form-sublabel"><small>Must contain only A-Z 0-9 _ -</small></label>').appendTo(row).find("small");
|
||||||
|
|
||||||
|
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-clone"></div>').appendTo(body);
|
||||||
|
$('<label for="projects-dialog-screen-create-project-repo">Git repository URL</label>').appendTo(row);
|
||||||
|
projectRepoInput = $('<input id="projects-dialog-screen-create-project-repo" type="text" placeholder="https://git.example.com/path/my-project.git"></input>').appendTo(row);
|
||||||
|
$('<label id="projects-dialog-screen-create-project-repo-label" class="projects-edit-form-sublabel"><small>https://, ssh:// or file://</small></label>').appendTo(row);
|
||||||
|
var projectRepoChanged = false;
|
||||||
|
var lastProjectRepo = "";
|
||||||
|
projectRepoInput.on("change keyup paste",function() {
|
||||||
|
projectRepoChanged = true;
|
||||||
|
var repo = $(this).val();
|
||||||
|
if (lastProjectRepo !== repo) {
|
||||||
|
$("#projects-dialog-screen-create-project-repo-label small").text("https://, ssh:// or file://");
|
||||||
|
}
|
||||||
|
lastProjectRepo = repo;
|
||||||
|
|
||||||
|
var m = /\/([^/]+?)(?:\.git)?$/.exec(repo);
|
||||||
|
if (m) {
|
||||||
|
var projectName = projectNameInput.val();
|
||||||
|
if (projectName === "" || projectName === autoInsertedName) {
|
||||||
|
autoInsertedName = m[1];
|
||||||
|
projectNameInput.val(autoInsertedName);
|
||||||
|
projectNameInput.change();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
validateForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
var cloneAuthRows = $('<div class="projects-dialog-screen-create-row"></div>').appendTo(body);
|
||||||
|
row = $('<div class="form-row projects-dialog-screen-create-row-auth-error"></div>').hide().appendTo(cloneAuthRows);
|
||||||
|
$('<div><i class="fa fa-warning"></i> Authentication failed</div>').appendTo(row);
|
||||||
|
|
||||||
|
// Repo credentials - username/password ----------------
|
||||||
|
row = $('<div class="hide form-row projects-dialog-screen-create-row-creds"></div>').hide().appendTo(cloneAuthRows);
|
||||||
|
|
||||||
|
var subrow = $('<div style="width: calc(50% - 10px); display:inline-block;"></div>').appendTo(row);
|
||||||
|
$('<label for="projects-dialog-screen-create-project-repo-user">Username</label>').appendTo(subrow);
|
||||||
|
projectRepoUserInput = $('<input id="projects-dialog-screen-create-project-repo-user" type="text"></input>').appendTo(subrow);
|
||||||
|
|
||||||
|
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
|
||||||
|
$('<label for="projects-dialog-screen-create-project-repo-pass">Password</label>').appendTo(subrow);
|
||||||
|
projectRepoPasswordInput = $('<input id="projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
|
||||||
|
// -----------------------------------------------------
|
||||||
|
|
||||||
|
// Repo credentials - key/passphrase -------------------
|
||||||
|
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-sshkey"></div>').hide().appendTo(cloneAuthRows);
|
||||||
|
subrow = $('<div style="width: calc(50% - 10px); display:inline-block;"></div>').appendTo(row);
|
||||||
|
$('<label for="projects-dialog-screen-create-project-repo-passphrase">SSH Key</label>').appendTo(subrow);
|
||||||
|
projectRepoSSHKeySelect = $("<select>",{style:"width: 100%"}).appendTo(subrow);
|
||||||
|
|
||||||
|
$.getJSON("settings/user/keys", function(data) {
|
||||||
|
var count = 0;
|
||||||
|
data.keys.forEach(function(key) {
|
||||||
|
projectRepoSSHKeySelect.append($("<option></option>").val(key.name).text(key.name));
|
||||||
|
count++;
|
||||||
|
});
|
||||||
|
if (count === 0) {
|
||||||
|
projectRepoSSHKeySelect.addClass("input-error");
|
||||||
|
projectRepoSSHKeySelect.attr("disabled",true);
|
||||||
|
sshwarningRow.show();
|
||||||
|
} else {
|
||||||
|
projectRepoSSHKeySelect.removeClass("input-error");
|
||||||
|
projectRepoSSHKeySelect.attr("disabled",false);
|
||||||
|
sshwarningRow.hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
|
||||||
|
$('<label for="projects-dialog-screen-create-project-repo-passphrase">Passphrase</label>').appendTo(subrow);
|
||||||
|
projectRepoPassphrase = $('<input id="projects-dialog-screen-create-project-repo-passphrase" type="password"></input>').appendTo(subrow);
|
||||||
|
|
||||||
|
subrow = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-sshkey"></div>').appendTo(cloneAuthRows);
|
||||||
|
var sshwarningRow = $('<div class="projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
|
||||||
|
$('<div class="form-row"><i class="fa fa-warning"></i> Before you can clone a repository over ssh you must add an SSH key to access it.</div>').appendTo(sshwarningRow);
|
||||||
|
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
|
||||||
|
$('<button class="editor-button">Add an ssh key</button>').appendTo(subrow).click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('#projects-dialog-cancel').click();
|
||||||
|
RED.userSettings.show('gitconfig');
|
||||||
|
setTimeout(function() {
|
||||||
|
$("#user-settings-gitconfig-add-key").click();
|
||||||
|
},500);
|
||||||
|
});
|
||||||
|
// -----------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// Secret - clone
|
||||||
|
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-clone"></div>').appendTo(body);
|
||||||
|
$('<label>Credentials encryption key</label>').appendTo(row);
|
||||||
|
projectSecretInput = $('<input type="password"></input>').appendTo(row);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return container;
|
||||||
|
},
|
||||||
|
buttons: function(options) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
text: "Back",
|
||||||
|
click: function() {
|
||||||
|
show('git-config');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "projects-dialog-clone-project",
|
||||||
|
disabled: true,
|
||||||
|
text: "Clone project", // TODO: nls
|
||||||
|
class: "primary disabled",
|
||||||
|
click: function() {
|
||||||
|
var projectType = $(".projects-dialog-screen-create-type.selected").data('type');
|
||||||
|
var projectData = {
|
||||||
|
name: projectNameInput.val(),
|
||||||
|
}
|
||||||
|
projectData.credentialSecret = projectSecretInput.val();
|
||||||
|
var repoUrl = projectRepoInput.val();
|
||||||
|
var metaData = {};
|
||||||
|
if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(repoUrl)) {
|
||||||
|
var selected = projectRepoSSHKeySelect.val();//false;//getSelectedSSHKey(projectRepoSSHKeySelect);
|
||||||
|
if ( selected ) {
|
||||||
|
projectData.git = {
|
||||||
|
remotes: {
|
||||||
|
'origin': {
|
||||||
|
url: repoUrl,
|
||||||
|
keyFile: selected,
|
||||||
|
passphrase: projectRepoPassphrase.val()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Error! Can't get selected SSH key path.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
projectData.git = {
|
||||||
|
remotes: {
|
||||||
|
'origin': {
|
||||||
|
url: repoUrl,
|
||||||
|
username: projectRepoUserInput.val(),
|
||||||
|
password: projectRepoPasswordInput.val()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
$(".projects-dialog-screen-create-row-auth-error").hide();
|
||||||
|
$("#projects-dialog-screen-create-project-repo-label small").text("https://, ssh:// or file://");
|
||||||
|
|
||||||
|
projectRepoUserInput.removeClass("input-error");
|
||||||
|
projectRepoPasswordInput.removeClass("input-error");
|
||||||
|
projectRepoSSHKeySelect.removeClass("input-error");
|
||||||
|
projectRepoPassphrase.removeClass("input-error");
|
||||||
|
|
||||||
|
RED.deploy.setDeployInflight(true);
|
||||||
|
RED.projects.settings.switchProject(projectData.name);
|
||||||
|
|
||||||
|
sendRequest({
|
||||||
|
url: "projects",
|
||||||
|
type: "POST",
|
||||||
|
handleAuthFail: false,
|
||||||
|
responses: {
|
||||||
|
200: function(data) {
|
||||||
|
dialog.dialog( "close" );
|
||||||
|
},
|
||||||
|
400: {
|
||||||
|
'project_exists': function(error) {
|
||||||
|
console.log("already exists");
|
||||||
|
},
|
||||||
|
'git_error': function(error) {
|
||||||
|
console.log("git error",error);
|
||||||
|
},
|
||||||
|
'git_connection_failed': function(error) {
|
||||||
|
projectRepoInput.addClass("input-error");
|
||||||
|
$("#projects-dialog-screen-create-project-repo-label small").text("Connection failed");
|
||||||
|
},
|
||||||
|
'git_not_a_repository': function(error) {
|
||||||
|
projectRepoInput.addClass("input-error");
|
||||||
|
$("#projects-dialog-screen-create-project-repo-label small").text("Not a git repository");
|
||||||
|
},
|
||||||
|
'git_repository_not_found': function(error) {
|
||||||
|
projectRepoInput.addClass("input-error");
|
||||||
|
$("#projects-dialog-screen-create-project-repo-label small").text("Repository not found");
|
||||||
|
},
|
||||||
|
'git_auth_failed': function(error) {
|
||||||
|
$(".projects-dialog-screen-create-row-auth-error").show();
|
||||||
|
|
||||||
|
projectRepoUserInput.addClass("input-error");
|
||||||
|
projectRepoPasswordInput.addClass("input-error");
|
||||||
|
// getRepoAuthDetails(req);
|
||||||
|
projectRepoSSHKeySelect.addClass("input-error");
|
||||||
|
projectRepoPassphrase.addClass("input-error");
|
||||||
|
},
|
||||||
|
'missing_flow_file': function(error) {
|
||||||
|
// This is handled via a runtime notification.
|
||||||
|
dialog.dialog("close");
|
||||||
|
},
|
||||||
|
'project_empty': function(error) {
|
||||||
|
// This is handled via a runtime notification.
|
||||||
|
dialog.dialog("close");
|
||||||
|
},
|
||||||
|
'credentials_load_failed': function(error) {
|
||||||
|
// This is handled via a runtime notification.
|
||||||
|
dialog.dialog("close");
|
||||||
|
},
|
||||||
|
'*': function(error) {
|
||||||
|
reportUnexpectedError(error);
|
||||||
|
$( dialog ).dialog( "close" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},projectData).then(function() {
|
||||||
|
RED.events.emit("project:change", {name:name});
|
||||||
|
}).always(function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
RED.deploy.setDeployInflight(false);
|
||||||
|
},500);
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(),
|
||||||
'default-files': (function() {
|
'default-files': (function() {
|
||||||
var projectFlowFileInput;
|
var projectFlowFileInput;
|
||||||
var projectCredentialFileInput;
|
var projectCredentialFileInput;
|
||||||
@ -1127,6 +1504,7 @@ RED.projects = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$(".projects-dialog-screen-create-row-auth-error").hide();
|
$(".projects-dialog-screen-create-row-auth-error").hide();
|
||||||
|
$("#projects-dialog-screen-create-project-repo-label small").text("https://, ssh:// or file://");
|
||||||
|
|
||||||
projectRepoUserInput.removeClass("input-error");
|
projectRepoUserInput.removeClass("input-error");
|
||||||
projectRepoPasswordInput.removeClass("input-error");
|
projectRepoPasswordInput.removeClass("input-error");
|
||||||
@ -1873,6 +2251,43 @@ RED.projects = (function() {
|
|||||||
createProjectOptions = {};
|
createProjectOptions = {};
|
||||||
show('default-files',{existingProject: true});
|
show('default-files',{existingProject: true});
|
||||||
}
|
}
|
||||||
|
function createDefaultPackageFile() {
|
||||||
|
RED.deploy.setDeployInflight(true);
|
||||||
|
RED.projects.settings.switchProject(activeProject.name);
|
||||||
|
|
||||||
|
var method = "PUT";
|
||||||
|
var url = "projects/"+activeProject.name;
|
||||||
|
var createProjectOptions = {
|
||||||
|
initialise: true
|
||||||
|
};
|
||||||
|
sendRequest({
|
||||||
|
url: url,
|
||||||
|
type: method,
|
||||||
|
requireCleanWorkspace: true,
|
||||||
|
handleAuthFail: false,
|
||||||
|
responses: {
|
||||||
|
200: function(data) { },
|
||||||
|
400: {
|
||||||
|
'git_error': function(error) {
|
||||||
|
console.log("git error",error);
|
||||||
|
},
|
||||||
|
'missing_flow_file': function(error) {
|
||||||
|
// This is a natural next error - but let the runtime event
|
||||||
|
// trigger the dialog rather than double-report it.
|
||||||
|
$( dialog ).dialog( "close" );
|
||||||
|
},
|
||||||
|
'*': function(error) {
|
||||||
|
reportUnexpectedError(error);
|
||||||
|
$( dialog ).dialog( "close" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},createProjectOptions).always(function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
RED.deploy.setDeployInflight(false);
|
||||||
|
},500);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function refresh(done) {
|
function refresh(done) {
|
||||||
$.getJSON("projects",function(data) {
|
$.getJSON("projects",function(data) {
|
||||||
@ -1952,6 +2367,7 @@ RED.projects = (function() {
|
|||||||
RED.projects.settings.show('deps');
|
RED.projects.settings.show('deps');
|
||||||
},
|
},
|
||||||
createDefaultFileSet: createDefaultFileSet,
|
createDefaultFileSet: createDefaultFileSet,
|
||||||
|
createDefaultPackageFile: createDefaultPackageFile,
|
||||||
// showSidebar: showSidebar,
|
// showSidebar: showSidebar,
|
||||||
refresh: refresh,
|
refresh: refresh,
|
||||||
editProject: function() {
|
editProject: function() {
|
||||||
|
@ -592,7 +592,10 @@ RED.sidebar.versionControl = (function() {
|
|||||||
closeBranchBox();
|
closeBranchBox();
|
||||||
localCommitListShade.show();
|
localCommitListShade.show();
|
||||||
$(this).addClass('selected');
|
$(this).addClass('selected');
|
||||||
|
var activeProject = RED.projects.getActiveProject();
|
||||||
|
$("#sidebar-version-control-repo-toolbar-set-upstream-row").toggle(!!activeProject.git.branches.remoteAlt);
|
||||||
remoteBox.show();
|
remoteBox.show();
|
||||||
|
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
remoteBox.css("height","265px");
|
remoteBox.css("height","265px");
|
||||||
},100);
|
},100);
|
||||||
@ -868,7 +871,8 @@ RED.sidebar.versionControl = (function() {
|
|||||||
if (activeProject.git.branches.remoteAlt) {
|
if (activeProject.git.branches.remoteAlt) {
|
||||||
url+="/"+activeProject.git.branches.remoteAlt;
|
url+="/"+activeProject.git.branches.remoteAlt;
|
||||||
}
|
}
|
||||||
if ($("#sidebar-version-control-repo-toolbar-set-upstream").prop('checked')) {
|
var setUpstream = $("#sidebar-version-control-repo-toolbar-set-upstream").prop('checked');
|
||||||
|
if (setUpstream) {
|
||||||
url+="?u=true"
|
url+="?u=true"
|
||||||
}
|
}
|
||||||
utils.sendRequest({
|
utils.sendRequest({
|
||||||
@ -880,6 +884,10 @@ RED.sidebar.versionControl = (function() {
|
|||||||
// done(error,null);
|
// done(error,null);
|
||||||
},
|
},
|
||||||
200: function(data) {
|
200: function(data) {
|
||||||
|
if (setUpstream && activeProject.git.branches.remoteAlt) {
|
||||||
|
activeProject.git.branches.remote = activeProject.git.branches.remoteAlt;
|
||||||
|
delete activeProject.git.branches.remoteAlt;
|
||||||
|
}
|
||||||
refresh(true);
|
refresh(true);
|
||||||
closeRemoteBox();
|
closeRemoteBox();
|
||||||
},
|
},
|
||||||
@ -928,6 +936,10 @@ RED.sidebar.versionControl = (function() {
|
|||||||
// done(error,null);
|
// done(error,null);
|
||||||
},
|
},
|
||||||
200: function(data) {
|
200: function(data) {
|
||||||
|
if (options.setUpstream && activeProject.git.branches.remoteAlt) {
|
||||||
|
activeProject.git.branches.remote = activeProject.git.branches.remoteAlt;
|
||||||
|
delete activeProject.git.branches.remoteAlt;
|
||||||
|
}
|
||||||
refresh(true);
|
refresh(true);
|
||||||
closeRemoteBox();
|
closeRemoteBox();
|
||||||
},
|
},
|
||||||
|
@ -49,7 +49,12 @@
|
|||||||
.palette-module-version {
|
.palette-module-version {
|
||||||
color: #aaa;
|
color: #aaa;
|
||||||
}
|
}
|
||||||
|
.palette-module-errors .fa-warning {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
ul.palette-module-error-list li {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -222,6 +227,20 @@
|
|||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.palette-module-meta .fa-warning {
|
||||||
|
color: #AD1625;
|
||||||
|
}
|
||||||
|
ul.palette-module-error-list {
|
||||||
|
display: inline-block;
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 0.9em;
|
||||||
|
li {
|
||||||
|
border: none;
|
||||||
|
background: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.palette-module-shade {
|
.palette-module-shade {
|
||||||
@include shade;
|
@include shade;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -139,16 +139,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
button.editor-button {
|
button.editor-button {
|
||||||
width: calc(50% - 40px);
|
width: calc(50% - 80px);
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
height: 175px;
|
height: auto;
|
||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
font-size: 1.5em !important;
|
padding: 10px;
|
||||||
|
border-color: #aaa;
|
||||||
i {
|
i {
|
||||||
color: #ccc;
|
color: #aaa;
|
||||||
}
|
}
|
||||||
&:hover i {
|
&:hover i {
|
||||||
color: #aaa;
|
color: #999;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.button-group {
|
.button-group {
|
||||||
@ -160,7 +161,6 @@
|
|||||||
button.projects-dialog-screen-create-type {
|
button.projects-dialog-screen-create-type {
|
||||||
height: auto;
|
height: auto;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
|
||||||
}
|
}
|
||||||
.button-group {
|
.button-group {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
@ -204,7 +204,14 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
var context = vm.createContext(sandbox);
|
var context = vm.createContext(sandbox);
|
||||||
try {
|
try {
|
||||||
this.script = vm.createScript(functionText);
|
this.script = vm.createScript(functionText, {
|
||||||
|
filename: 'Function node:'+this.id+(this.name?' ['+this.name+']':''), // filename for stack traces
|
||||||
|
displayErrors: true
|
||||||
|
// Using the following options causes node 4/6 to not include the line number
|
||||||
|
// in the stack output. So don't use them.
|
||||||
|
// lineOffset: -11, // line number offset to be used for stack traces
|
||||||
|
// columnOffset: 0, // column number offset to be used for stack traces
|
||||||
|
});
|
||||||
this.on("input", function(msg) {
|
this.on("input", function(msg) {
|
||||||
try {
|
try {
|
||||||
var start = process.hrtime();
|
var start = process.hrtime();
|
||||||
@ -219,6 +226,13 @@ module.exports = function(RED) {
|
|||||||
this.status({fill:"yellow",shape:"dot",text:""+converted});
|
this.status({fill:"yellow",shape:"dot",text:""+converted});
|
||||||
}
|
}
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
//remove unwanted part
|
||||||
|
var index = err.stack.search(/\n\s*at ContextifyScript.Script.runInContext/);
|
||||||
|
err.stack = err.stack.slice(0, index).split('\n').slice(0,-1).join('\n');
|
||||||
|
var stack = err.stack.split(/\r?\n/);
|
||||||
|
|
||||||
|
//store the error in msg to be used in flows
|
||||||
|
msg.error = err;
|
||||||
|
|
||||||
var line = 0;
|
var line = 0;
|
||||||
var errorMessage;
|
var errorMessage;
|
||||||
|
@ -212,48 +212,84 @@
|
|||||||
<input type="password" id="node-config-input-password">
|
<input type="password" id="node-config-input-password">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="mqtt-broker-tab-birth" style="display:none">
|
<div id="mqtt-broker-tab-messages" style="display:none">
|
||||||
<div class="form-row">
|
<div id="mqtt-broker-section-birth">
|
||||||
<label for="node-config-input-birthTopic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
|
<div class="palette-header">
|
||||||
<input type="text" id="node-config-input-birthTopic" data-i18n="[placeholder]mqtt.placeholder.birth-topic">
|
<i class="fa fa-angle-down"></i><span data-i18n="mqtt.sections-label.birth-message"></span>
|
||||||
|
</div>
|
||||||
|
<div class="section-content" style="padding:10px 0 0 10px">
|
||||||
|
<div class="form-row">
|
||||||
|
<label style="width: 100px !important;" for="node-config-input-birthTopic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
|
||||||
|
<input style="width: calc(100% - 300px) !important" type="text" id="node-config-input-birthTopic" data-i18n="[placeholder]mqtt.placeholder.birth-topic">
|
||||||
|
<label style="margin-left: 10px; width: 90px !important;" for="node-config-input-birthRetain"><i class="fa fa-history"></i> <span data-i18n="mqtt.label.retain"></span></label>
|
||||||
|
<select id="node-config-input-birthRetain" style="width:75px !important">
|
||||||
|
<option value="false" data-i18n="mqtt.false"></option>
|
||||||
|
<option value="true" data-i18n="mqtt.true"></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label style="width: 100px !important;" for="node-config-input-birthPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
|
||||||
|
<input style="width: calc(100% - 300px) !important" type="text" id="node-config-input-birthPayload" style="width:300px" data-i18n="[placeholder]common.label.payload">
|
||||||
|
<label style="margin-left: 10px; width: 90px !important;" for="node-config-input-birthQos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
|
||||||
|
<select id="node-config-input-birthQos" style="width:75px !important">
|
||||||
|
<option value="0">0</option>
|
||||||
|
<option value="1">1</option>
|
||||||
|
<option value="2">2</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div id="mqtt-broker-section-close">
|
||||||
<label for="node-config-input-birthQos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
|
<div class="palette-header">
|
||||||
<select id="node-config-input-birthQos" style="width:125px !important">
|
<i class="fa fa-angle-down"></i><span data-i18n="mqtt.sections-label.close-message"></span>
|
||||||
<option value="0">0</option>
|
</div>
|
||||||
<option value="1">1</option>
|
<div class="section-content" style="padding:10px 0 0 10px">
|
||||||
<option value="2">2</option>
|
<div class="form-row">
|
||||||
</select>
|
<label style="width: 100px !important;" for="node-config-input-closeTopic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
|
||||||
<i class="fa fa-history"></i> <span data-i18n="mqtt.retain"></span> <select id="node-config-input-birthRetain" style="width:125px !important">
|
<input style="width: calc(100% - 300px) !important" type="text" id="node-config-input-closeTopic" style="width:300px" data-i18n="[placeholder]mqtt.placeholder.close-topic">
|
||||||
<option value="false" data-i18n="mqtt.false"></option>
|
<label style="margin-left: 10px; width: 90px !important;" for="node-config-input-closeRetain"><i class="fa fa-history"></i> <span data-i18n="mqtt.label.retain"></span></label>
|
||||||
<option value="true" data-i18n="mqtt.true"></option>
|
<select id="node-config-input-closeRetain" style="width:75px !important">
|
||||||
</select>
|
<option value="false" data-i18n="mqtt.false"></option>
|
||||||
|
<option value="true" data-i18n="mqtt.true"></option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label style="width: 100px !important;" for="node-config-input-closePayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
|
||||||
|
<input style="width: calc(100% - 300px) !important" type="text" id="node-config-input-closePayload" style="width:300px" data-i18n="[placeholder]common.label.payload">
|
||||||
|
<label style="margin-left: 10px; width: 90px !important;" for="node-config-input-closeQos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
|
||||||
|
<select id="node-config-input-closeQos" style="width:75px !important">
|
||||||
|
<option value="0">0</option>
|
||||||
|
<option value="1">1</option>
|
||||||
|
<option value="2">2</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div id="mqtt-broker-section-will">
|
||||||
<label for="node-config-input-birthPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
|
<div class="palette-header">
|
||||||
<input type="text" id="node-config-input-birthPayload" data-i18n="[placeholder]common.label.payload">
|
<i class="fa fa-angle-down"></i><span data-i18n="mqtt.sections-label.will-message"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div class="section-content" style="padding:10px 0 0 10px">
|
||||||
<div id="mqtt-broker-tab-will" style="display:none">
|
<div class="form-row">
|
||||||
<div class="form-row">
|
<label style="width: 100px !important;" for="node-config-input-willTopic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
|
||||||
<label for="node-config-input-willTopic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
|
<input style="width: calc(100% - 300px) !important" type="text" id="node-config-input-willTopic" style="width:300px" data-i18n="[placeholder]mqtt.placeholder.will-topic">
|
||||||
<input type="text" id="node-config-input-willTopic" data-i18n="[placeholder]mqtt.placeholder.will-topic">
|
<label style="margin-left: 10px; width: 90px !important;" for="node-config-input-willRetain"><i class="fa fa-history"></i> <span data-i18n="mqtt.label.retain"></span></label>
|
||||||
</div>
|
<select id="node-config-input-willRetain" style="width:75px !important">
|
||||||
<div class="form-row">
|
<option value="false" data-i18n="mqtt.false"></option>
|
||||||
<label for="node-config-input-willQos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
|
<option value="true" data-i18n="mqtt.true"></option>
|
||||||
<select id="node-config-input-willQos" style="width:125px !important">
|
</select>
|
||||||
<option value="0">0</option>
|
</div>
|
||||||
<option value="1">1</option>
|
<div class="form-row">
|
||||||
<option value="2">2</option>
|
<label style="width: 100px !important;" for="node-config-input-willPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
|
||||||
</select>
|
<input style="width: calc(100% - 300px) !important" type="text" id="node-config-input-willPayload" style="width:300px" data-i18n="[placeholder]common.label.payload">
|
||||||
<i class="fa fa-history"></i> <span data-i18n="mqtt.retain"></span> <select id="node-config-input-willRetain" style="width:125px !important">
|
<label style="margin-left: 10px; width: 90px !important;" for="node-config-input-willQos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
|
||||||
<option value="false" data-i18n="mqtt.false"></option>
|
<select id="node-config-input-willQos" style="width:75px !important">
|
||||||
<option value="true" data-i18n="mqtt.true"></option>
|
<option value="0">0</option>
|
||||||
</select>
|
<option value="1">1</option>
|
||||||
</div>
|
<option value="2">2</option>
|
||||||
<div class="form-row">
|
</select>
|
||||||
<label for="node-config-input-willPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
|
</div>
|
||||||
<input type="text" id="node-config-input-willPayload" data-i18n="[placeholder]common.label.payload">
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -269,6 +305,9 @@
|
|||||||
<h4>Birth Message</h4>
|
<h4>Birth Message</h4>
|
||||||
<p>This is a message that will be published on the configured topic whenever the
|
<p>This is a message that will be published on the configured topic whenever the
|
||||||
connection is established.</p>
|
connection is established.</p>
|
||||||
|
<h4>Close Message</h4>
|
||||||
|
<p>This is a message that will be published on the configured topic before the
|
||||||
|
connection is closed normally, either by re-deploying the node, or by shutting down.</p>
|
||||||
<h4>Will Message</h4>
|
<h4>Will Message</h4>
|
||||||
<p>This is a message that will be published by the broker in the event the node
|
<p>This is a message that will be published by the broker in the event the node
|
||||||
unexpectedly loses its connection.</p>
|
unexpectedly loses its connection.</p>
|
||||||
@ -300,14 +339,18 @@
|
|||||||
compatmode: { value: true},
|
compatmode: { value: true},
|
||||||
keepalive: {value:60,validate:RED.validators.number()},
|
keepalive: {value:60,validate:RED.validators.number()},
|
||||||
cleansession: {value: true},
|
cleansession: {value: true},
|
||||||
willTopic: {value:""},
|
|
||||||
willQos: {value:"0"},
|
|
||||||
willRetain: {value:false},
|
|
||||||
willPayload: {value:""},
|
|
||||||
birthTopic: {value:""},
|
birthTopic: {value:""},
|
||||||
birthQos: {value:"0"},
|
birthQos: {value:"0"},
|
||||||
birthRetain: {value:false},
|
birthRetain: {value:false},
|
||||||
birthPayload: {value:""}
|
birthPayload: {value:""},
|
||||||
|
closeTopic: {value:""},
|
||||||
|
closeQos: {value:"0"},
|
||||||
|
closeRetain: {value:false},
|
||||||
|
closePayload: {value:""},
|
||||||
|
willTopic: {value:""},
|
||||||
|
willQos: {value:"0"},
|
||||||
|
willRetain: {value:false},
|
||||||
|
willPayload: {value:""}
|
||||||
},
|
},
|
||||||
credentials: {
|
credentials: {
|
||||||
user: {type:"text"},
|
user: {type:"text"},
|
||||||
@ -343,14 +386,39 @@
|
|||||||
id: "mqtt-broker-tab-security",
|
id: "mqtt-broker-tab-security",
|
||||||
label: this._("mqtt.tabs-label.security")
|
label: this._("mqtt.tabs-label.security")
|
||||||
});
|
});
|
||||||
|
|
||||||
tabs.addTab({
|
tabs.addTab({
|
||||||
id: "mqtt-broker-tab-birth",
|
id: "mqtt-broker-tab-messages",
|
||||||
label: this._("mqtt.tabs-label.birth")
|
label: this._("mqtt.tabs-label.messages")
|
||||||
});
|
|
||||||
tabs.addTab({
|
|
||||||
id: "mqtt-broker-tab-will",
|
|
||||||
label: this._("mqtt.tabs-label.will")
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function setUpSection(sectionId, isExpanded) {
|
||||||
|
var birthMessageSection = $(sectionId);
|
||||||
|
var paletteHeader = birthMessageSection.find('.palette-header');
|
||||||
|
var twistie = paletteHeader.find('i');
|
||||||
|
var sectionContent = birthMessageSection.find('.section-content');
|
||||||
|
|
||||||
|
function toggleSection(expanded) {
|
||||||
|
twistie.toggleClass('expanded', expanded);
|
||||||
|
sectionContent.toggle(expanded);
|
||||||
|
}
|
||||||
|
paletteHeader.click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var isExpanded = twistie.hasClass('expanded');
|
||||||
|
toggleSection(!isExpanded);
|
||||||
|
});
|
||||||
|
toggleSection(isExpanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
// show first section if none are set so the user gets the idea
|
||||||
|
var showBirthSection = this.birthTopic !== ""
|
||||||
|
|| this.willTopic === ""
|
||||||
|
&& this.birthTopic === ""
|
||||||
|
&& this.closeTopic == "";
|
||||||
|
setUpSection('#mqtt-broker-section-birth', showBirthSection);
|
||||||
|
setUpSection('#mqtt-broker-section-close', this.closeTopic !== "");
|
||||||
|
setUpSection('#mqtt-broker-section-will', this.willTopic !== "");
|
||||||
|
|
||||||
setTimeout(function() { tabs.resize(); },0);
|
setTimeout(function() { tabs.resize(); },0);
|
||||||
if (typeof this.cleansession === 'undefined') {
|
if (typeof this.cleansession === 'undefined') {
|
||||||
this.cleansession = true;
|
this.cleansession = true;
|
||||||
@ -368,14 +436,18 @@
|
|||||||
this.keepalive = 15;
|
this.keepalive = 15;
|
||||||
$("#node-config-input-keepalive").val(this.keepalive);
|
$("#node-config-input-keepalive").val(this.keepalive);
|
||||||
}
|
}
|
||||||
if (typeof this.willQos === 'undefined') {
|
|
||||||
this.willQos = "0";
|
|
||||||
$("#node-config-input-willQos").val("0");
|
|
||||||
}
|
|
||||||
if (typeof this.birthQos === 'undefined') {
|
if (typeof this.birthQos === 'undefined') {
|
||||||
this.birthQos = "0";
|
this.birthQos = "0";
|
||||||
$("#node-config-input-birthQos").val("0");
|
$("#node-config-input-birthQos").val("0");
|
||||||
}
|
}
|
||||||
|
if (typeof this.closeQos === 'undefined') {
|
||||||
|
this.willQos = "0";
|
||||||
|
$("#node-config-input-willQos").val("0");
|
||||||
|
}
|
||||||
|
if (typeof this.willQos === 'undefined') {
|
||||||
|
this.willQos = "0";
|
||||||
|
$("#node-config-input-willQos").val("0");
|
||||||
|
}
|
||||||
|
|
||||||
function updateTLSOptions() {
|
function updateTLSOptions() {
|
||||||
if ($("#node-config-input-usetls").is(':checked')) {
|
if ($("#node-config-input-usetls").is(':checked')) {
|
||||||
|
@ -60,6 +60,15 @@ module.exports = function(RED) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (n.closeTopic) {
|
||||||
|
this.closeMessage = {
|
||||||
|
topic: n.closeTopic,
|
||||||
|
payload: n.closePayload || "",
|
||||||
|
qos: Number(n.closeQos||0),
|
||||||
|
retain: n.closeRetain=="true"|| n.closeRetain===true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (this.credentials) {
|
if (this.credentials) {
|
||||||
this.username = this.credentials.user;
|
this.username = this.credentials.user;
|
||||||
this.password = this.credentials.password;
|
this.password = this.credentials.password;
|
||||||
@ -314,6 +323,10 @@ module.exports = function(RED) {
|
|||||||
this.on('close', function(done) {
|
this.on('close', function(done) {
|
||||||
this.closing = true;
|
this.closing = true;
|
||||||
if (this.connected) {
|
if (this.connected) {
|
||||||
|
// Send close message
|
||||||
|
if (node.closeMessage) {
|
||||||
|
node.publish(node.closeMessage);
|
||||||
|
}
|
||||||
this.client.once('close', function() {
|
this.client.once('close', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-row node-input-iface">
|
<div class="form-row node-input-iface">
|
||||||
<label for="node-input-iface"><i class="fa fa-random"></i> <span data-i18n="udp.label.interface"></span></label>
|
<label for="node-input-iface"><i class="fa fa-random"></i> <span data-i18n="udp.label.interface"></span></label>
|
||||||
<input type="text" id="node-input-iface" data-i18n="[placeholder]udp.label.interfaceprompt">
|
<input type="text" id="node-input-iface" data-i18n="[placeholder]udp.placeholder.interfaceprompt">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-port"><i class="fa fa-sign-in"></i> <span data-i18n="udp.label.onport"></span></label>
|
<label for="node-input-port"><i class="fa fa-sign-in"></i> <span data-i18n="udp.label.onport"></span></label>
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
var os = require('os');
|
||||||
var dgram = require('dgram');
|
var dgram = require('dgram');
|
||||||
var udpInputPortsInUse = {};
|
var udpInputPortsInUse = {};
|
||||||
|
|
||||||
@ -30,6 +31,29 @@ module.exports = function(RED) {
|
|||||||
this.ipv = n.ipv || "udp4";
|
this.ipv = n.ipv || "udp4";
|
||||||
var node = this;
|
var node = this;
|
||||||
|
|
||||||
|
if (node.iface && node.iface.indexOf(".") === -1) {
|
||||||
|
try {
|
||||||
|
if ((os.networkInterfaces())[node.iface][0].hasOwnProperty("scopeid")) {
|
||||||
|
if (node.ipv === "udp4") {
|
||||||
|
node.iface = (os.networkInterfaces())[node.iface][1].address;
|
||||||
|
} else {
|
||||||
|
node.iface = (os.networkInterfaces())[node.iface][0].address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (node.ipv === "udp4") {
|
||||||
|
node.iface = (os.networkInterfaces())[node.iface][0].address;
|
||||||
|
} else {
|
||||||
|
node.iface = (os.networkInterfaces())[node.iface][1].address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
node.warn(RED._("udp.errors.ifnotfound",{iface:node.iface}));
|
||||||
|
node.iface = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var opts = {type:node.ipv, reuseAddr:true};
|
var opts = {type:node.ipv, reuseAddr:true};
|
||||||
if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
|
if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
|
||||||
var server;
|
var server;
|
||||||
@ -39,7 +63,7 @@ module.exports = function(RED) {
|
|||||||
udpInputPortsInUse[this.port] = server;
|
udpInputPortsInUse[this.port] = server;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
node.warn(RED._("udp.errors.alreadyused",node.port));
|
node.warn(RED._("udp.errors.alreadyused",{port:node.port}));
|
||||||
server = udpInputPortsInUse[this.port]; // re-use existing
|
server = udpInputPortsInUse[this.port]; // re-use existing
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,12 +145,38 @@ module.exports = function(RED) {
|
|||||||
this.ipv = n.ipv || "udp4";
|
this.ipv = n.ipv || "udp4";
|
||||||
var node = this;
|
var node = this;
|
||||||
|
|
||||||
|
if (node.iface && node.iface.indexOf(".") === -1) {
|
||||||
|
try {
|
||||||
|
if ((os.networkInterfaces())[node.iface][0].hasOwnProperty("scopeid")) {
|
||||||
|
if (node.ipv === "udp4") {
|
||||||
|
node.iface = (os.networkInterfaces())[node.iface][1].address;
|
||||||
|
} else {
|
||||||
|
node.iface = (os.networkInterfaces())[node.iface][0].address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (node.ipv === "udp4") {
|
||||||
|
node.iface = (os.networkInterfaces())[node.iface][0].address;
|
||||||
|
} else {
|
||||||
|
node.iface = (os.networkInterfaces())[node.iface][1].address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(e) {
|
||||||
|
node.warn(RED._("udp.errors.ifnotfound",{iface:node.iface}));
|
||||||
|
node.iface = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var opts = {type:node.ipv, reuseAddr:true};
|
var opts = {type:node.ipv, reuseAddr:true};
|
||||||
if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
|
if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
|
||||||
|
|
||||||
var sock;
|
var sock;
|
||||||
if (udpInputPortsInUse[this.outport || this.port]) {
|
var p = this.port;
|
||||||
sock = udpInputPortsInUse[this.outport || this.port];
|
if (node.multicast != "false") { p = this.outport||"0"; }
|
||||||
|
if (udpInputPortsInUse[p]) {
|
||||||
|
sock = udpInputPortsInUse[p];
|
||||||
|
node.log(RED._("udp.status.re-use",{outport:node.outport,host:node.addr,port:node.port}));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sock = dgram.createSocket(opts); // default to udp4
|
sock = dgram.createSocket(opts); // default to udp4
|
||||||
@ -136,36 +186,35 @@ module.exports = function(RED) {
|
|||||||
// prevent it going to the global error handler and shutting node-red
|
// prevent it going to the global error handler and shutting node-red
|
||||||
// down.
|
// down.
|
||||||
});
|
});
|
||||||
udpInputPortsInUse[this.outport || this.port] = sock;
|
udpInputPortsInUse[p] = sock;
|
||||||
}
|
|
||||||
|
|
||||||
if (node.multicast != "false") {
|
if (node.multicast != "false") {
|
||||||
if (node.outport === "") { node.outport = node.port; }
|
sock.bind(node.outport, function() { // have to bind before you can enable broadcast...
|
||||||
sock.bind(node.outport, function() { // have to bind before you can enable broadcast...
|
sock.setBroadcast(true); // turn on broadcast
|
||||||
sock.setBroadcast(true); // turn on broadcast
|
if (node.multicast == "multi") {
|
||||||
if (node.multicast == "multi") {
|
try {
|
||||||
try {
|
sock.setMulticastTTL(128);
|
||||||
sock.setMulticastTTL(128);
|
sock.addMembership(node.addr,node.iface); // Add to the multicast group
|
||||||
sock.addMembership(node.addr,node.iface); // Add to the multicast group
|
node.log(RED._("udp.status.mc-ready",{iface:node.iface,outport:node.outport,host:node.addr,port:node.port}));
|
||||||
node.log(RED._("udp.status.mc-ready",{outport:node.outport,host:node.addr,port:node.port}));
|
} catch (e) {
|
||||||
} catch (e) {
|
if (e.errno == "EINVAL") {
|
||||||
if (e.errno == "EINVAL") {
|
node.error(RED._("udp.errors.bad-mcaddress"));
|
||||||
node.error(RED._("udp.errors.bad-mcaddress"));
|
} else if (e.errno == "ENODEV") {
|
||||||
} else if (e.errno == "ENODEV") {
|
node.error(RED._("udp.errors.interface"));
|
||||||
node.error(RED._("udp.errors.interface"));
|
} else {
|
||||||
} else {
|
node.error(RED._("udp.errors.error",{error:e.errno}));
|
||||||
node.error(RED._("udp.errors.error",{error:e.errno}));
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
node.log(RED._("udp.status.bc-ready",{outport:node.outport,host:node.addr,port:node.port}));
|
||||||
}
|
}
|
||||||
} else {
|
});
|
||||||
node.log(RED._("udp.status.bc-ready",{outport:node.outport,host:node.addr,port:node.port}));
|
} else if ((node.outport !== "") && (!udpInputPortsInUse[node.outport])) {
|
||||||
}
|
sock.bind(node.outport);
|
||||||
});
|
node.log(RED._("udp.status.ready",{outport:node.outport,host:node.addr,port:node.port}));
|
||||||
} else if ((node.outport !== "") && (!udpInputPortsInUse[node.outport])) {
|
} else {
|
||||||
sock.bind(node.outport);
|
node.log(RED._("udp.status.ready-nolocal",{host:node.addr,port:node.port}));
|
||||||
node.log(RED._("udp.status.ready",{outport:node.outport,host:node.addr,port:node.port}));
|
}
|
||||||
} else {
|
|
||||||
node.log(RED._("udp.status.ready-nolocal",{host:node.addr,port:node.port}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
node.on("input", function(msg) {
|
node.on("input", function(msg) {
|
||||||
@ -198,8 +247,8 @@ module.exports = function(RED) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
node.on("close", function() {
|
node.on("close", function() {
|
||||||
if (udpInputPortsInUse.hasOwnProperty(node.outport || node.port)) {
|
if (udpInputPortsInUse.hasOwnProperty(p)) {
|
||||||
delete udpInputPortsInUse[node.outport || node.port];
|
delete udpInputPortsInUse[p];
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
sock.close();
|
sock.close();
|
||||||
|
@ -323,6 +323,7 @@
|
|||||||
"broker": "Server",
|
"broker": "Server",
|
||||||
"example": "e.g. localhost",
|
"example": "e.g. localhost",
|
||||||
"qos": "QoS",
|
"qos": "QoS",
|
||||||
|
"retain": "Retain",
|
||||||
"clientid": "Client ID",
|
"clientid": "Client ID",
|
||||||
"port": "Port",
|
"port": "Port",
|
||||||
"keepalive": "Keep alive time (s)",
|
"keepalive": "Keep alive time (s)",
|
||||||
@ -332,17 +333,22 @@
|
|||||||
"verify-server-cert":"Verify server certificate",
|
"verify-server-cert":"Verify server certificate",
|
||||||
"compatmode": "Use legacy MQTT 3.1 support"
|
"compatmode": "Use legacy MQTT 3.1 support"
|
||||||
},
|
},
|
||||||
|
"sections-label":{
|
||||||
|
"birth-message": "Message sent on connection (birth message)",
|
||||||
|
"will-message":"Message sent on an unexpected disconnection (will message)",
|
||||||
|
"close-message":"Message sent before disconnecting (close message)"
|
||||||
|
},
|
||||||
"tabs-label": {
|
"tabs-label": {
|
||||||
"connection": "Connection",
|
"connection": "Connection",
|
||||||
"security": "Security",
|
"security": "Security",
|
||||||
"will": "Will Message",
|
"messages": "Messages"
|
||||||
"birth": "Birth Message"
|
|
||||||
},
|
},
|
||||||
"placeholder": {
|
"placeholder": {
|
||||||
"clientid": "Leave blank for auto generated",
|
"clientid": "Leave blank for auto generated",
|
||||||
"clientid-nonclean":"Must be set for non-clean sessions",
|
"clientid-nonclean":"Must be set for non-clean sessions",
|
||||||
"will-topic": "Leave blank to disable will message",
|
"will-topic": "Leave blank to disable will message",
|
||||||
"birth-topic": "Leave blank to disable birth message"
|
"birth-topic": "Leave blank to disable birth message",
|
||||||
|
"close-topic": "Leave blank to disable close message"
|
||||||
},
|
},
|
||||||
"state": {
|
"state": {
|
||||||
"connected": "Connected to broker: __broker__",
|
"connected": "Connected to broker: __broker__",
|
||||||
@ -495,15 +501,15 @@
|
|||||||
"using": "using",
|
"using": "using",
|
||||||
"output": "Output",
|
"output": "Output",
|
||||||
"group": "Group",
|
"group": "Group",
|
||||||
"interface": "Local IP",
|
"interface": "Local IF",
|
||||||
"interfaceprompt": "(optional) local ip address to bind to",
|
|
||||||
"send": "Send a",
|
"send": "Send a",
|
||||||
"toport": "to port",
|
"toport": "to port",
|
||||||
"address": "Address",
|
"address": "Address",
|
||||||
"decode-base64": "Decode Base64 encoded payload?"
|
"decode-base64": "Decode Base64 encoded payload?"
|
||||||
},
|
},
|
||||||
"placeholder": {
|
"placeholder": {
|
||||||
"interface": "(optional) ip address of eth0",
|
"interface": "(optional) local interface or address to bind to",
|
||||||
|
"interfaceprompt": "(optional) local interface or address to bind to",
|
||||||
"address": "destination ip"
|
"address": "destination ip"
|
||||||
},
|
},
|
||||||
"udpmsgs": "udp messages",
|
"udpmsgs": "udp messages",
|
||||||
@ -531,10 +537,11 @@
|
|||||||
"mc-group": "udp multicast group __group__",
|
"mc-group": "udp multicast group __group__",
|
||||||
"listener-stopped": "udp listener stopped",
|
"listener-stopped": "udp listener stopped",
|
||||||
"output-stopped": "udp output stopped",
|
"output-stopped": "udp output stopped",
|
||||||
"mc-ready": "udp multicast ready: __outport__ -> __host__:__port__",
|
"mc-ready": "udp multicast ready: __iface__:__outport__ -> __host__:__port__",
|
||||||
"bc-ready": "udp broadcast ready: __outport__ -> __host__:__port__",
|
"bc-ready": "udp broadcast ready: __outport__ -> __host__:__port__",
|
||||||
"ready": "udp ready: __outport__ -> __host__:__port__",
|
"ready": "udp ready: __outport__ -> __host__:__port__",
|
||||||
"ready-nolocal": "udp ready: __host__:__port__"
|
"ready-nolocal": "udp ready: __host__:__port__",
|
||||||
|
"re-use": "udp re-use socket: __outport__ -> __host__:__port__"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"access-error": "UDP access error, you may need root access for ports below 1024",
|
"access-error": "UDP access error, you may need root access for ports below 1024",
|
||||||
@ -544,7 +551,8 @@
|
|||||||
"ip-notset": "udp: ip address not set",
|
"ip-notset": "udp: ip address not set",
|
||||||
"port-notset": "udp: port not set",
|
"port-notset": "udp: port not set",
|
||||||
"port-invalid": "udp: port number not valid",
|
"port-invalid": "udp: port number not valid",
|
||||||
"alreadyused": "udp: port already in use"
|
"alreadyused": "udp: port __port__ already in use",
|
||||||
|
"ifnotfound": "udp: interface __iface__ not found"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"switch": {
|
"switch": {
|
||||||
@ -566,6 +574,7 @@
|
|||||||
"false":"is false",
|
"false":"is false",
|
||||||
"null":"is null",
|
"null":"is null",
|
||||||
"nnull":"is not null",
|
"nnull":"is not null",
|
||||||
|
"istype":"is of type",
|
||||||
"head":"head",
|
"head":"head",
|
||||||
"tail":"tail",
|
"tail":"tail",
|
||||||
"index":"index between",
|
"index":"index between",
|
||||||
@ -671,7 +680,8 @@
|
|||||||
"html": {
|
"html": {
|
||||||
"label": {
|
"label": {
|
||||||
"select": "Selector",
|
"select": "Selector",
|
||||||
"output": "Output"
|
"output": "Output",
|
||||||
|
"in": "in"
|
||||||
},
|
},
|
||||||
"output": {
|
"output": {
|
||||||
"html": "the html content of the elements",
|
"html": "the html content of the elements",
|
||||||
|
@ -327,17 +327,22 @@
|
|||||||
"verify-server-cert": "サーバの証明書を確認",
|
"verify-server-cert": "サーバの証明書を確認",
|
||||||
"compatmode": "旧MQTT 3.1のサポート"
|
"compatmode": "旧MQTT 3.1のサポート"
|
||||||
},
|
},
|
||||||
|
"sections-label":{
|
||||||
|
"birth-message": "接続時の送信メッセージ(Birthメッセージ)",
|
||||||
|
"will-message":"予期しない切断時の送信メッセージ(Willメッセージ)",
|
||||||
|
"close-message":"切断前の送信メッセージ(Closeメッセージ)"
|
||||||
|
},
|
||||||
"tabs-label": {
|
"tabs-label": {
|
||||||
"connection": "接続",
|
"connection": "接続",
|
||||||
"security": "セキュリティ",
|
"security": "セキュリティ",
|
||||||
"will": "Willメッセージ",
|
"messages": "メッセージ"
|
||||||
"birth": "Birthメッセージ"
|
|
||||||
},
|
},
|
||||||
"placeholder": {
|
"placeholder": {
|
||||||
"clientid": "IDを自動生成する場合は、無記入にしてください",
|
"clientid": "IDを自動生成する場合は、無記入にしてください",
|
||||||
"clientid-nonclean": "新規ではないセッションを設定してください",
|
"clientid-nonclean": "新規ではないセッションを設定してください",
|
||||||
"will-topic": "Willメッセージを無効化する場合は、無記入にしてください",
|
"will-topic": "Willメッセージを無効化する場合は、無記入にしてください",
|
||||||
"birth-topic": "Birthメッセージを無効化する場合は、無記入にしてください"
|
"birth-topic": "Birthメッセージを無効化する場合は、無記入にしてください",
|
||||||
|
"close-topic": "Closeメッセージを無効化する場合は、無記入にしてください"
|
||||||
},
|
},
|
||||||
"state": {
|
"state": {
|
||||||
"connected": "ブローカへ接続しました: __broker__",
|
"connected": "ブローカへ接続しました: __broker__",
|
||||||
@ -488,14 +493,14 @@
|
|||||||
"output": "出力",
|
"output": "出力",
|
||||||
"group": "グループ",
|
"group": "グループ",
|
||||||
"interface": "ローカルIP",
|
"interface": "ローカルIP",
|
||||||
"interfaceprompt": "(任意) 使用するローカルIPアドレス",
|
|
||||||
"send": "送信",
|
"send": "送信",
|
||||||
"toport": "ポート",
|
"toport": "ポート",
|
||||||
"address": "アドレス",
|
"address": "アドレス",
|
||||||
"decode-base64": "Base64形式のペイロードを復号"
|
"decode-base64": "Base64形式のペイロードを復号"
|
||||||
},
|
},
|
||||||
"placeholder": {
|
"placeholder": {
|
||||||
"interface": "(任意) eth0のIPアドレス",
|
"interface": "(任意) 使用するローカルインターフェイスもしくはアドレス",
|
||||||
|
"interfaceprompt": "(任意) 使用するローカルインターフェイスもしくはアドレス",
|
||||||
"address": "宛先IPアドレス"
|
"address": "宛先IPアドレス"
|
||||||
},
|
},
|
||||||
"udpmsgs": "UDPメッセージ",
|
"udpmsgs": "UDPメッセージ",
|
||||||
@ -523,10 +528,11 @@
|
|||||||
"mc-group": "udpノードがグループ __group__ へマルチキャストしました",
|
"mc-group": "udpノードがグループ __group__ へマルチキャストしました",
|
||||||
"listener-stopped": "udpノードが待ち受けを停止しました",
|
"listener-stopped": "udpノードが待ち受けを停止しました",
|
||||||
"output-stopped": "udpノードが出力を停止しました",
|
"output-stopped": "udpノードが出力を停止しました",
|
||||||
"mc-ready": "udpノードはマルチキャストの準備ができています: __outport__ -> __host__:__port__",
|
"mc-ready": "udpノードはマルチキャストの準備ができています: __iface__:__outport__ -> __host__:__port__",
|
||||||
"bc-ready": "udpノードはブロードキャストの準備ができています: __outport__ -> __host__:__port__",
|
"bc-ready": "udpノードはブロードキャストの準備ができています: __outport__ -> __host__:__port__",
|
||||||
"ready": "udpノードは準備ができています: __outport__ -> __host__:__port__",
|
"ready": "udpノードは準備ができています: __outport__ -> __host__:__port__",
|
||||||
"ready-nolocal": "udpノードは準備ができています: __host__:__port__"
|
"ready-nolocal": "udpノードは準備ができています: __host__:__port__",
|
||||||
|
"re-use": "udp再利用ソケット: __outport__ -> __host__:__port__"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"access-error": "UDP接続エラー 管理者権限で1024未満のポート番号にアクセスできる必要があります",
|
"access-error": "UDP接続エラー 管理者権限で1024未満のポート番号にアクセスできる必要があります",
|
||||||
@ -536,6 +542,8 @@
|
|||||||
"ip-notset": "udp: IPアドレスが設定されていません",
|
"ip-notset": "udp: IPアドレスが設定されていません",
|
||||||
"port-notset": "udp: ポートが設定されていません",
|
"port-notset": "udp: ポートが設定されていません",
|
||||||
"port-invalid": "udp: ポート番号が不正です",
|
"port-invalid": "udp: ポート番号が不正です",
|
||||||
|
"alreadyused": "udp: 既に__port__番ポートが使用されています",
|
||||||
|
"ifnotfound": "udp: インターフェイス __iface__ がありません",
|
||||||
"alreadyused": "udp: 既にポートが使用されています"
|
"alreadyused": "udp: 既にポートが使用されています"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -661,7 +669,8 @@
|
|||||||
"html": {
|
"html": {
|
||||||
"label": {
|
"label": {
|
||||||
"select": "抽出する要素",
|
"select": "抽出する要素",
|
||||||
"output": "出力"
|
"output": "出力",
|
||||||
|
"in": "対象:"
|
||||||
},
|
},
|
||||||
"output": {
|
"output": {
|
||||||
"html": "要素内のHTML",
|
"html": "要素内のHTML",
|
||||||
@ -761,7 +770,9 @@
|
|||||||
"status": {
|
"status": {
|
||||||
"stopped": "停止",
|
"stopped": "停止",
|
||||||
"closed": "切断",
|
"closed": "切断",
|
||||||
"not-running": "停止中"
|
"not-running": "停止中",
|
||||||
|
"not-available": "利用不可",
|
||||||
|
"na": "N/A : __value__"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"ignorenode": "Raspberry Pi固有のノードを無視しました",
|
"ignorenode": "Raspberry Pi固有のノードを無視しました",
|
||||||
|
@ -525,7 +525,8 @@
|
|||||||
"mc-ready": "udp 组播已准备好: __outport__ -> __host__:__port__",
|
"mc-ready": "udp 组播已准备好: __outport__ -> __host__:__port__",
|
||||||
"bc-ready": "udp 广播已准备好: __outport__ -> __host__:__port__",
|
"bc-ready": "udp 广播已准备好: __outport__ -> __host__:__port__",
|
||||||
"ready": "udp 已准备好: __outport__ -> __host__:__port__",
|
"ready": "udp 已准备好: __outport__ -> __host__:__port__",
|
||||||
"ready-nolocal": "udp 已准备好: __host__:__port__"
|
"ready-nolocal": "udp 已准备好: __host__:__port__",
|
||||||
|
"re-use": "udp 重用套接字: __outport__ -> __host__:__port__"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"access-error": "UDP 访问错误, 你可能需要root权限才能接入1024以下的端口",
|
"access-error": "UDP 访问错误, 你可能需要root权限才能接入1024以下的端口",
|
||||||
|
@ -85,6 +85,7 @@
|
|||||||
{v:"false",t:"switch.rules.false",kind:'V'},
|
{v:"false",t:"switch.rules.false",kind:'V'},
|
||||||
{v:"null",t:"switch.rules.null",kind:'V'},
|
{v:"null",t:"switch.rules.null",kind:'V'},
|
||||||
{v:"nnull",t:"switch.rules.nnull",kind:'V'},
|
{v:"nnull",t:"switch.rules.nnull",kind:'V'},
|
||||||
|
{v:"istype",t:"switch.rules.istype",kind:'V'},
|
||||||
{v:"head",t:"switch.rules.head",kind:'S'},
|
{v:"head",t:"switch.rules.head",kind:'S'},
|
||||||
{v:"index",t:"switch.rules.index",kind:'S'},
|
{v:"index",t:"switch.rules.index",kind:'S'},
|
||||||
{v:"tail",t:"switch.rules.tail",kind:'S'},
|
{v:"tail",t:"switch.rules.tail",kind:'S'},
|
||||||
@ -160,6 +161,7 @@
|
|||||||
var selectField = rule.find("select");
|
var selectField = rule.find("select");
|
||||||
var type = selectField.val()||"";
|
var type = selectField.val()||"";
|
||||||
var valueField = rule.find(".node-input-rule-value");
|
var valueField = rule.find(".node-input-rule-value");
|
||||||
|
var typeField = rule.find(".node-input-rule-type-value");
|
||||||
var numField = rule.find(".node-input-rule-num-value");
|
var numField = rule.find(".node-input-rule-num-value");
|
||||||
var expField = rule.find(".node-input-rule-exp-value");
|
var expField = rule.find(".node-input-rule-exp-value");
|
||||||
var btwnField1 = rule.find(".node-input-rule-btwn-value");
|
var btwnField1 = rule.find(".node-input-rule-btwn-value");
|
||||||
@ -180,6 +182,8 @@
|
|||||||
numField.typedInput("width",(newWidth-selectWidth-70));
|
numField.typedInput("width",(newWidth-selectWidth-70));
|
||||||
} else if (type === "jsonata_exp") {
|
} else if (type === "jsonata_exp") {
|
||||||
expField.typedInput("width",(newWidth-selectWidth-70));
|
expField.typedInput("width",(newWidth-selectWidth-70));
|
||||||
|
} else if (type === "istype") {
|
||||||
|
typeField.typedInput("width",(newWidth-selectWidth-70));
|
||||||
} else {
|
} else {
|
||||||
if (type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else") {
|
if (type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else") {
|
||||||
// valueField.hide();
|
// valueField.hide();
|
||||||
@ -232,6 +236,18 @@
|
|||||||
var btwnValueField = $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata',previousValueType]});
|
var btwnValueField = $('<input/>',{class:"node-input-rule-btwn-value",type:"text",style:"margin-left: 5px;"}).appendTo(row).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata',previousValueType]});
|
||||||
var btwnAndLabel = $('<span/>',{class:"node-input-rule-btwn-label"}).text(" "+andLabel+" ").appendTo(row3);
|
var btwnAndLabel = $('<span/>',{class:"node-input-rule-btwn-label"}).text(" "+andLabel+" ").appendTo(row3);
|
||||||
var btwnValue2Field = $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"margin-left:2px;"}).appendTo(row3).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata',previousValueType]});
|
var btwnValue2Field = $('<input/>',{class:"node-input-rule-btwn-value2",type:"text",style:"margin-left:2px;"}).appendTo(row3).typedInput({default:'num',types:['msg','flow','global','str','num','jsonata',previousValueType]});
|
||||||
|
var typeValueField = $('<input/>',{class:"node-input-rule-type-value",type:"text",style:"margin-left: 5px;"}).appendTo(row)
|
||||||
|
.typedInput({default:'string',types:[
|
||||||
|
{value:"string",label:"string",hasValue:false},
|
||||||
|
{value:"number",label:"number",hasValue:false},
|
||||||
|
{value:"boolean",label:"boolean",hasValue:false},
|
||||||
|
{value:"array",label:"array",hasValue:false},
|
||||||
|
{value:"buffer",label:"buffer",hasValue:false},
|
||||||
|
{value:"object",label:"object",hasValue:false},
|
||||||
|
{value:"json",label:"JSON string",hasValue:false},
|
||||||
|
{value:"undefined",label:"undefined",hasValue:false},
|
||||||
|
{value:"null",label:"null",hasValue:false}
|
||||||
|
]});
|
||||||
var finalspan = $('<span/>',{style:"float: right;margin-top: 6px;"}).appendTo(row);
|
var finalspan = $('<span/>',{style:"float: right;margin-top: 6px;"}).appendTo(row);
|
||||||
finalspan.append(' → <span class="node-input-rule-index">'+(i+1)+'</span> ');
|
finalspan.append(' → <span class="node-input-rule-index">'+(i+1)+'</span> ');
|
||||||
var caseSensitive = $('<input/>',{id:"node-input-rule-case-"+i,class:"node-input-rule-case",type:"checkbox",style:"width:auto;vertical-align:top"}).appendTo(row2);
|
var caseSensitive = $('<input/>',{id:"node-input-rule-case-"+i,class:"node-input-rule-case",type:"checkbox",style:"width:auto;vertical-align:top"}).appendTo(row2);
|
||||||
@ -243,26 +259,39 @@
|
|||||||
valueField.typedInput('hide');
|
valueField.typedInput('hide');
|
||||||
expValueField.typedInput('hide');
|
expValueField.typedInput('hide');
|
||||||
numValueField.typedInput('hide');
|
numValueField.typedInput('hide');
|
||||||
|
typeValueField.typedInput('hide');
|
||||||
btwnValueField.typedInput('show');
|
btwnValueField.typedInput('show');
|
||||||
} else if ((type === "head") || (type === "tail")) {
|
} else if ((type === "head") || (type === "tail")) {
|
||||||
btwnValueField.typedInput('hide');
|
btwnValueField.typedInput('hide');
|
||||||
btwnValue2Field.typedInput('hide');
|
btwnValue2Field.typedInput('hide');
|
||||||
expValueField.typedInput('hide');
|
expValueField.typedInput('hide');
|
||||||
numValueField.typedInput('show');
|
numValueField.typedInput('show');
|
||||||
|
typeValueField.typedInput('hide');
|
||||||
valueField.typedInput('hide');
|
valueField.typedInput('hide');
|
||||||
} else if (type === "jsonata_exp") {
|
} else if (type === "jsonata_exp") {
|
||||||
btwnValueField.typedInput('hide');
|
btwnValueField.typedInput('hide');
|
||||||
btwnValue2Field.typedInput('hide');
|
btwnValue2Field.typedInput('hide');
|
||||||
expValueField.typedInput('show');
|
expValueField.typedInput('show');
|
||||||
numValueField.typedInput('hide');
|
numValueField.typedInput('hide');
|
||||||
|
typeValueField.typedInput('hide');
|
||||||
valueField.typedInput('hide');
|
valueField.typedInput('hide');
|
||||||
} else {
|
} else {
|
||||||
btwnValueField.typedInput('hide');
|
btwnValueField.typedInput('hide');
|
||||||
expValueField.typedInput('hide');
|
expValueField.typedInput('hide');
|
||||||
numValueField.typedInput('hide');
|
numValueField.typedInput('hide');
|
||||||
|
typeValueField.typedInput('hide');
|
||||||
|
valueField.typedInput('hide');
|
||||||
if (type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else") {
|
if (type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else") {
|
||||||
valueField.typedInput('hide');
|
valueField.typedInput('hide');
|
||||||
} else {
|
typeValueField.typedInput('hide');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (type === "istype") {
|
||||||
|
valueField.typedInput('hide');
|
||||||
|
typeValueField.typedInput('show');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
typeValueField.typedInput('hide');
|
||||||
valueField.typedInput('show');
|
valueField.typedInput('show');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -287,6 +316,9 @@
|
|||||||
} else if ((rule.t === "head") || (rule.t === "tail")) {
|
} else if ((rule.t === "head") || (rule.t === "tail")) {
|
||||||
numValueField.typedInput('value',rule.v);
|
numValueField.typedInput('value',rule.v);
|
||||||
numValueField.typedInput('type',rule.vt||'num');
|
numValueField.typedInput('type',rule.vt||'num');
|
||||||
|
} else if (rule.t === "istype") {
|
||||||
|
typeValueField.typedInput('value',rule.vt);
|
||||||
|
typeValueField.typedInput('type',rule.vt);
|
||||||
} else if (rule.t === "jsonata_exp") {
|
} else if (rule.t === "jsonata_exp") {
|
||||||
expValueField.typedInput('value',rule.v);
|
expValueField.typedInput('value',rule.v);
|
||||||
expValueField.typedInput('type',rule.vt||'jsonata');
|
expValueField.typedInput('type',rule.vt||'jsonata');
|
||||||
@ -359,6 +391,9 @@
|
|||||||
} else if ((type === "head") || (type === "tail")) {
|
} else if ((type === "head") || (type === "tail")) {
|
||||||
r.v = rule.find(".node-input-rule-num-value").typedInput('value');
|
r.v = rule.find(".node-input-rule-num-value").typedInput('value');
|
||||||
r.vt = rule.find(".node-input-rule-num-value").typedInput('type');
|
r.vt = rule.find(".node-input-rule-num-value").typedInput('type');
|
||||||
|
} else if (type === "istype") {
|
||||||
|
r.v = rule.find(".node-input-rule-type-value").typedInput('type');
|
||||||
|
r.vt = rule.find(".node-input-rule-type-value").typedInput('type');
|
||||||
} else if (type === "jsonata_exp") {
|
} else if (type === "jsonata_exp") {
|
||||||
r.v = rule.find(".node-input-rule-exp-value").typedInput('value');
|
r.v = rule.find(".node-input-rule-exp-value").typedInput('value');
|
||||||
r.vt = rule.find(".node-input-rule-exp-value").typedInput('type');
|
r.vt = rule.find(".node-input-rule-exp-value").typedInput('type');
|
||||||
|
@ -31,6 +31,16 @@ module.exports = function(RED) {
|
|||||||
'false': function(a) { return a === false; },
|
'false': function(a) { return a === false; },
|
||||||
'null': function(a) { return (typeof a == "undefined" || a === null); },
|
'null': function(a) { return (typeof a == "undefined" || a === null); },
|
||||||
'nnull': function(a) { return (typeof a != "undefined" && a !== null); },
|
'nnull': function(a) { return (typeof a != "undefined" && a !== null); },
|
||||||
|
'istype': function(a, b) {
|
||||||
|
if (b === "array") { return Array.isArray(a); }
|
||||||
|
else if (b === "buffer") { return Buffer.isBuffer(a); }
|
||||||
|
else if (b === "json") {
|
||||||
|
try { JSON.parse(a); return true; } // or maybe ??? a !== null; }
|
||||||
|
catch(e) { return false;}
|
||||||
|
}
|
||||||
|
else if (b === "null") { return a === null; }
|
||||||
|
else { return typeof a === b && !Array.isArray(a) && !Buffer.isBuffer(a) && a !== null; }
|
||||||
|
},
|
||||||
'head': function(a, b, c, d, parts) {
|
'head': function(a, b, c, d, parts) {
|
||||||
var count = Number(b);
|
var count = Number(b);
|
||||||
return (parts.index < count);
|
return (parts.index < count);
|
||||||
@ -292,6 +302,10 @@ module.exports = function(RED) {
|
|||||||
node.error(RED._("switch.errors.invalid-expr",{error:err.message}));
|
node.error(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (rule.vt === 'json') {
|
||||||
|
v1 = "json";
|
||||||
|
} else if (rule.vt === 'null') {
|
||||||
|
v1 = "null";
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
v1 = RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg);
|
v1 = RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<script type="text/x-red" data-template-name="html">
|
<script type="text/x-red" data-template-name="html">
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
|
<label for="node-input-property"><i class="fa fa-ellipsis-h"></i> <span data-i18n="node-red:common.label.property"></span></label>
|
||||||
<input type="text" id="node-input-property" style="width:70%;"/>
|
<input type="text" id="node-input-property" style="width:70%">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-tag"><i class="fa fa-filter"></i> <span data-i18n="html.label.select"></span></label>
|
<label for="node-input-tag"><i class="fa fa-filter"></i> <span data-i18n="html.label.select"></span></label>
|
||||||
@ -24,6 +24,10 @@
|
|||||||
<option value="multi" data-i18n="html.format.multi"></option>
|
<option value="multi" data-i18n="html.format.multi"></option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-outproperty"> </label>
|
||||||
|
<span data-i18n="html.label.in" style="padding-left:8px; padding-right:2px; vertical-align:-1px;"></span> <input type="text" id="node-input-outproperty" style="width:64%">
|
||||||
|
</div>
|
||||||
<br/>
|
<br/>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||||
@ -59,6 +63,7 @@
|
|||||||
defaults: {
|
defaults: {
|
||||||
name: {value:""},
|
name: {value:""},
|
||||||
property: {value:"payload"},
|
property: {value:"payload"},
|
||||||
|
outproperty: {value:"payload"},
|
||||||
tag: {value:""},
|
tag: {value:""},
|
||||||
ret: {value:"html"},
|
ret: {value:"html"},
|
||||||
as: {value:"single"}
|
as: {value:"single"}
|
||||||
@ -74,6 +79,7 @@
|
|||||||
},
|
},
|
||||||
oneditprepare: function() {
|
oneditprepare: function() {
|
||||||
$("#node-input-property").typedInput({default:'msg',types:['msg']});
|
$("#node-input-property").typedInput({default:'msg',types:['msg']});
|
||||||
|
$("#node-input-outproperty").typedInput({default:'msg',types:['msg']});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -21,6 +21,7 @@ module.exports = function(RED) {
|
|||||||
function CheerioNode(n) {
|
function CheerioNode(n) {
|
||||||
RED.nodes.createNode(this,n);
|
RED.nodes.createNode(this,n);
|
||||||
this.property = n.property||"payload";
|
this.property = n.property||"payload";
|
||||||
|
this.outproperty = n.outproperty||this.property||"payload";
|
||||||
this.tag = n.tag;
|
this.tag = n.tag;
|
||||||
this.ret = n.ret || "html";
|
this.ret = n.ret || "html";
|
||||||
this.as = n.as || "single";
|
this.as = n.as || "single";
|
||||||
@ -48,7 +49,7 @@ module.exports = function(RED) {
|
|||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (pay2) {
|
if (pay2) {
|
||||||
var new_msg = RED.util.cloneMessage(msg);
|
var new_msg = RED.util.cloneMessage(msg);
|
||||||
RED.util.setMessageProperty(new_msg,node.property,pay2);
|
RED.util.setMessageProperty(new_msg,node.outproperty,pay2);
|
||||||
new_msg.parts = {
|
new_msg.parts = {
|
||||||
id: msg._msgid,
|
id: msg._msgid,
|
||||||
index: index,
|
index: index,
|
||||||
@ -68,7 +69,7 @@ module.exports = function(RED) {
|
|||||||
index++;
|
index++;
|
||||||
});
|
});
|
||||||
if (node.as === "single") { // Always return an array - even if blank
|
if (node.as === "single") { // Always return an array - even if blank
|
||||||
RED.util.setMessageProperty(msg,node.property,pay);
|
RED.util.setMessageProperty(msg,node.outproperty,pay);
|
||||||
node.send(msg);
|
node.send(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
package.json
22
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red",
|
"name": "node-red",
|
||||||
"version": "0.18.4",
|
"version": "0.18.5",
|
||||||
"description": "A visual tool for wiring the Internet of Things",
|
"description": "A visual tool for wiring the Internet of Things",
|
||||||
"homepage": "http://nodered.org",
|
"homepage": "http://nodered.org",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
@ -43,22 +43,22 @@
|
|||||||
"cookie-parser": "1.4.3",
|
"cookie-parser": "1.4.3",
|
||||||
"cors": "2.8.4",
|
"cors": "2.8.4",
|
||||||
"cron": "1.3.0",
|
"cron": "1.3.0",
|
||||||
"express": "4.16.2",
|
"express": "4.16.3",
|
||||||
"express-session": "1.15.6",
|
"express-session": "1.15.6",
|
||||||
"follow-redirects": "1.3.0",
|
"follow-redirects": "1.4.1",
|
||||||
"fs-extra": "5.0.0",
|
"fs-extra": "5.0.0",
|
||||||
"fs.notify": "0.0.4",
|
"fs.notify": "0.0.4",
|
||||||
"hash-sum": "1.0.2",
|
"hash-sum": "1.0.2",
|
||||||
"i18next": "1.10.6",
|
"i18next": "1.10.6",
|
||||||
"ink-docstrap": "^1.3.2",
|
"ink-docstrap": "^1.3.2",
|
||||||
"is-utf8": "0.2.1",
|
"is-utf8": "0.2.1",
|
||||||
"js-yaml": "3.10.0",
|
"js-yaml": "3.11.0",
|
||||||
"json-stringify-safe": "5.0.1",
|
"json-stringify-safe": "5.0.1",
|
||||||
"jsonata": "1.5.0",
|
"jsonata": "1.5.3",
|
||||||
"media-typer": "0.3.0",
|
"media-typer": "0.3.0",
|
||||||
"memorystore": "1.6.0",
|
"memorystore": "1.6.0",
|
||||||
"mime": "1.4.1",
|
"mime": "1.4.1",
|
||||||
"mqtt": "2.15.1",
|
"mqtt": "2.17.0",
|
||||||
"multer": "1.3.0",
|
"multer": "1.3.0",
|
||||||
"mustache": "2.3.0",
|
"mustache": "2.3.0",
|
||||||
"node-red-node-email": "0.1.*",
|
"node-red-node-email": "0.1.*",
|
||||||
@ -71,10 +71,10 @@
|
|||||||
"passport": "0.4.0",
|
"passport": "0.4.0",
|
||||||
"passport-http-bearer": "1.0.1",
|
"passport-http-bearer": "1.0.1",
|
||||||
"passport-oauth2-client-password": "0.1.2",
|
"passport-oauth2-client-password": "0.1.2",
|
||||||
"raw-body": "2.3.2",
|
"raw-body": "2.3.3",
|
||||||
"semver": "5.4.1",
|
"semver": "5.5.0",
|
||||||
"sentiment": "2.1.0",
|
"sentiment": "2.1.0",
|
||||||
"uglify-js": "3.3.6",
|
"uglify-js": "3.3.24",
|
||||||
"when": "3.7.8",
|
"when": "3.7.8",
|
||||||
"ws": "1.1.5",
|
"ws": "1.1.5",
|
||||||
"xml2js": "0.4.19"
|
"xml2js": "0.4.19"
|
||||||
@ -103,6 +103,7 @@
|
|||||||
"grunt-sass": "~2.0.0",
|
"grunt-sass": "~2.0.0",
|
||||||
"grunt-simple-mocha": "~0.4.1",
|
"grunt-simple-mocha": "~0.4.1",
|
||||||
"grunt-webdriver": "^2.0.3",
|
"grunt-webdriver": "^2.0.3",
|
||||||
|
"http-proxy": "^1.16.2",
|
||||||
"ink-docstrap": "^1.3.2",
|
"ink-docstrap": "^1.3.2",
|
||||||
"istanbul": "0.4.5",
|
"istanbul": "0.4.5",
|
||||||
"mocha": "^5.1.1",
|
"mocha": "^5.1.1",
|
||||||
@ -113,7 +114,8 @@
|
|||||||
"wdio-chromedriver-service": "^0.1.1",
|
"wdio-chromedriver-service": "^0.1.1",
|
||||||
"wdio-mocha-framework": "^0.5.11",
|
"wdio-mocha-framework": "^0.5.11",
|
||||||
"wdio-spec-reporter": "^0.1.3",
|
"wdio-spec-reporter": "^0.1.3",
|
||||||
"webdriverio": "^4.9.11"
|
"webdriverio": "^4.9.11",
|
||||||
|
"node-red-node-test-helper": "^0.1.7"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
|
4
red.js
4
red.js
@ -192,7 +192,7 @@ try {
|
|||||||
if (err.code == "unsupported_version") {
|
if (err.code == "unsupported_version") {
|
||||||
console.log("Unsupported version of node.js:",process.version);
|
console.log("Unsupported version of node.js:",process.version);
|
||||||
console.log("Node-RED requires node.js v4 or later");
|
console.log("Node-RED requires node.js v4 or later");
|
||||||
} else if (err.code == "not_built") {
|
} else if (err.code == "not_built") {
|
||||||
console.log("Node-RED has not been built. See README.md for details");
|
console.log("Node-RED has not been built. See README.md for details");
|
||||||
} else {
|
} else {
|
||||||
console.log("Failed to start server:");
|
console.log("Failed to start server:");
|
||||||
@ -276,7 +276,7 @@ function getListenPath() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var listenPath = 'http'+(settings.https?'s':'')+'://'+
|
var listenPath = 'http'+(settings.https?'s':'')+'://'+
|
||||||
(settings.uiHost == '0.0.0.0'?'127.0.0.1':settings.uiHost)+
|
(settings.uiHost == '::'?'localhost':(settings.uiHost == '0.0.0.0'?'127.0.0.1':settings.uiHost))+
|
||||||
':'+port;
|
':'+port;
|
||||||
if (settings.httpAdminRoot !== false) {
|
if (settings.httpAdminRoot !== false) {
|
||||||
listenPath += settings.httpAdminRoot;
|
listenPath += settings.httpAdminRoot;
|
||||||
|
@ -93,6 +93,7 @@
|
|||||||
"credentials_load_failed": "<p>Flows stopped as the credentials could not be decrypted.</p><p>The flow credential file is encrypted, but the project's encryption key is missing or invalid.</p>",
|
"credentials_load_failed": "<p>Flows stopped as the credentials could not be decrypted.</p><p>The flow credential file is encrypted, but the project's encryption key is missing or invalid.</p>",
|
||||||
"credentials_load_failed_reset":"<p>Credentials could not be decrypted</p><p>The flow credential file is encrypted, but the project's encryption key is missing or invalid.</p><p>The flow credential file will be reset on the next deployment. Any existing flow credentials will be cleared.</p>",
|
"credentials_load_failed_reset":"<p>Credentials could not be decrypted</p><p>The flow credential file is encrypted, but the project's encryption key is missing or invalid.</p><p>The flow credential file will be reset on the next deployment. Any existing flow credentials will be cleared.</p>",
|
||||||
"missing_flow_file": "<p>Project flow file not found.</p><p>The project is not configured with a flow file.</p>",
|
"missing_flow_file": "<p>Project flow file not found.</p><p>The project is not configured with a flow file.</p>",
|
||||||
|
"missing_package_file": "<p>Project package file not found.</p><p>The project is missing a package.json file.</p>",
|
||||||
"project_empty": "<p>The project is empty.</p><p>Do you want to create a default set of project files?<br/>Otherwise, you will have to manually add files to the project outside of the editor.</p>",
|
"project_empty": "<p>The project is empty.</p><p>Do you want to create a default set of project files?<br/>Otherwise, you will have to manually add files to the project outside of the editor.</p>",
|
||||||
"project_not_found": "<p>Project '__project__' not found.</p>",
|
"project_not_found": "<p>Project '__project__' not found.</p>",
|
||||||
"git_merge_conflict": "<p>Automatic merging of changes failed.</p><p>Fix the unmerged conflicts then commit the results.</p>"
|
"git_merge_conflict": "<p>Automatic merging of changes failed.</p><p>Fix the unmerged conflicts then commit the results.</p>"
|
||||||
|
@ -68,10 +68,17 @@ var api = module.exports = {
|
|||||||
try {
|
try {
|
||||||
var safeSettings = {
|
var safeSettings = {
|
||||||
httpNodeRoot: runtime.settings.httpNodeRoot||"/",
|
httpNodeRoot: runtime.settings.httpNodeRoot||"/",
|
||||||
version: runtime.settings.version,
|
version: runtime.settings.version
|
||||||
user: opts.user
|
}
|
||||||
|
if (opts.user) {
|
||||||
|
safeSettings.user = {}
|
||||||
|
var props = ["anonymous","username","image","permissions"];
|
||||||
|
props.forEach(prop => {
|
||||||
|
if (opts.user.hasOwnProperty(prop)) {
|
||||||
|
safeSettings.user[prop] = opts.user[prop];
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (util.isArray(runtime.settings.paletteCategories)) {
|
if (util.isArray(runtime.settings.paletteCategories)) {
|
||||||
safeSettings.paletteCategories = runtime.settings.paletteCategories;
|
safeSettings.paletteCategories = runtime.settings.paletteCategories;
|
||||||
}
|
}
|
||||||
|
@ -237,8 +237,8 @@ function Flow(global,flow) {
|
|||||||
|
|
||||||
this.handleError = function(node,logMessage,msg) {
|
this.handleError = function(node,logMessage,msg) {
|
||||||
var count = 1;
|
var count = 1;
|
||||||
if (msg && msg.hasOwnProperty("error")) {
|
if (msg && msg.hasOwnProperty("error") && msg.error !== null) {
|
||||||
if (msg.error.hasOwnProperty("source")) {
|
if (msg.error.hasOwnProperty("source") && msg.error.source !== null) {
|
||||||
if (msg.error.source.id === node.id) {
|
if (msg.error.source.id === node.id) {
|
||||||
count = msg.error.source.count+1;
|
count = msg.error.source.count+1;
|
||||||
if (count === 10) {
|
if (count === 10) {
|
||||||
|
@ -53,15 +53,13 @@ function getGitUser(user) {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
function Project(name) {
|
function Project(path) {
|
||||||
this.name = name;
|
this.path = path;
|
||||||
this.path = fspath.join(projectsDir,name);
|
this.name = fspath.basename(path);
|
||||||
this.paths = {};
|
this.paths = {};
|
||||||
this.files = {};
|
this.files = {};
|
||||||
this.auth = {origin:{}};
|
this.auth = {origin:{}};
|
||||||
|
|
||||||
this.missingFiles = [];
|
this.missingFiles = [];
|
||||||
|
|
||||||
this.credentialSecret = null;
|
this.credentialSecret = null;
|
||||||
}
|
}
|
||||||
Project.prototype.load = function () {
|
Project.prototype.load = function () {
|
||||||
@ -70,7 +68,9 @@ Project.prototype.load = function () {
|
|||||||
// console.log(globalProjectSettings)
|
// console.log(globalProjectSettings)
|
||||||
var projectSettings = {};
|
var projectSettings = {};
|
||||||
if (globalProjectSettings) {
|
if (globalProjectSettings) {
|
||||||
projectSettings = globalProjectSettings.projects[this.name]||{};
|
if (globalProjectSettings.projects.hasOwnProperty(this.name)) {
|
||||||
|
projectSettings = globalProjectSettings.projects[this.name] || {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.credentialSecret = projectSettings.credentialSecret;
|
this.credentialSecret = projectSettings.credentialSecret;
|
||||||
@ -81,9 +81,7 @@ Project.prototype.load = function () {
|
|||||||
|
|
||||||
var promises = [];
|
var promises = [];
|
||||||
return checkProjectFiles(project).then(function(missingFiles) {
|
return checkProjectFiles(project).then(function(missingFiles) {
|
||||||
if (missingFiles.length > 0) {
|
project.missingFiles = missingFiles;
|
||||||
project.missingFiles = missingFiles;
|
|
||||||
}
|
|
||||||
if (missingFiles.indexOf('package.json') === -1) {
|
if (missingFiles.indexOf('package.json') === -1) {
|
||||||
project.paths['package.json'] = fspath.join(project.path,"package.json");
|
project.paths['package.json'] = fspath.join(project.path,"package.json");
|
||||||
promises.push(fs.readFile(project.paths['package.json'],"utf8").then(function(content) {
|
promises.push(fs.readFile(project.paths['package.json'],"utf8").then(function(content) {
|
||||||
@ -135,9 +133,9 @@ Project.prototype.load = function () {
|
|||||||
|
|
||||||
Project.prototype.initialise = function(user,data) {
|
Project.prototype.initialise = function(user,data) {
|
||||||
var project = this;
|
var project = this;
|
||||||
if (!this.empty) {
|
// if (!this.empty) {
|
||||||
throw new Error("Cannot initialise non-empty project");
|
// throw new Error("Cannot initialise non-empty project");
|
||||||
}
|
// }
|
||||||
var files = Object.keys(defaultFileSet);
|
var files = Object.keys(defaultFileSet);
|
||||||
var promises = [];
|
var promises = [];
|
||||||
|
|
||||||
@ -148,17 +146,25 @@ Project.prototype.initialise = function(user,data) {
|
|||||||
promises.push(settings.set('projects',projects));
|
promises.push(settings.set('projects',projects));
|
||||||
}
|
}
|
||||||
|
|
||||||
project.files.flow = data.files.flow;
|
if (data.hasOwnProperty('files')) {
|
||||||
project.files.credentials = data.files.credentials;
|
if (data.files.hasOwnProperty('flow') && data.files.hasOwnProperty('credentials')) {
|
||||||
var flowFilePath = fspath.join(project.path,project.files.flow);
|
project.files.flow = data.files.flow;
|
||||||
var credsFilePath = getCredentialsFilename(flowFilePath);
|
project.files.credentials = data.files.credentials;
|
||||||
promises.push(util.writeFile(flowFilePath,"[]"));
|
var flowFilePath = fspath.join(project.path,project.files.flow);
|
||||||
promises.push(util.writeFile(credsFilePath,"{}"));
|
var credsFilePath = getCredentialsFilename(flowFilePath);
|
||||||
files.push(project.files.flow);
|
promises.push(util.writeFile(flowFilePath,"[]"));
|
||||||
files.push(project.files.credentials);
|
promises.push(util.writeFile(credsFilePath,"{}"));
|
||||||
|
files.push(project.files.flow);
|
||||||
|
files.push(project.files.credentials);
|
||||||
|
}
|
||||||
|
}
|
||||||
for (var file in defaultFileSet) {
|
for (var file in defaultFileSet) {
|
||||||
if (defaultFileSet.hasOwnProperty(file)) {
|
if (defaultFileSet.hasOwnProperty(file)) {
|
||||||
promises.push(util.writeFile(fspath.join(project.path,file),defaultFileSet[file](project)));
|
var path = fspath.join(project.path,file);
|
||||||
|
if (!fs.existsSync(path)) {
|
||||||
|
promises.push(util.writeFile(path,defaultFileSet[file](project)));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -740,7 +746,7 @@ Project.prototype.getCredentialsFileBackup = function() {
|
|||||||
return getBackupFilename(this.getCredentialsFile());
|
return getBackupFilename(this.getCredentialsFile());
|
||||||
}
|
}
|
||||||
|
|
||||||
Project.prototype.toJSON = function () {
|
Project.prototype.export = function () {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: this.name,
|
name: this.name,
|
||||||
@ -780,28 +786,18 @@ function getBackupFilename(filename) {
|
|||||||
return fspath.join(ffDir,"."+ffName+".backup");
|
return fspath.join(ffDir,"."+ffName+".backup");
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkProjectExists(project) {
|
function checkProjectExists(projectPath) {
|
||||||
var projectPath = fspath.join(projectsDir,project);
|
|
||||||
return fs.pathExists(projectPath).then(function(exists) {
|
return fs.pathExists(projectPath).then(function(exists) {
|
||||||
if (!exists) {
|
if (!exists) {
|
||||||
var e = new Error("Project not found: "+project);
|
var e = new Error("Project not found");
|
||||||
e.code = "project_not_found";
|
e.code = "project_not_found";
|
||||||
e.project = project;
|
var name = fspath.basename(projectPath);
|
||||||
|
e.project = name;
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createProjectDirectory(project) {
|
|
||||||
var projectPath = fspath.join(projectsDir,project);
|
|
||||||
return fs.ensureDir(projectPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteProjectDirectory(project) {
|
|
||||||
var projectPath = fspath.join(projectsDir,project);
|
|
||||||
return fs.remove(projectPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDefaultProject(user, project) {
|
function createDefaultProject(user, project) {
|
||||||
var projectPath = fspath.join(projectsDir,project.name);
|
var projectPath = fspath.join(projectsDir,project.name);
|
||||||
// Create a basic skeleton of a project
|
// Create a basic skeleton of a project
|
||||||
@ -904,17 +900,23 @@ function createProject(user, metadata) {
|
|||||||
} else {
|
} else {
|
||||||
username = user.username;
|
username = user.username;
|
||||||
}
|
}
|
||||||
|
if (!metadata.path) {
|
||||||
|
throw new Error("Project missing path property");
|
||||||
|
}
|
||||||
|
if (!metadata.name) {
|
||||||
|
throw new Error("Project missing name property");
|
||||||
|
}
|
||||||
|
|
||||||
var project = metadata.name;
|
var project = metadata.name;
|
||||||
|
var projectPath = metadata.path;
|
||||||
return new Promise(function(resolve,reject) {
|
return new Promise(function(resolve,reject) {
|
||||||
var projectPath = fspath.join(projectsDir,project);
|
|
||||||
fs.stat(projectPath, function(err,stat) {
|
fs.stat(projectPath, function(err,stat) {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
var e = new Error("NLS: Project already exists");
|
var e = new Error("NLS: Project already exists");
|
||||||
e.code = "project_exists";
|
e.code = "project_exists";
|
||||||
return reject(e);
|
return reject(e);
|
||||||
}
|
}
|
||||||
createProjectDirectory(project).then(function() {
|
fs.ensureDir(projectPath).then(function() {
|
||||||
var projects = settings.get('projects');
|
var projects = settings.get('projects');
|
||||||
if (!projects) {
|
if (!projects) {
|
||||||
projects = {
|
projects = {
|
||||||
@ -951,7 +953,7 @@ function createProject(user, metadata) {
|
|||||||
return createDefaultProject(user, metadata);
|
return createDefaultProject(user, metadata);
|
||||||
}
|
}
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
resolve(getProject(project))
|
resolve(loadProject(projectPath))
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
fs.remove(projectPath,function() {
|
fs.remove(projectPath,function() {
|
||||||
reject(err);
|
reject(err);
|
||||||
@ -961,50 +963,21 @@ function createProject(user, metadata) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteProject(user, name) {
|
function deleteProject(user, projectPath) {
|
||||||
return checkProjectExists(name).then(function() {
|
return checkProjectExists(projectPath).then(function() {
|
||||||
if (currentProject && currentProject.name === name) {
|
return fs.remove(projectPath).then(function() {
|
||||||
var e = new Error("NLS: Can't delete the active project");
|
var name = fspath.basename(projectPath);
|
||||||
e.code = "cannot_delete_active_project";
|
var projects = settings.get('projects');
|
||||||
throw e;
|
delete projects.projects[name];
|
||||||
}
|
return settings.set('projects', projects);
|
||||||
else {
|
|
||||||
return deleteProjectDirectory(name).then(function() {
|
|
||||||
var projects = settings.get('projects');
|
|
||||||
delete projects.projects[name];
|
|
||||||
return settings.set('projects', projects);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentProject;
|
|
||||||
|
|
||||||
function getProject(name) {
|
|
||||||
return checkProjectExists(name).then(function() {
|
|
||||||
if (currentProject && currentProject.name === name) {
|
|
||||||
return currentProject;
|
|
||||||
}
|
|
||||||
currentProject = new Project(name);
|
|
||||||
return currentProject.load();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function listProjects() {
|
|
||||||
return fs.readdir(projectsDir).then(function(fns) {
|
|
||||||
var dirs = [];
|
|
||||||
fns.sort(function(A,B) {
|
|
||||||
return A.toLowerCase().localeCompare(B.toLowerCase());
|
|
||||||
}).filter(function(fn) {
|
|
||||||
var fullPath = fspath.join(projectsDir,fn);
|
|
||||||
if (fn[0] != ".") {
|
|
||||||
var stats = fs.lstatSync(fullPath);
|
|
||||||
if (stats.isDirectory()) {
|
|
||||||
dirs.push(fn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return dirs;
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadProject(projectPath) {
|
||||||
|
return checkProjectExists(projectPath).then(function() {
|
||||||
|
var project = new Project(projectPath);
|
||||||
|
return project.load();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1018,9 +991,7 @@ function init(_settings, _runtime) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: init,
|
init: init,
|
||||||
get: getProject,
|
load: loadProject,
|
||||||
create: createProject,
|
create: createProject,
|
||||||
delete: deleteProject,
|
delete: deleteProject
|
||||||
list: listProjects
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,9 @@ function runGitCommand(args,cwd,env) {
|
|||||||
var err = new Error(stderr);
|
var err = new Error(stderr);
|
||||||
err.stdout = stdout;
|
err.stdout = stdout;
|
||||||
err.stderr = stderr;
|
err.stderr = stderr;
|
||||||
if (/fatal: could not read/i.test(stderr)) {
|
if(/Connection refused/i.test(stderr)) {
|
||||||
|
err.code = "git_connection_failed";
|
||||||
|
} else if (/fatal: could not read/i.test(stderr)) {
|
||||||
// Username/Password
|
// Username/Password
|
||||||
err.code = "git_auth_failed";
|
err.code = "git_auth_failed";
|
||||||
} else if(/HTTP Basic: Access denied/i.test(stderr)) {
|
} else if(/HTTP Basic: Access denied/i.test(stderr)) {
|
||||||
@ -58,8 +60,6 @@ function runGitCommand(args,cwd,env) {
|
|||||||
} else if(/Host key verification failed/i.test(stderr)) {
|
} else if(/Host key verification failed/i.test(stderr)) {
|
||||||
// TODO: handle host key verification errors separately
|
// TODO: handle host key verification errors separately
|
||||||
err.code = "git_auth_failed";
|
err.code = "git_auth_failed";
|
||||||
} else if(/Connection refused/i.test(stderr)) {
|
|
||||||
err.code = "git_connection_failed";
|
|
||||||
} else if (/commit your changes or stash/i.test(stderr)) {
|
} else if (/commit your changes or stash/i.test(stderr)) {
|
||||||
err.code = "git_local_overwrite";
|
err.code = "git_local_overwrite";
|
||||||
} else if (/CONFLICT/.test(err.stdout)) {
|
} else if (/CONFLICT/.test(err.stdout)) {
|
||||||
|
@ -127,10 +127,20 @@ function init(_settings, _runtime) {
|
|||||||
activeProject = globalSettings.projects.activeProject;
|
activeProject = globalSettings.projects.activeProject;
|
||||||
}
|
}
|
||||||
if (settings.flowFile) {
|
if (settings.flowFile) {
|
||||||
|
// if flowFile is a known project name - use it
|
||||||
if (globalSettings.projects.projects.hasOwnProperty(settings.flowFile)) {
|
if (globalSettings.projects.projects.hasOwnProperty(settings.flowFile)) {
|
||||||
activeProject = settings.flowFile;
|
activeProject = settings.flowFile;
|
||||||
globalSettings.projects.activeProject = settings.flowFile;
|
globalSettings.projects.activeProject = settings.flowFile;
|
||||||
saveSettings = true;
|
saveSettings = true;
|
||||||
|
} else {
|
||||||
|
// if it resolves to a dir - use it... but:
|
||||||
|
// - where to get credsecret from?
|
||||||
|
// - what if the name clashes with a known project?
|
||||||
|
|
||||||
|
// var stat = fs.statSync(settings.flowFile);
|
||||||
|
// if (stat && stat.isDirectory()) {
|
||||||
|
// activeProject = settings.flowFile;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!activeProject) {
|
if (!activeProject) {
|
||||||
@ -148,6 +158,24 @@ function init(_settings, _runtime) {
|
|||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function listProjects() {
|
||||||
|
return fs.readdir(projectsDir).then(function(fns) {
|
||||||
|
var dirs = [];
|
||||||
|
fns.sort(function(A,B) {
|
||||||
|
return A.toLowerCase().localeCompare(B.toLowerCase());
|
||||||
|
}).filter(function(fn) {
|
||||||
|
var fullPath = fspath.join(projectsDir,fn);
|
||||||
|
if (fn[0] != ".") {
|
||||||
|
var stats = fs.lstatSync(fullPath);
|
||||||
|
if (stats.isDirectory()) {
|
||||||
|
dirs.push(fn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return dirs;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function getUserGitSettings(user) {
|
function getUserGitSettings(user) {
|
||||||
var userSettings = settings.getUserSettings(user)||{};
|
var userSettings = settings.getUserSettings(user)||{};
|
||||||
return userSettings.git;
|
return userSettings.git;
|
||||||
@ -160,7 +188,11 @@ function getBackupFilename(filename) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function loadProject(name) {
|
function loadProject(name) {
|
||||||
return Projects.get(name).then(function(project) {
|
var projectPath = name;
|
||||||
|
if (projectPath.indexOf(fspath.sep) === -1) {
|
||||||
|
projectPath = fspath.join(projectsDir,name);
|
||||||
|
}
|
||||||
|
return Projects.load(projectPath).then(function(project) {
|
||||||
activeProject = project;
|
activeProject = project;
|
||||||
flowsFullPath = project.getFlowFile();
|
flowsFullPath = project.getFlowFile();
|
||||||
flowsFileBackup = project.getFlowFileBackup();
|
flowsFileBackup = project.getFlowFileBackup();
|
||||||
@ -170,26 +202,20 @@ function loadProject(name) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function listProjects(user) {
|
|
||||||
return Projects.list();
|
|
||||||
}
|
|
||||||
|
|
||||||
function getProject(user, name) {
|
function getProject(user, name) {
|
||||||
checkActiveProject(name);
|
checkActiveProject(name);
|
||||||
//return when.resolve(activeProject.info);
|
//return when.resolve(activeProject.info);
|
||||||
var username;
|
return Promise.resolve(activeProject.export());
|
||||||
if (!user) {
|
|
||||||
username = "_";
|
|
||||||
} else {
|
|
||||||
username = user.username;
|
|
||||||
}
|
|
||||||
return Projects.get(name).then(function(project) {
|
|
||||||
return project.toJSON();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteProject(user, name) {
|
function deleteProject(user, name) {
|
||||||
return Projects.delete(user, name);
|
if (activeProject && activeProject.name === name) {
|
||||||
|
var e = new Error("NLS: Can't delete the active project");
|
||||||
|
e.code = "cannot_delete_active_project";
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
var projectPath = fspath.join(projectsDir,name);
|
||||||
|
return Projects.delete(user, projectPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkActiveProject(project) {
|
function checkActiveProject(project) {
|
||||||
@ -347,6 +373,7 @@ function createProject(user, metadata) {
|
|||||||
metadata.files.oldCredentials = credentialsFile;
|
metadata.files.oldCredentials = credentialsFile;
|
||||||
metadata.files.credentialSecret = currentEncryptionKey;
|
metadata.files.credentialSecret = currentEncryptionKey;
|
||||||
}
|
}
|
||||||
|
metadata.path = fspath.join(projectsDir,metadata.name);
|
||||||
return Projects.create(user, metadata).then(function(p) {
|
return Projects.create(user, metadata).then(function(p) {
|
||||||
return setActiveProject(user, p.name);
|
return setActiveProject(user, p.name);
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
@ -479,6 +506,12 @@ function getFlows() {
|
|||||||
error.code = "project_empty";
|
error.code = "project_empty";
|
||||||
return when.reject(error);
|
return when.reject(error);
|
||||||
}
|
}
|
||||||
|
if (activeProject.missingFiles && activeProject.missingFiles.indexOf('package.json') !== -1) {
|
||||||
|
log.warn("Project missing package.json");
|
||||||
|
error = new Error("Project missing package.json");
|
||||||
|
error.code = "missing_package_file";
|
||||||
|
return when.reject(error);
|
||||||
|
}
|
||||||
if (!activeProject.getFlowFile()) {
|
if (!activeProject.getFlowFile()) {
|
||||||
log.warn("Project has no flow file");
|
log.warn("Project has no flow file");
|
||||||
error = new Error("Project has no flow file");
|
error = new Error("Project has no flow file");
|
||||||
|
@ -80,27 +80,26 @@ module.exports = {
|
|||||||
*/
|
*/
|
||||||
writeFile: function(path,content,backupPath) {
|
writeFile: function(path,content,backupPath) {
|
||||||
if (backupPath) {
|
if (backupPath) {
|
||||||
try {
|
if (fs.existsSync(path)) {
|
||||||
fs.renameSync(path,backupPath);
|
fs.renameSync(path,backupPath);
|
||||||
} catch(err) {
|
}
|
||||||
}
|
}
|
||||||
}
|
return when.promise(function(resolve,reject) {
|
||||||
return when.promise(function(resolve,reject) {
|
var stream = fs.createWriteStream(path);
|
||||||
var stream = fs.createWriteStream(path);
|
stream.on('open',function(fd) {
|
||||||
stream.on('open',function(fd) {
|
stream.write(content,'utf8',function() {
|
||||||
stream.write(content,'utf8',function() {
|
fs.fsync(fd,function(err) {
|
||||||
fs.fsync(fd,function(err) {
|
if (err) {
|
||||||
if (err) {
|
log.warn(log._("storage.localfilesystem.fsync-fail",{path: path, message: err.toString()}));
|
||||||
log.warn(log._("storage.localfilesystem.fsync-fail",{path: path, message: err.toString()}));
|
}
|
||||||
}
|
stream.end(resolve);
|
||||||
stream.end(resolve);
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
stream.on('error',function(err) {
|
||||||
stream.on('error',function(err) {
|
reject(err);
|
||||||
reject(err);
|
});
|
||||||
});
|
});
|
||||||
});
|
|
||||||
},
|
},
|
||||||
readFile: readFile,
|
readFile: readFile,
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ module.exports = {
|
|||||||
uiPort: process.env.PORT || 1880,
|
uiPort: process.env.PORT || 1880,
|
||||||
|
|
||||||
// By default, the Node-RED UI accepts connections on all IPv4 interfaces.
|
// By default, the Node-RED UI accepts connections on all IPv4 interfaces.
|
||||||
|
// To listen on all IPv6 addresses, set uiHost to "::",
|
||||||
// The following property can be used to listen on a specific interface. For
|
// The following property can be used to listen on a specific interface. For
|
||||||
// example, the following would only allow connections from the local machine.
|
// example, the following would only allow connections from the local machine.
|
||||||
//uiHost: "127.0.0.1",
|
//uiHost: "127.0.0.1",
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var sentimentNode = require("../../../../nodes/core/analysis/72-sentiment.js");
|
var sentimentNode = require("../../../../nodes/core/analysis/72-sentiment.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('sentiment Node', function() {
|
describe('sentiment Node', function() {
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var injectNode = require("../../../../nodes/core/core/20-inject.js");
|
var injectNode = require("../../../../nodes/core/core/20-inject.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('inject node', function() {
|
describe('inject node', function() {
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var catchNode = require("../../../../nodes/core/core/25-catch.js");
|
var catchNode = require("../../../../nodes/core/core/25-catch.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('catch Node', function() {
|
describe('catch Node', function() {
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var catchNode = require("../../../../nodes/core/core/25-status.js");
|
var catchNode = require("../../../../nodes/core/core/25-status.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('status Node', function() {
|
describe('status Node', function() {
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var debugNode = require("../../../../nodes/core/core/58-debug.js");
|
var debugNode = require("../../../../nodes/core/core/58-debug.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
var WebSocket = require('ws');
|
var WebSocket = require('ws');
|
||||||
|
|
||||||
describe('debug node', function() {
|
describe('debug node', function() {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var linkNode = require("../../../../nodes/core/core/60-link.js");
|
var linkNode = require("../../../../nodes/core/core/60-link.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('link Node', function() {
|
describe('link Node', function() {
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var sinon = require("sinon");
|
var sinon = require("sinon");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
var execNode = require("../../../../nodes/core/core/75-exec.js");
|
var execNode = require("../../../../nodes/core/core/75-exec.js");
|
||||||
var osType = require("os").type();
|
var osType = require("os").type();
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var functionNode = require("../../../../nodes/core/core/80-function.js");
|
var functionNode = require("../../../../nodes/core/core/80-function.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('function node', function() {
|
describe('function node', function() {
|
||||||
|
|
||||||
@ -242,7 +242,7 @@ describe('function node', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle and log script error', function(done) {
|
it('should handle and log script error', function(done) {
|
||||||
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"retunr"}];
|
var flow = [{id:"n1",type:"function",wires:[["n2"]],func:"var a = 1;\nretunr"}];
|
||||||
helper.load(functionNode, flow, function() {
|
helper.load(functionNode, flow, function() {
|
||||||
var n1 = helper.getNode("n1");
|
var n1 = helper.getNode("n1");
|
||||||
n1.receive({payload:"foo",topic: "bar"});
|
n1.receive({payload:"foo",topic: "bar"});
|
||||||
@ -256,7 +256,7 @@ describe('function node', function() {
|
|||||||
msg.should.have.property('level', helper.log().ERROR);
|
msg.should.have.property('level', helper.log().ERROR);
|
||||||
msg.should.have.property('id', 'n1');
|
msg.should.have.property('id', 'n1');
|
||||||
msg.should.have.property('type', 'function');
|
msg.should.have.property('type', 'function');
|
||||||
msg.should.have.property('msg', 'ReferenceError: retunr is not defined (line 1, col 1)');
|
msg.should.have.property('msg', 'ReferenceError: retunr is not defined (line 2, col 1)');
|
||||||
done();
|
done();
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
done(err);
|
done(err);
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var templateNode = require("../../../../nodes/core/core/80-template.js");
|
var templateNode = require("../../../../nodes/core/core/80-template.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('template node', function() {
|
describe('template node', function() {
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
var should = require("should");
|
var should = require("should");
|
||||||
|
|
||||||
var delayNode = require("../../../../nodes/core/core/89-delay.js");
|
var delayNode = require("../../../../nodes/core/core/89-delay.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
var GRACE_PERCENTAGE=10;
|
var GRACE_PERCENTAGE=10;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var sinon = require("sinon");
|
var sinon = require("sinon");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
var triggerNode = require("../../../../nodes/core/core/89-trigger.js");
|
var triggerNode = require("../../../../nodes/core/core/89-trigger.js");
|
||||||
var RED = require("../../../../red/red.js");
|
var RED = require("../../../../red/red.js");
|
||||||
|
|
||||||
@ -419,7 +419,7 @@ describe('trigger node', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to extend the delay (but with no 2nd output)', function(done) {
|
it('should be able to extend the delay (but with no 2nd output)', function(done) {
|
||||||
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"pay", op2type:"nul", op1:"false", op2:"true", duration:"50", wires:[["n2"]] },
|
var flow = [{"id":"n1", "type":"trigger", "name":"triggerNode", extend:"true", op1type:"pay", op2type:"nul", op1:"false", op2:"true", duration:"100", wires:[["n2"]] },
|
||||||
{id:"n2", type:"helper"} ];
|
{id:"n2", type:"helper"} ];
|
||||||
helper.load(triggerNode, flow, function() {
|
helper.load(triggerNode, flow, function() {
|
||||||
var n1 = helper.getNode("n1");
|
var n1 = helper.getNode("n1");
|
||||||
@ -434,7 +434,7 @@ describe('trigger node', function() {
|
|||||||
else {
|
else {
|
||||||
msg.should.have.a.property("payload", "World");
|
msg.should.have.a.property("payload", "World");
|
||||||
//console.log(Date.now() - ss);
|
//console.log(Date.now() - ss);
|
||||||
(Date.now() - ss).should.be.greaterThan(70);
|
(Date.now() - ss).should.be.greaterThan(140);
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -447,7 +447,7 @@ describe('trigger node', function() {
|
|||||||
},20);
|
},20);
|
||||||
setTimeout( function() {
|
setTimeout( function() {
|
||||||
n1.emit("input", {payload:"World"});
|
n1.emit("input", {payload:"World"});
|
||||||
},80);
|
},150);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var commentNode = require("../../../../nodes/core/core/90-comment.js");
|
var commentNode = require("../../../../nodes/core/core/90-comment.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('comment Node', function() {
|
describe('comment Node', function() {
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var unknown = require("../../../../nodes/core/core/98-unknown.js");
|
var unknown = require("../../../../nodes/core/core/98-unknown.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('unknown Node', function() {
|
describe('unknown Node', function() {
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@
|
|||||||
var ws = require("ws");
|
var ws = require("ws");
|
||||||
var when = require("when");
|
var when = require("when");
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
var websocketNode = require("../../../../nodes/core/io/22-websocket.js");
|
var websocketNode = require("../../../../nodes/core/io/22-websocket.js");
|
||||||
|
|
||||||
var sockets = [];
|
var sockets = [];
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
var fs = require("fs-extra");
|
var fs = require("fs-extra");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
var watchNode = require("../../../../nodes/core/io/23-watch.js");
|
var watchNode = require("../../../../nodes/core/io/23-watch.js");
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
var net = require("net");
|
var net = require("net");
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var stoppable = require('stoppable');
|
var stoppable = require('stoppable');
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
var tcpinNode = require("../../../../nodes/core/io/31-tcpin.js");
|
var tcpinNode = require("../../../../nodes/core/io/31-tcpin.js");
|
||||||
|
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ describe('TCP in Node', function() {
|
|||||||
sock.end();
|
sock.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function startServer(done) {
|
function startServer(done) {
|
||||||
server_port += 1;
|
server_port += 1;
|
||||||
server = stoppable(net.createServer(function(c) {
|
server = stoppable(net.createServer(function(c) {
|
||||||
@ -59,7 +60,7 @@ describe('TCP in Node', function() {
|
|||||||
function stopServer(done) {
|
function stopServer(done) {
|
||||||
server.stop(done);
|
server.stop(done);
|
||||||
}
|
}
|
||||||
|
|
||||||
function send(wdata) {
|
function send(wdata) {
|
||||||
var opt = {port:port, host:"localhost"};
|
var opt = {port:port, host:"localhost"};
|
||||||
var client = net.createConnection(opt, function() {
|
var client = net.createConnection(opt, function() {
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
var net = require("net");
|
var net = require("net");
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var stoppable = require('stoppable');
|
var stoppable = require('stoppable');
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
var tcpinNode = require("../../../../nodes/core/io/31-tcpin.js");
|
var tcpinNode = require("../../../../nodes/core/io/31-tcpin.js");
|
||||||
|
|
||||||
|
|
||||||
@ -71,7 +71,7 @@ describe('TCP Request Node', function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should send & recv data', function(done) {
|
it('should send & recv data', function(done) {
|
||||||
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] },
|
var flow = [{id:"n1", type:"tcp request", server:"localhost", port:port, out:"time", splitc: "0", wires:[["n2"]] },
|
||||||
{id:"n2", type:"helper"}];
|
{id:"n2", type:"helper"}];
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var dgram = require("dgram");
|
var dgram = require("dgram");
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
var udpNode = require("../../../../nodes/core/io/32-udp.js");
|
var udpNode = require("../../../../nodes/core/io/32-udp.js");
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var dgram = require("dgram");
|
var dgram = require("dgram");
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
var udpNode = require("../../../../nodes/core/io/32-udp.js");
|
var udpNode = require("../../../../nodes/core/io/32-udp.js");
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
var should = require("should");
|
var should = require("should");
|
||||||
|
|
||||||
var switchNode = require("../../../../nodes/core/logic/10-switch.js");
|
var switchNode = require("../../../../nodes/core/logic/10-switch.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
var RED = require("../../../../red/red.js");
|
var RED = require("../../../../red/red.js");
|
||||||
|
|
||||||
describe('switch Node', function() {
|
describe('switch Node', function() {
|
||||||
@ -103,7 +103,7 @@ describe('switch Node', function() {
|
|||||||
helperNode1.on("input", function(msg) {
|
helperNode1.on("input", function(msg) {
|
||||||
try {
|
try {
|
||||||
if (shouldReceive === true) {
|
if (shouldReceive === true) {
|
||||||
msg.payload.should.equal(sendPayload);
|
should.equal(msg.payload,sendPayload);
|
||||||
done();
|
done();
|
||||||
} else {
|
} else {
|
||||||
should.fail(null, null, "We should never get an input!");
|
should.fail(null, null, "We should never get an input!");
|
||||||
@ -168,8 +168,6 @@ describe('switch Node', function() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
it('should check if payload equals given value', function(done) {
|
it('should check if payload equals given value', function(done) {
|
||||||
genericSwitchTest("eq", "Hello", true, true, "Hello", done);
|
genericSwitchTest("eq", "Hello", true, true, "Hello", done);
|
||||||
});
|
});
|
||||||
@ -258,6 +256,43 @@ describe('switch Node', function() {
|
|||||||
genericSwitchTest("regex", "[abc]+", true, true, "abbabac", done);
|
genericSwitchTest("regex", "[abc]+", true, true, "abbabac", done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should check if payload if of type string ', function(done) {
|
||||||
|
genericSwitchTest("istype", "string", true, true, "Hello", done);
|
||||||
|
});
|
||||||
|
it('should check if payload if of type number ', function(done) {
|
||||||
|
genericSwitchTest("istype", "number", true, true, 999, done);
|
||||||
|
});
|
||||||
|
it('should check if payload if of type number 0', function(done) {
|
||||||
|
genericSwitchTest("istype", "number", true, true, 0, done);
|
||||||
|
});
|
||||||
|
it('should check if payload if of type boolean true', function(done) {
|
||||||
|
genericSwitchTest("istype", "boolean", true, true, true, done);
|
||||||
|
});
|
||||||
|
it('should check if payload if of type boolean false', function(done) {
|
||||||
|
genericSwitchTest("istype", "boolean", true, true, true, done);
|
||||||
|
});
|
||||||
|
it('should check if payload if of type array ', function(done) {
|
||||||
|
genericSwitchTest("istype", "array", true, true, [1,2,3,"a","b"], done);
|
||||||
|
});
|
||||||
|
it('should check if payload if of type buffer ', function(done) {
|
||||||
|
genericSwitchTest("istype", "buffer", true, true, Buffer.from("Hello"), done);
|
||||||
|
});
|
||||||
|
it('should check if payload if of type object ', function(done) {
|
||||||
|
genericSwitchTest("istype", "object", true, true, {a:1,b:"b",c:true}, done);
|
||||||
|
});
|
||||||
|
it('should check if payload if of type JSON string ', function(done) {
|
||||||
|
genericSwitchTest("istype", "json", true, true, JSON.stringify({a:1,b:"b",c:true}), done);
|
||||||
|
});
|
||||||
|
it('should check if payload if of type JSON string (and fail if not) ', function(done) {
|
||||||
|
genericSwitchTest("istype", "json", true, false, "Hello", done);
|
||||||
|
});
|
||||||
|
it('should check if payload if of type null', function(done) {
|
||||||
|
genericSwitchTest("istype", "null", true, true, null, done);
|
||||||
|
});
|
||||||
|
it('should check if payload if of type undefined', function(done) {
|
||||||
|
genericSwitchTest("istype", "undefined", true, true, undefined, done);
|
||||||
|
});
|
||||||
|
|
||||||
it('should match regex with ignore-case flag set true', function(done) {
|
it('should match regex with ignore-case flag set true', function(done) {
|
||||||
var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"regex","v":"onetwothree","case":true}],checkall:true,outputs:1,wires:[["helperNode1"]]},
|
var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"regex","v":"onetwothree","case":true}],checkall:true,outputs:1,wires:[["helperNode1"]]},
|
||||||
{id:"helperNode1", type:"helper", wires:[]}];
|
{id:"helperNode1", type:"helper", wires:[]}];
|
||||||
@ -433,7 +468,6 @@ describe('switch Node', function() {
|
|||||||
var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"nnull"}],checkall:false,outputs:1,wires:[["helperNode1"]]},
|
var flow = [{id:"switchNode1",type:"switch",name:"switchNode",property:"payload",rules:[{"t":"nnull"}],checkall:false,outputs:1,wires:[["helperNode1"]]},
|
||||||
{id:"helperNode1", type:"helper", wires:[]}];
|
{id:"helperNode1", type:"helper", wires:[]}];
|
||||||
|
|
||||||
|
|
||||||
helper.load(switchNode, flow, function() {
|
helper.load(switchNode, flow, function() {
|
||||||
var switchNode1 = helper.getNode("switchNode1");
|
var switchNode1 = helper.getNode("switchNode1");
|
||||||
var helperNode1 = helper.getNode("helperNode1");
|
var helperNode1 = helper.getNode("helperNode1");
|
||||||
@ -787,5 +821,4 @@ describe('switch Node', function() {
|
|||||||
n1.receive({payload:1, parts:{index:0, count:4, id:222}});
|
n1.receive({payload:1, parts:{index:0, count:4, id:222}});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
var should = require("should");
|
var should = require("should");
|
||||||
|
|
||||||
var changeNode = require("../../../../nodes/core/logic/15-change.js");
|
var changeNode = require("../../../../nodes/core/logic/15-change.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('change Node', function() {
|
describe('change Node', function() {
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
var should = require("should");
|
var should = require("should");
|
||||||
|
|
||||||
var rangeNode = require("../../../../nodes/core/logic/16-range.js");
|
var rangeNode = require("../../../../nodes/core/logic/16-range.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('range Node', function() {
|
describe('range Node', function() {
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
var should = require("should");
|
var should = require("should");
|
||||||
var splitNode = require("../../../../nodes/core/logic/17-split.js");
|
var splitNode = require("../../../../nodes/core/logic/17-split.js");
|
||||||
var joinNode = require("../../../../nodes/core/logic/17-split.js");
|
var joinNode = require("../../../../nodes/core/logic/17-split.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
var RED = require("../../../../red/red.js");
|
var RED = require("../../../../red/red.js");
|
||||||
|
|
||||||
describe('SPLIT node', function() {
|
describe('SPLIT node', function() {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var sortNode = require("../../../../nodes/core/logic/18-sort.js");
|
var sortNode = require("../../../../nodes/core/logic/18-sort.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
var RED = require("../../../../red/red.js");
|
var RED = require("../../../../red/red.js");
|
||||||
|
|
||||||
describe('SORT node', function() {
|
describe('SORT node', function() {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var batchNode = require("../../../../nodes/core/logic/19-batch.js");
|
var batchNode = require("../../../../nodes/core/logic/19-batch.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
var RED = require("../../../../red/red.js");
|
var RED = require("../../../../red/red.js");
|
||||||
|
|
||||||
describe('BATCH node', function() {
|
describe('BATCH node', function() {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var csvNode = require("../../../../nodes/core/parsers/70-CSV.js");
|
var csvNode = require("../../../../nodes/core/parsers/70-CSV.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('CSV node', function() {
|
describe('CSV node', function() {
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ var path = require("path");
|
|||||||
var fs = require('fs-extra');
|
var fs = require('fs-extra');
|
||||||
|
|
||||||
var htmlNode = require("../../../../nodes/core/parsers/70-HTML.js");
|
var htmlNode = require("../../../../nodes/core/parsers/70-HTML.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('html node', function() {
|
describe('html node', function() {
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ describe('html node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should retrieve header contents if asked to by msg.select - alternative property', function(done) {
|
it('should retrieve header contents if asked to by msg.select - alternative in property', function(done) {
|
||||||
fs.readFile(file, 'utf8', function(err, data) {
|
fs.readFile(file, 'utf8', function(err, data) {
|
||||||
var flow = [{id:"n1",type:"html",property:"foo",wires:[["n2"]],func:"return msg;"},
|
var flow = [{id:"n1",type:"html",property:"foo",wires:[["n2"]],func:"return msg;"},
|
||||||
{id:"n2", type:"helper"}];
|
{id:"n2", type:"helper"}];
|
||||||
@ -79,7 +79,7 @@ describe('html node', function() {
|
|||||||
var n2 = helper.getNode("n2");
|
var n2 = helper.getNode("n2");
|
||||||
n2.on("input", function(msg) {
|
n2.on("input", function(msg) {
|
||||||
msg.should.have.property('topic', 'bar');
|
msg.should.have.property('topic', 'bar');
|
||||||
should.equal(msg.foo, 'This is a test page for node 70-HTML');
|
msg.foo[0].should.equal('This is a test page for node 70-HTML');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
n1.receive({foo:data,topic:"bar",select:"h1"});
|
n1.receive({foo:data,topic:"bar",select:"h1"});
|
||||||
@ -87,6 +87,24 @@ describe('html node', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should retrieve header contents if asked to by msg.select - alternative in and out properties', function(done) {
|
||||||
|
fs.readFile(file, 'utf8', function(err, data) {
|
||||||
|
var flow = [{id:"n1",type:"html",property:"foo",outproperty:"bar",tag:"h1",wires:[["n2"]],func:"return msg;"},
|
||||||
|
{id:"n2", type:"helper"}];
|
||||||
|
|
||||||
|
helper.load(htmlNode, flow, function() {
|
||||||
|
var n1 = helper.getNode("n1");
|
||||||
|
var n2 = helper.getNode("n2");
|
||||||
|
n2.on("input", function(msg) {
|
||||||
|
msg.should.have.property('topic', 'bar');
|
||||||
|
msg.bar[0].should.equal('This is a test page for node 70-HTML');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
n1.receive({foo:data,topic:"bar"});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should emit an empty array if no matching elements', function(done) {
|
it('should emit an empty array if no matching elements', function(done) {
|
||||||
fs.readFile(file, 'utf8', function(err, data) {
|
fs.readFile(file, 'utf8', function(err, data) {
|
||||||
var flow = [{id:"n1",type:"html",wires:[["n2"]],func:"return msg;"},
|
var flow = [{id:"n1",type:"html",wires:[["n2"]],func:"return msg;"},
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var jsonNode = require("../../../../nodes/core/parsers/70-JSON.js");
|
var jsonNode = require("../../../../nodes/core/parsers/70-JSON.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('JSON node', function() {
|
describe('JSON node', function() {
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var xmlNode = require("../../../../nodes/core/parsers/70-XML.js");
|
var xmlNode = require("../../../../nodes/core/parsers/70-XML.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('XML node', function() {
|
describe('XML node', function() {
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
var should = require("should");
|
var should = require("should");
|
||||||
var yamlNode = require("../../../../nodes/core/parsers/70-YAML.js");
|
var yamlNode = require("../../../../nodes/core/parsers/70-YAML.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('YAML node', function() {
|
describe('YAML node', function() {
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ var os = require('os');
|
|||||||
var fs = require('fs-extra');
|
var fs = require('fs-extra');
|
||||||
var sinon = require('sinon');
|
var sinon = require('sinon');
|
||||||
var tailNode = require("../../../../nodes/core/storage/28-tail.js");
|
var tailNode = require("../../../../nodes/core/storage/28-tail.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('tail Node', function() {
|
describe('tail Node', function() {
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ var fs = require('fs-extra');
|
|||||||
var os = require('os');
|
var os = require('os');
|
||||||
var sinon = require("sinon");
|
var sinon = require("sinon");
|
||||||
var fileNode = require("../../../../nodes/core/storage/50-file.js");
|
var fileNode = require("../../../../nodes/core/storage/50-file.js");
|
||||||
var helper = require("../../helper.js");
|
var helper = require("node-red-node-test-helper");
|
||||||
|
|
||||||
describe('file Nodes', function() {
|
describe('file Nodes', function() {
|
||||||
|
|
||||||
|
@ -1,169 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright JS Foundation and other contributors, http://js.foundation
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
**/
|
|
||||||
|
|
||||||
var should = require("should");
|
|
||||||
var sinon = require("sinon");
|
|
||||||
var when = require("when");
|
|
||||||
var request = require('supertest');
|
|
||||||
var express = require("express");
|
|
||||||
var stoppable = require('stoppable');
|
|
||||||
var nock;
|
|
||||||
if (!process.version.match(/^v0\.[0-9]\./)) {
|
|
||||||
// only set nock for node >= 0.10
|
|
||||||
try {
|
|
||||||
nock = require('nock');
|
|
||||||
} catch (err) {
|
|
||||||
// nevermind, will skip nock tests
|
|
||||||
nock = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var RED = require("../../red/red.js");
|
|
||||||
var redNodes = require("../../red/runtime/nodes");
|
|
||||||
var flows = require("../../red/runtime/nodes/flows");
|
|
||||||
var credentials = require("../../red/runtime/nodes/credentials");
|
|
||||||
var comms = require("../../red/api/editor/comms.js");
|
|
||||||
var log = require("../../red/util/log.js");
|
|
||||||
var context = require("../../red/runtime/nodes/context.js");
|
|
||||||
var events = require("../../red/runtime/events.js");
|
|
||||||
|
|
||||||
var http = require('http');
|
|
||||||
var express = require('express');
|
|
||||||
var app = express();
|
|
||||||
|
|
||||||
var address = '127.0.0.1';
|
|
||||||
var listenPort = 0; // use ephemeral port
|
|
||||||
var port;
|
|
||||||
var url;
|
|
||||||
var logSpy;
|
|
||||||
var server;
|
|
||||||
|
|
||||||
function helperNode(n) {
|
|
||||||
RED.nodes.createNode(this, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
load: function(testNode, testFlows, testCredentials, cb) {
|
|
||||||
var i;
|
|
||||||
|
|
||||||
logSpy = sinon.spy(log,"log");
|
|
||||||
logSpy.FATAL = log.FATAL;
|
|
||||||
logSpy.ERROR = log.ERROR;
|
|
||||||
logSpy.WARN = log.WARN;
|
|
||||||
logSpy.INFO = log.INFO;
|
|
||||||
logSpy.DEBUG = log.DEBUG;
|
|
||||||
logSpy.TRACE = log.TRACE;
|
|
||||||
logSpy.METRIC = log.METRIC;
|
|
||||||
|
|
||||||
if (typeof testCredentials === 'function') {
|
|
||||||
cb = testCredentials;
|
|
||||||
testCredentials = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
var storage = {
|
|
||||||
getFlows: function() {
|
|
||||||
return when.resolve({flows:testFlows,credentials:testCredentials});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var settings = {
|
|
||||||
available: function() { return false; }
|
|
||||||
};
|
|
||||||
|
|
||||||
var red = {};
|
|
||||||
for (i in RED) {
|
|
||||||
if (RED.hasOwnProperty(i) && !/^(init|start|stop)$/.test(i)) {
|
|
||||||
var propDescriptor = Object.getOwnPropertyDescriptor(RED,i);
|
|
||||||
Object.defineProperty(red,i,propDescriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
red["_"] = function(messageId) {
|
|
||||||
return messageId;
|
|
||||||
};
|
|
||||||
|
|
||||||
redNodes.init({events:events,settings:settings, storage:storage,log:log,});
|
|
||||||
RED.nodes.registerType("helper", helperNode);
|
|
||||||
if (Array.isArray(testNode)) {
|
|
||||||
for (i = 0; i < testNode.length; i++) {
|
|
||||||
testNode[i](red);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
testNode(red);
|
|
||||||
}
|
|
||||||
flows.load().then(function() {
|
|
||||||
flows.startFlows();
|
|
||||||
should.deepEqual(testFlows, flows.getFlows().flows);
|
|
||||||
cb();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
unload: function() {
|
|
||||||
// TODO: any other state to remove between tests?
|
|
||||||
redNodes.clearRegistry();
|
|
||||||
logSpy.restore();
|
|
||||||
context.clean({allNodes:[]});
|
|
||||||
return flows.stopFlows();
|
|
||||||
},
|
|
||||||
|
|
||||||
getNode: function(id) {
|
|
||||||
return flows.get(id);
|
|
||||||
},
|
|
||||||
|
|
||||||
credentials: credentials,
|
|
||||||
|
|
||||||
clearFlows: function() {
|
|
||||||
return flows.stopFlows();
|
|
||||||
},
|
|
||||||
|
|
||||||
request: function() {
|
|
||||||
return request(RED.httpAdmin);
|
|
||||||
},
|
|
||||||
|
|
||||||
startServer: function(done) {
|
|
||||||
server = stoppable(http.createServer(function(req,res) { app(req,res); }), 0);
|
|
||||||
RED.init(server, {
|
|
||||||
SKIP_BUILD_CHECK: true,
|
|
||||||
logging:{console:{level:'off'}}
|
|
||||||
});
|
|
||||||
server.listen(listenPort, address);
|
|
||||||
server.on('listening', function() {
|
|
||||||
port = server.address().port;
|
|
||||||
url = 'http://' + address + ':' + port;
|
|
||||||
comms.start();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
//TODO consider saving TCP handshake/server reinit on start/stop/start sequences
|
|
||||||
stopServer: function(done) {
|
|
||||||
if (server) {
|
|
||||||
try {
|
|
||||||
comms.stop();
|
|
||||||
server.stop(done);
|
|
||||||
} catch(e) {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
url: function() { return url; },
|
|
||||||
|
|
||||||
nock: nock,
|
|
||||||
|
|
||||||
log: function() { return logSpy;}
|
|
||||||
};
|
|
@ -45,6 +45,28 @@ describe("runtime-api/settings", function() {
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
|
before(function() {
|
||||||
|
sinon.stub(theme,"settings",function() { return { test: 456 };});
|
||||||
|
app = express();
|
||||||
|
app.get("/settings",info.runtimeSettings);
|
||||||
|
app.get("/settingsWithUser",function(req,res,next) {
|
||||||
|
req.user = {
|
||||||
|
username: "nick",
|
||||||
|
permissions: "*",
|
||||||
|
image: "http://example.com",
|
||||||
|
anonymous: false,
|
||||||
|
private: "secret"
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
},info.runtimeSettings);
|
||||||
|
});
|
||||||
|
after(function() {
|
||||||
|
theme.settings.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
it('returns the filtered settings', function(done) {
|
it('returns the filtered settings', function(done) {
|
||||||
info.init({
|
info.init({
|
||||||
settings: {
|
settings: {
|
||||||
@ -77,6 +99,42 @@ describe("runtime-api/settings", function() {
|
|||||||
res.body.should.have.property("testNodeSetting","helloWorld");
|
res.body.should.have.property("testNodeSetting","helloWorld");
|
||||||
res.body.should.not.have.property("foo",123);
|
res.body.should.not.have.property("foo",123);
|
||||||
res.body.should.have.property("flowEncryptionType","test-key-type");
|
res.body.should.have.property("flowEncryptionType","test-key-type");
|
||||||
|
res.body.should.not.have.property("user");
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('returns the filtered user in settings', function(done) {
|
||||||
|
info.init({
|
||||||
|
settings: {
|
||||||
|
foo: 123,
|
||||||
|
httpNodeRoot: "testHttpNodeRoot",
|
||||||
|
version: "testVersion",
|
||||||
|
paletteCategories :["red","blue","green"],
|
||||||
|
exportNodeSettings: function(obj) {
|
||||||
|
obj.testNodeSetting = "helloWorld";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodes: {
|
||||||
|
paletteEditorEnabled: function() { return true; },
|
||||||
|
getCredentialKeyType: function() { return "test-key-type"}
|
||||||
|
},
|
||||||
|
log: { error: console.error },
|
||||||
|
storage: {}
|
||||||
|
});
|
||||||
|
request(app)
|
||||||
|
.get("/settingsWithUser")
|
||||||
|
.expect(200)
|
||||||
|
.end(function(err,res) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
res.body.should.have.property("user");
|
||||||
|
res.body.user.should.have.property("username","nick");
|
||||||
|
res.body.user.should.have.property("permissions","*");
|
||||||
|
res.body.user.should.have.property("image","http://example.com");
|
||||||
|
res.body.user.should.have.property("anonymous",false);
|
||||||
|
res.body.user.should.not.have.property("private");
|
||||||
|
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
20
test/resources/ssl/server.crt
Normal file
20
test/resources/ssl/server.crt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDMDCCAhgCCQDPGPyu5M6ZaDANBgkqhkiG9w0BAQsFADBZMQswCQYDVQQGEwJB
|
||||||
|
VTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0
|
||||||
|
cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhvc3QwIBcNMTgwMzE2MDY0ODU1WhgP
|
||||||
|
MjExODAyMjAwNjQ4NTVaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0
|
||||||
|
YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMM
|
||||||
|
CWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMifGelM
|
||||||
|
k/b3HeIj98y9P5jS+Qblqpq7+gsCaL+qglMFmG0QXe6Ordkrh3xeY0uTkaFatwLM
|
||||||
|
WMzoX60nNdaVjC9U9RlQLK/3nncCveexxRUGtI8VpxN04ivBE/ULhtJeStQFrfyt
|
||||||
|
LWr1WWf8o8P/EWzZnh0Y1oHc0XqhOPHu9Nfd9kn5nfHNd/xbY8KXa4DkVSJ1lLFK
|
||||||
|
3t/nSWttchF8zKgNpoQznNGqUTjT28l0sS8fyH76DyRj3Ke6xdNxX2NRUU0PnGFI
|
||||||
|
RMsBG4Qrzo5xY7lQP7uVVgZUlxryw+NuZuC1PBXaUKJOf6CGwrTq5WB9zF1iBZCs
|
||||||
|
wD68NvtLd0kHEgECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAfqNOg2v90r5x4lFo
|
||||||
|
SYmPUoX24gdwHd/mfCzDJksB8n98X1eULYZqqRF2Q7LMkYu/twxfR3EKQX1HZxQY
|
||||||
|
LpGUYX4ubJdVTy13opJs8B4NkhvRuOAP0+b7RVt4RfuxLX9tYOB98tEbf7Mj0ccq
|
||||||
|
F4sHi+PMCh64K7rNWECHar0F51yNtNXcxJPMuHZVmj0/U7h6ZxNf+GzdTi8YKmVy
|
||||||
|
5OHI7xol/II/v3QOi1L+BaEIUkqYODKuQouJVIzu4zX6JRfAaxwjJmliYoJm7OEY
|
||||||
|
dFMEQUw1Ggsos+KbkGi9mCDbveYpWcZTR8nfPwmx+oJtt47DTHUC3KSdRxgtfjGs
|
||||||
|
otmVSw==
|
||||||
|
-----END CERTIFICATE-----
|
27
test/resources/ssl/server.key
Normal file
27
test/resources/ssl/server.key
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAyJ8Z6UyT9vcd4iP3zL0/mNL5BuWqmrv6CwJov6qCUwWYbRBd
|
||||||
|
7o6t2SuHfF5jS5ORoVq3AsxYzOhfrSc11pWML1T1GVAsr/eedwK957HFFQa0jxWn
|
||||||
|
E3TiK8ET9QuG0l5K1AWt/K0tavVZZ/yjw/8RbNmeHRjWgdzReqE48e701932Sfmd
|
||||||
|
8c13/FtjwpdrgORVInWUsUre3+dJa21yEXzMqA2mhDOc0apRONPbyXSxLx/IfvoP
|
||||||
|
JGPcp7rF03FfY1FRTQ+cYUhEywEbhCvOjnFjuVA/u5VWBlSXGvLD425m4LU8FdpQ
|
||||||
|
ok5/oIbCtOrlYH3MXWIFkKzAPrw2+0t3SQcSAQIDAQABAoIBAGmbryUrpZxU24tG
|
||||||
|
idRiLw9Ax8yEq7lGiMqw2vlCRdZ0VJfdDMVeoE945ZpniXeoV/oLadl0Pq6nCG56
|
||||||
|
/JFYKfJkk51eoheDjwxxCgzkfK2j2PqVWF0ao1CLE/ljtvYYouVXlA42D3mFbCoc
|
||||||
|
SQ0MwVx+dgg1If48gp0+L17T/ll/VOOQumts5UzoKC8YABLL00g5ZL9/jZlVipgl
|
||||||
|
HfENMPWOfy3q5kSgQqvTWTMdSE6644ryV890mrwcC/RzqQBSNgRh1Lqx3jcXQSdN
|
||||||
|
x5C19gEK60hZqcvzBkKYudMHUC6I0lcuao1xwBnHUQIVKmLFPZBUIQq3tVar/YUc
|
||||||
|
d65cJpECgYEA5D9QilQpHxv875wBvBOEbyt9TqDBBN/5JpGQ9sBKpA0eNp3UzrOr
|
||||||
|
+n0TlyoDZYjkxgNJScS4TpeKde1Hk5j2kkMngjS69dn4G6wmOI79gAOGrCiJd1/I
|
||||||
|
AWb09KxUKlWBbfKuLHdl1wSMCYQornDdXxYCxhv9sMZKbEJ//tsI420CgYEA4QPf
|
||||||
|
n/dRAm+6zwNQTWOYWlj5jsG1TilBBCtoRqUqVlrAgR6rS1lgOleHkVrWH0g0Lkmh
|
||||||
|
9DxWiWuNNXxdU/5zx9AQn/JuHuL8EjDLN5r7idcg2LtEElCkr12y0I9nzS2OOZnj
|
||||||
|
MTioIh+hghzNuk09NlVJrHi48bJUVL/6Ws7ruGUCgYBT9UJAD+MscVQiI2Wz9A30
|
||||||
|
ArBOOu2lSGnSmRsU2PjbzYN+naIJAqhRNK7/HNIxCCD3AYB05SrSpgWliUmZ7ltM
|
||||||
|
w+0FhTX8d1g/fZx1k4uGCkYAj8y5H39nnKKgWb9/7wH0Gp+c9bJ9XEvSuE1qlVOo
|
||||||
|
xWTx0JwJ6Xa4yeFhMtrbJQKBgF/FfErjwvEciRBPQsCNoWzi7eUbAYYw/OE/cHSR
|
||||||
|
HAIBQmoymYnKkrCCTMtLNFPAMaV55ZrEi7iVtFaNhlOXu8PSBSFu1/wBdHRxnC0g
|
||||||
|
o+s5S1uz6Pc6p72UTeWDBBVKTHyryQ1MJhPQDrgIdm/TLDiR+HeWMnF9C3O++lno
|
||||||
|
NGAZAoGBAKhsmatxVD9B3jvUDd/CWhXVDSZQECrfJ+Uy1q6b5NO5yMibpIZv14Nj
|
||||||
|
VT+b2qXoO1wL6htTRJXXNPmrB/JtrLiLg/vxVuA7CPSgot8SDA+/lbRhf1n/SKnD
|
||||||
|
ECXrEUmq28SgBItbY4vcy5PVEHRvlzqO/LpD6Y7iGNpR7zw9Yk3b
|
||||||
|
-----END RSA PRIVATE KEY-----
|
Loading…
Reference in New Issue
Block a user