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
|
||||
|
||||
Projects
|
||||
|
@ -31,7 +31,7 @@ done
|
||||
# Find the real location of this script
|
||||
CURRENT_PATH=`pwd`
|
||||
SCRIPT_PATH="${BASH_SOURCE[0]}";
|
||||
while([ -h "${SCRIPT_PATH}" ]); do
|
||||
while [ -h "${SCRIPT_PATH}" ]; do
|
||||
cd "`dirname "${SCRIPT_PATH}"`"
|
||||
SCRIPT_PATH="$(readlink "`basename "${SCRIPT_PATH}"`")";
|
||||
done
|
||||
|
@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
**/
|
||||
$(function() {
|
||||
|
||||
$(function() {
|
||||
if ((window.location.hostname !== "localhost") && (window.location.hostname !== "127.0.0.1")) {
|
||||
document.title = document.title+" : "+window.location.hostname;
|
||||
}
|
||||
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") {
|
||||
if (RED.user.hasPermission("projects.write")) {
|
||||
options.buttons = [
|
||||
|
@ -208,6 +208,8 @@ RED.palette.editor = (function() {
|
||||
if (nodeEntry) {
|
||||
var activeTypeCount = 0;
|
||||
var typeCount = 0;
|
||||
var errorCount = 0;
|
||||
nodeEntry.errorList.empty();
|
||||
nodeEntries[module].totalUseCount = 0;
|
||||
nodeEntries[module].setUseCount = {};
|
||||
|
||||
@ -216,7 +218,10 @@ RED.palette.editor = (function() {
|
||||
var inUseCount = 0;
|
||||
var set = moduleInfo.sets[setName];
|
||||
var setElements = nodeEntry.sets[setName];
|
||||
|
||||
if (set.err) {
|
||||
errorCount++;
|
||||
$("<li>").text(set.err).appendTo(nodeEntry.errorList);
|
||||
}
|
||||
if (set.enabled) {
|
||||
activeTypeCount += set.types.length;
|
||||
}
|
||||
@ -255,6 +260,13 @@ RED.palette.editor = (function() {
|
||||
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;
|
||||
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);
|
||||
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 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 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);
|
||||
@ -620,6 +635,8 @@ RED.palette.editor = (function() {
|
||||
updateButton: updateButton,
|
||||
removeButton: removeButton,
|
||||
enableButton: enableButton,
|
||||
errorRow: errorRow,
|
||||
errorList: errorList,
|
||||
setCount: setCount,
|
||||
container: container,
|
||||
shade: shade,
|
||||
@ -651,7 +668,6 @@ RED.palette.editor = (function() {
|
||||
typeSwatches[t] = $('<span>',{class:"palette-module-type-swatch"}).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);
|
||||
enableButton.click(function(evt) {
|
||||
evt.preventDefault();
|
||||
|
@ -1255,6 +1255,14 @@ RED.projects.settings = (function() {
|
||||
text: 'Delete remote',
|
||||
click: function() {
|
||||
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 options = {
|
||||
url: url,
|
||||
@ -1276,6 +1284,7 @@ RED.projects.settings = (function() {
|
||||
activeProject.git.remotes[name] = remote;
|
||||
});
|
||||
}
|
||||
delete activeProject.git.branches.remoteAlt;
|
||||
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("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;
|
||||
},
|
||||
buttons: [
|
||||
@ -90,13 +110,6 @@ RED.projects = (function() {
|
||||
createProjectOptions = {};
|
||||
$( 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.email = gitEmailInput.val();
|
||||
RED.settings.set('git', currentGitSettings);
|
||||
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() {
|
||||
var projectFlowFileInput;
|
||||
var projectCredentialFileInput;
|
||||
@ -1127,6 +1504,7 @@ RED.projects = (function() {
|
||||
}
|
||||
|
||||
$(".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");
|
||||
@ -1873,6 +2251,43 @@ RED.projects = (function() {
|
||||
createProjectOptions = {};
|
||||
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) {
|
||||
$.getJSON("projects",function(data) {
|
||||
@ -1952,6 +2367,7 @@ RED.projects = (function() {
|
||||
RED.projects.settings.show('deps');
|
||||
},
|
||||
createDefaultFileSet: createDefaultFileSet,
|
||||
createDefaultPackageFile: createDefaultPackageFile,
|
||||
// showSidebar: showSidebar,
|
||||
refresh: refresh,
|
||||
editProject: function() {
|
||||
|
@ -592,7 +592,10 @@ RED.sidebar.versionControl = (function() {
|
||||
closeBranchBox();
|
||||
localCommitListShade.show();
|
||||
$(this).addClass('selected');
|
||||
var activeProject = RED.projects.getActiveProject();
|
||||
$("#sidebar-version-control-repo-toolbar-set-upstream-row").toggle(!!activeProject.git.branches.remoteAlt);
|
||||
remoteBox.show();
|
||||
|
||||
setTimeout(function() {
|
||||
remoteBox.css("height","265px");
|
||||
},100);
|
||||
@ -868,7 +871,8 @@ RED.sidebar.versionControl = (function() {
|
||||
if (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"
|
||||
}
|
||||
utils.sendRequest({
|
||||
@ -880,6 +884,10 @@ RED.sidebar.versionControl = (function() {
|
||||
// done(error,null);
|
||||
},
|
||||
200: function(data) {
|
||||
if (setUpstream && activeProject.git.branches.remoteAlt) {
|
||||
activeProject.git.branches.remote = activeProject.git.branches.remoteAlt;
|
||||
delete activeProject.git.branches.remoteAlt;
|
||||
}
|
||||
refresh(true);
|
||||
closeRemoteBox();
|
||||
},
|
||||
@ -928,6 +936,10 @@ RED.sidebar.versionControl = (function() {
|
||||
// done(error,null);
|
||||
},
|
||||
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);
|
||||
closeRemoteBox();
|
||||
},
|
||||
|
@ -49,7 +49,12 @@
|
||||
.palette-module-version {
|
||||
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;
|
||||
}
|
||||
}
|
||||
.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 {
|
||||
@include shade;
|
||||
text-align: center;
|
||||
|
@ -139,16 +139,17 @@
|
||||
}
|
||||
}
|
||||
button.editor-button {
|
||||
width: calc(50% - 40px);
|
||||
width: calc(50% - 80px);
|
||||
margin: 20px;
|
||||
height: 175px;
|
||||
height: auto;
|
||||
line-height: 2em;
|
||||
font-size: 1.5em !important;
|
||||
padding: 10px;
|
||||
border-color: #aaa;
|
||||
i {
|
||||
color: #ccc;
|
||||
color: #aaa;
|
||||
}
|
||||
&:hover i {
|
||||
color: #aaa;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.button-group {
|
||||
@ -160,7 +161,6 @@
|
||||
button.projects-dialog-screen-create-type {
|
||||
height: auto;
|
||||
padding: 10px;
|
||||
|
||||
}
|
||||
.button-group {
|
||||
text-align: center;
|
||||
|
@ -204,7 +204,14 @@ module.exports = function(RED) {
|
||||
}
|
||||
var context = vm.createContext(sandbox);
|
||||
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) {
|
||||
try {
|
||||
var start = process.hrtime();
|
||||
@ -219,6 +226,13 @@ module.exports = function(RED) {
|
||||
this.status({fill:"yellow",shape:"dot",text:""+converted});
|
||||
}
|
||||
} 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 errorMessage;
|
||||
|
@ -212,48 +212,84 @@
|
||||
<input type="password" id="node-config-input-password">
|
||||
</div>
|
||||
</div>
|
||||
<div id="mqtt-broker-tab-birth" style="display:none">
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-birthTopic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
|
||||
<input type="text" id="node-config-input-birthTopic" data-i18n="[placeholder]mqtt.placeholder.birth-topic">
|
||||
<div id="mqtt-broker-tab-messages" style="display:none">
|
||||
<div id="mqtt-broker-section-birth">
|
||||
<div class="palette-header">
|
||||
<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 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:125px !important">
|
||||
<option value="0">0</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
</select>
|
||||
<i class="fa fa-history"></i> <span data-i18n="mqtt.retain"></span> <select id="node-config-input-birthRetain" style="width:125px !important">
|
||||
<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 for="node-config-input-birthPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
|
||||
<input type="text" id="node-config-input-birthPayload" data-i18n="[placeholder]common.label.payload">
|
||||
</div>
|
||||
</div>
|
||||
<div id="mqtt-broker-tab-will" style="display:none">
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-willTopic"><i class="fa fa-tasks"></i> <span data-i18n="common.label.topic"></span></label>
|
||||
<input type="text" id="node-config-input-willTopic" data-i18n="[placeholder]mqtt.placeholder.will-topic">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-config-input-willQos"><i class="fa fa-empire"></i> <span data-i18n="mqtt.label.qos"></span></label>
|
||||
<select id="node-config-input-willQos" style="width:125px !important">
|
||||
<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>
|
||||
<i class="fa fa-history"></i> <span data-i18n="mqtt.retain"></span> <select id="node-config-input-willRetain" style="width:125px !important">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="mqtt-broker-section-close">
|
||||
<div class="palette-header">
|
||||
<i class="fa fa-angle-down"></i><span data-i18n="mqtt.sections-label.close-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-closeTopic"><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-closeTopic" style="width:300px" data-i18n="[placeholder]mqtt.placeholder.close-topic">
|
||||
<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>
|
||||
<select id="node-config-input-closeRetain" 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 for="node-config-input-willPayload"><i class="fa fa-envelope"></i> <span data-i18n="common.label.payload"></span></label>
|
||||
<input type="text" id="node-config-input-willPayload" data-i18n="[placeholder]common.label.payload">
|
||||
<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 id="mqtt-broker-section-will">
|
||||
<div class="palette-header">
|
||||
<i class="fa fa-angle-down"></i><span data-i18n="mqtt.sections-label.will-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-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">
|
||||
<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>
|
||||
<select id="node-config-input-willRetain" 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-willPayload"><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-willPayload" style="width:300px" data-i18n="[placeholder]common.label.payload">
|
||||
<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>
|
||||
<select id="node-config-input-willQos" 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>
|
||||
@ -269,6 +305,9 @@
|
||||
<h4>Birth Message</h4>
|
||||
<p>This is a message that will be published on the configured topic whenever the
|
||||
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>
|
||||
<p>This is a message that will be published by the broker in the event the node
|
||||
unexpectedly loses its connection.</p>
|
||||
@ -300,14 +339,18 @@
|
||||
compatmode: { value: true},
|
||||
keepalive: {value:60,validate:RED.validators.number()},
|
||||
cleansession: {value: true},
|
||||
willTopic: {value:""},
|
||||
willQos: {value:"0"},
|
||||
willRetain: {value:false},
|
||||
willPayload: {value:""},
|
||||
birthTopic: {value:""},
|
||||
birthQos: {value:"0"},
|
||||
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: {
|
||||
user: {type:"text"},
|
||||
@ -343,14 +386,39 @@
|
||||
id: "mqtt-broker-tab-security",
|
||||
label: this._("mqtt.tabs-label.security")
|
||||
});
|
||||
|
||||
tabs.addTab({
|
||||
id: "mqtt-broker-tab-birth",
|
||||
label: this._("mqtt.tabs-label.birth")
|
||||
id: "mqtt-broker-tab-messages",
|
||||
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);
|
||||
if (typeof this.cleansession === 'undefined') {
|
||||
this.cleansession = true;
|
||||
@ -368,14 +436,18 @@
|
||||
this.keepalive = 15;
|
||||
$("#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') {
|
||||
this.birthQos = "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() {
|
||||
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) {
|
||||
this.username = this.credentials.user;
|
||||
this.password = this.credentials.password;
|
||||
@ -314,6 +323,10 @@ module.exports = function(RED) {
|
||||
this.on('close', function(done) {
|
||||
this.closing = true;
|
||||
if (this.connected) {
|
||||
// Send close message
|
||||
if (node.closeMessage) {
|
||||
node.publish(node.closeMessage);
|
||||
}
|
||||
this.client.once('close', function() {
|
||||
done();
|
||||
});
|
||||
|
@ -29,7 +29,7 @@
|
||||
</div>
|
||||
<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>
|
||||
<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 class="form-row">
|
||||
<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) {
|
||||
"use strict";
|
||||
var os = require('os');
|
||||
var dgram = require('dgram');
|
||||
var udpInputPortsInUse = {};
|
||||
|
||||
@ -30,6 +31,29 @@ module.exports = function(RED) {
|
||||
this.ipv = n.ipv || "udp4";
|
||||
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};
|
||||
if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
|
||||
var server;
|
||||
@ -39,7 +63,7 @@ module.exports = function(RED) {
|
||||
udpInputPortsInUse[this.port] = server;
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
@ -121,12 +145,38 @@ module.exports = function(RED) {
|
||||
this.ipv = n.ipv || "udp4";
|
||||
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};
|
||||
if (process.version.indexOf("v0.10") === 0) { opts = node.ipv; }
|
||||
|
||||
var sock;
|
||||
if (udpInputPortsInUse[this.outport || this.port]) {
|
||||
sock = udpInputPortsInUse[this.outport || this.port];
|
||||
var p = 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 {
|
||||
sock = dgram.createSocket(opts); // default to udp4
|
||||
@ -136,18 +186,16 @@ module.exports = function(RED) {
|
||||
// prevent it going to the global error handler and shutting node-red
|
||||
// down.
|
||||
});
|
||||
udpInputPortsInUse[this.outport || this.port] = sock;
|
||||
}
|
||||
udpInputPortsInUse[p] = sock;
|
||||
|
||||
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.setBroadcast(true); // turn on broadcast
|
||||
if (node.multicast == "multi") {
|
||||
try {
|
||||
sock.setMulticastTTL(128);
|
||||
sock.addMembership(node.addr,node.iface); // Add to the multicast group
|
||||
node.log(RED._("udp.status.mc-ready",{outport:node.outport,host:node.addr,port:node.port}));
|
||||
node.log(RED._("udp.status.mc-ready",{iface:node.iface,outport:node.outport,host:node.addr,port:node.port}));
|
||||
} catch (e) {
|
||||
if (e.errno == "EINVAL") {
|
||||
node.error(RED._("udp.errors.bad-mcaddress"));
|
||||
@ -167,6 +215,7 @@ module.exports = function(RED) {
|
||||
} else {
|
||||
node.log(RED._("udp.status.ready-nolocal",{host:node.addr,port:node.port}));
|
||||
}
|
||||
}
|
||||
|
||||
node.on("input", function(msg) {
|
||||
if (msg.hasOwnProperty("payload")) {
|
||||
@ -198,8 +247,8 @@ module.exports = function(RED) {
|
||||
});
|
||||
|
||||
node.on("close", function() {
|
||||
if (udpInputPortsInUse.hasOwnProperty(node.outport || node.port)) {
|
||||
delete udpInputPortsInUse[node.outport || node.port];
|
||||
if (udpInputPortsInUse.hasOwnProperty(p)) {
|
||||
delete udpInputPortsInUse[p];
|
||||
}
|
||||
try {
|
||||
sock.close();
|
||||
|
@ -323,6 +323,7 @@
|
||||
"broker": "Server",
|
||||
"example": "e.g. localhost",
|
||||
"qos": "QoS",
|
||||
"retain": "Retain",
|
||||
"clientid": "Client ID",
|
||||
"port": "Port",
|
||||
"keepalive": "Keep alive time (s)",
|
||||
@ -332,17 +333,22 @@
|
||||
"verify-server-cert":"Verify server certificate",
|
||||
"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": {
|
||||
"connection": "Connection",
|
||||
"security": "Security",
|
||||
"will": "Will Message",
|
||||
"birth": "Birth Message"
|
||||
"messages": "Messages"
|
||||
},
|
||||
"placeholder": {
|
||||
"clientid": "Leave blank for auto generated",
|
||||
"clientid-nonclean":"Must be set for non-clean sessions",
|
||||
"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": {
|
||||
"connected": "Connected to broker: __broker__",
|
||||
@ -495,15 +501,15 @@
|
||||
"using": "using",
|
||||
"output": "Output",
|
||||
"group": "Group",
|
||||
"interface": "Local IP",
|
||||
"interfaceprompt": "(optional) local ip address to bind to",
|
||||
"interface": "Local IF",
|
||||
"send": "Send a",
|
||||
"toport": "to port",
|
||||
"address": "Address",
|
||||
"decode-base64": "Decode Base64 encoded payload?"
|
||||
},
|
||||
"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"
|
||||
},
|
||||
"udpmsgs": "udp messages",
|
||||
@ -531,10 +537,11 @@
|
||||
"mc-group": "udp multicast group __group__",
|
||||
"listener-stopped": "udp listener 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__",
|
||||
"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": {
|
||||
"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",
|
||||
"port-notset": "udp: port not set",
|
||||
"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": {
|
||||
@ -566,6 +574,7 @@
|
||||
"false":"is false",
|
||||
"null":"is null",
|
||||
"nnull":"is not null",
|
||||
"istype":"is of type",
|
||||
"head":"head",
|
||||
"tail":"tail",
|
||||
"index":"index between",
|
||||
@ -671,7 +680,8 @@
|
||||
"html": {
|
||||
"label": {
|
||||
"select": "Selector",
|
||||
"output": "Output"
|
||||
"output": "Output",
|
||||
"in": "in"
|
||||
},
|
||||
"output": {
|
||||
"html": "the html content of the elements",
|
||||
|
@ -327,17 +327,22 @@
|
||||
"verify-server-cert": "サーバの証明書を確認",
|
||||
"compatmode": "旧MQTT 3.1のサポート"
|
||||
},
|
||||
"sections-label":{
|
||||
"birth-message": "接続時の送信メッセージ(Birthメッセージ)",
|
||||
"will-message":"予期しない切断時の送信メッセージ(Willメッセージ)",
|
||||
"close-message":"切断前の送信メッセージ(Closeメッセージ)"
|
||||
},
|
||||
"tabs-label": {
|
||||
"connection": "接続",
|
||||
"security": "セキュリティ",
|
||||
"will": "Willメッセージ",
|
||||
"birth": "Birthメッセージ"
|
||||
"messages": "メッセージ"
|
||||
},
|
||||
"placeholder": {
|
||||
"clientid": "IDを自動生成する場合は、無記入にしてください",
|
||||
"clientid-nonclean": "新規ではないセッションを設定してください",
|
||||
"will-topic": "Willメッセージを無効化する場合は、無記入にしてください",
|
||||
"birth-topic": "Birthメッセージを無効化する場合は、無記入にしてください"
|
||||
"birth-topic": "Birthメッセージを無効化する場合は、無記入にしてください",
|
||||
"close-topic": "Closeメッセージを無効化する場合は、無記入にしてください"
|
||||
},
|
||||
"state": {
|
||||
"connected": "ブローカへ接続しました: __broker__",
|
||||
@ -488,14 +493,14 @@
|
||||
"output": "出力",
|
||||
"group": "グループ",
|
||||
"interface": "ローカルIP",
|
||||
"interfaceprompt": "(任意) 使用するローカルIPアドレス",
|
||||
"send": "送信",
|
||||
"toport": "ポート",
|
||||
"address": "アドレス",
|
||||
"decode-base64": "Base64形式のペイロードを復号"
|
||||
},
|
||||
"placeholder": {
|
||||
"interface": "(任意) eth0のIPアドレス",
|
||||
"interface": "(任意) 使用するローカルインターフェイスもしくはアドレス",
|
||||
"interfaceprompt": "(任意) 使用するローカルインターフェイスもしくはアドレス",
|
||||
"address": "宛先IPアドレス"
|
||||
},
|
||||
"udpmsgs": "UDPメッセージ",
|
||||
@ -523,10 +528,11 @@
|
||||
"mc-group": "udpノードがグループ __group__ へマルチキャストしました",
|
||||
"listener-stopped": "udpノードが待ち受けを停止しました",
|
||||
"output-stopped": "udpノードが出力を停止しました",
|
||||
"mc-ready": "udpノードはマルチキャストの準備ができています: __outport__ -> __host__:__port__",
|
||||
"mc-ready": "udpノードはマルチキャストの準備ができています: __iface__:__outport__ -> __host__:__port__",
|
||||
"bc-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": {
|
||||
"access-error": "UDP接続エラー 管理者権限で1024未満のポート番号にアクセスできる必要があります",
|
||||
@ -536,6 +542,8 @@
|
||||
"ip-notset": "udp: IPアドレスが設定されていません",
|
||||
"port-notset": "udp: ポートが設定されていません",
|
||||
"port-invalid": "udp: ポート番号が不正です",
|
||||
"alreadyused": "udp: 既に__port__番ポートが使用されています",
|
||||
"ifnotfound": "udp: インターフェイス __iface__ がありません",
|
||||
"alreadyused": "udp: 既にポートが使用されています"
|
||||
}
|
||||
},
|
||||
@ -661,7 +669,8 @@
|
||||
"html": {
|
||||
"label": {
|
||||
"select": "抽出する要素",
|
||||
"output": "出力"
|
||||
"output": "出力",
|
||||
"in": "対象:"
|
||||
},
|
||||
"output": {
|
||||
"html": "要素内のHTML",
|
||||
@ -761,7 +770,9 @@
|
||||
"status": {
|
||||
"stopped": "停止",
|
||||
"closed": "切断",
|
||||
"not-running": "停止中"
|
||||
"not-running": "停止中",
|
||||
"not-available": "利用不可",
|
||||
"na": "N/A : __value__"
|
||||
},
|
||||
"errors": {
|
||||
"ignorenode": "Raspberry Pi固有のノードを無視しました",
|
||||
|
@ -525,7 +525,8 @@
|
||||
"mc-ready": "udp 组播已准备好: __outport__ -> __host__:__port__",
|
||||
"bc-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": {
|
||||
"access-error": "UDP 访问错误, 你可能需要root权限才能接入1024以下的端口",
|
||||
|
@ -85,6 +85,7 @@
|
||||
{v:"false",t:"switch.rules.false",kind:'V'},
|
||||
{v:"null",t:"switch.rules.null",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:"index",t:"switch.rules.index",kind:'S'},
|
||||
{v:"tail",t:"switch.rules.tail",kind:'S'},
|
||||
@ -160,6 +161,7 @@
|
||||
var selectField = rule.find("select");
|
||||
var type = selectField.val()||"";
|
||||
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 expField = rule.find(".node-input-rule-exp-value");
|
||||
var btwnField1 = rule.find(".node-input-rule-btwn-value");
|
||||
@ -180,6 +182,8 @@
|
||||
numField.typedInput("width",(newWidth-selectWidth-70));
|
||||
} else if (type === "jsonata_exp") {
|
||||
expField.typedInput("width",(newWidth-selectWidth-70));
|
||||
} else if (type === "istype") {
|
||||
typeField.typedInput("width",(newWidth-selectWidth-70));
|
||||
} else {
|
||||
if (type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else") {
|
||||
// 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 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 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);
|
||||
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);
|
||||
@ -243,26 +259,39 @@
|
||||
valueField.typedInput('hide');
|
||||
expValueField.typedInput('hide');
|
||||
numValueField.typedInput('hide');
|
||||
typeValueField.typedInput('hide');
|
||||
btwnValueField.typedInput('show');
|
||||
} else if ((type === "head") || (type === "tail")) {
|
||||
btwnValueField.typedInput('hide');
|
||||
btwnValue2Field.typedInput('hide');
|
||||
expValueField.typedInput('hide');
|
||||
numValueField.typedInput('show');
|
||||
typeValueField.typedInput('hide');
|
||||
valueField.typedInput('hide');
|
||||
} else if (type === "jsonata_exp") {
|
||||
btwnValueField.typedInput('hide');
|
||||
btwnValue2Field.typedInput('hide');
|
||||
expValueField.typedInput('show');
|
||||
numValueField.typedInput('hide');
|
||||
typeValueField.typedInput('hide');
|
||||
valueField.typedInput('hide');
|
||||
} else {
|
||||
btwnValueField.typedInput('hide');
|
||||
expValueField.typedInput('hide');
|
||||
numValueField.typedInput('hide');
|
||||
typeValueField.typedInput('hide');
|
||||
valueField.typedInput('hide');
|
||||
if (type === "true" || type === "false" || type === "null" || type === "nnull" || type === "else") {
|
||||
valueField.typedInput('hide');
|
||||
} else {
|
||||
typeValueField.typedInput('hide');
|
||||
}
|
||||
else
|
||||
if (type === "istype") {
|
||||
valueField.typedInput('hide');
|
||||
typeValueField.typedInput('show');
|
||||
}
|
||||
else {
|
||||
typeValueField.typedInput('hide');
|
||||
valueField.typedInput('show');
|
||||
}
|
||||
}
|
||||
@ -287,6 +316,9 @@
|
||||
} else if ((rule.t === "head") || (rule.t === "tail")) {
|
||||
numValueField.typedInput('value',rule.v);
|
||||
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") {
|
||||
expValueField.typedInput('value',rule.v);
|
||||
expValueField.typedInput('type',rule.vt||'jsonata');
|
||||
@ -359,6 +391,9 @@
|
||||
} else if ((type === "head") || (type === "tail")) {
|
||||
r.v = rule.find(".node-input-rule-num-value").typedInput('value');
|
||||
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") {
|
||||
r.v = rule.find(".node-input-rule-exp-value").typedInput('value');
|
||||
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; },
|
||||
'null': 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) {
|
||||
var count = Number(b);
|
||||
return (parts.index < count);
|
||||
@ -292,6 +302,10 @@ module.exports = function(RED) {
|
||||
node.error(RED._("switch.errors.invalid-expr",{error:err.message}));
|
||||
return;
|
||||
}
|
||||
} else if (rule.vt === 'json') {
|
||||
v1 = "json";
|
||||
} else if (rule.vt === 'null') {
|
||||
v1 = "null";
|
||||
} else {
|
||||
try {
|
||||
v1 = RED.util.evaluateNodeProperty(rule.v,rule.vt,node,msg);
|
||||
|
@ -2,7 +2,7 @@
|
||||
<script type="text/x-red" data-template-name="html">
|
||||
<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>
|
||||
<input type="text" id="node-input-property" style="width:70%;"/>
|
||||
<input type="text" id="node-input-property" style="width:70%">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<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>
|
||||
</select>
|
||||
</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/>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
@ -59,6 +63,7 @@
|
||||
defaults: {
|
||||
name: {value:""},
|
||||
property: {value:"payload"},
|
||||
outproperty: {value:"payload"},
|
||||
tag: {value:""},
|
||||
ret: {value:"html"},
|
||||
as: {value:"single"}
|
||||
@ -74,6 +79,7 @@
|
||||
},
|
||||
oneditprepare: function() {
|
||||
$("#node-input-property").typedInput({default:'msg',types:['msg']});
|
||||
$("#node-input-outproperty").typedInput({default:'msg',types:['msg']});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -21,6 +21,7 @@ module.exports = function(RED) {
|
||||
function CheerioNode(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
this.property = n.property||"payload";
|
||||
this.outproperty = n.outproperty||this.property||"payload";
|
||||
this.tag = n.tag;
|
||||
this.ret = n.ret || "html";
|
||||
this.as = n.as || "single";
|
||||
@ -48,7 +49,7 @@ module.exports = function(RED) {
|
||||
/* istanbul ignore else */
|
||||
if (pay2) {
|
||||
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 = {
|
||||
id: msg._msgid,
|
||||
index: index,
|
||||
@ -68,7 +69,7 @@ module.exports = function(RED) {
|
||||
index++;
|
||||
});
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
22
package.json
22
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red",
|
||||
"version": "0.18.4",
|
||||
"version": "0.18.5",
|
||||
"description": "A visual tool for wiring the Internet of Things",
|
||||
"homepage": "http://nodered.org",
|
||||
"license": "Apache-2.0",
|
||||
@ -43,22 +43,22 @@
|
||||
"cookie-parser": "1.4.3",
|
||||
"cors": "2.8.4",
|
||||
"cron": "1.3.0",
|
||||
"express": "4.16.2",
|
||||
"express": "4.16.3",
|
||||
"express-session": "1.15.6",
|
||||
"follow-redirects": "1.3.0",
|
||||
"follow-redirects": "1.4.1",
|
||||
"fs-extra": "5.0.0",
|
||||
"fs.notify": "0.0.4",
|
||||
"hash-sum": "1.0.2",
|
||||
"i18next": "1.10.6",
|
||||
"ink-docstrap": "^1.3.2",
|
||||
"is-utf8": "0.2.1",
|
||||
"js-yaml": "3.10.0",
|
||||
"js-yaml": "3.11.0",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"jsonata": "1.5.0",
|
||||
"jsonata": "1.5.3",
|
||||
"media-typer": "0.3.0",
|
||||
"memorystore": "1.6.0",
|
||||
"mime": "1.4.1",
|
||||
"mqtt": "2.15.1",
|
||||
"mqtt": "2.17.0",
|
||||
"multer": "1.3.0",
|
||||
"mustache": "2.3.0",
|
||||
"node-red-node-email": "0.1.*",
|
||||
@ -71,10 +71,10 @@
|
||||
"passport": "0.4.0",
|
||||
"passport-http-bearer": "1.0.1",
|
||||
"passport-oauth2-client-password": "0.1.2",
|
||||
"raw-body": "2.3.2",
|
||||
"semver": "5.4.1",
|
||||
"raw-body": "2.3.3",
|
||||
"semver": "5.5.0",
|
||||
"sentiment": "2.1.0",
|
||||
"uglify-js": "3.3.6",
|
||||
"uglify-js": "3.3.24",
|
||||
"when": "3.7.8",
|
||||
"ws": "1.1.5",
|
||||
"xml2js": "0.4.19"
|
||||
@ -103,6 +103,7 @@
|
||||
"grunt-sass": "~2.0.0",
|
||||
"grunt-simple-mocha": "~0.4.1",
|
||||
"grunt-webdriver": "^2.0.3",
|
||||
"http-proxy": "^1.16.2",
|
||||
"ink-docstrap": "^1.3.2",
|
||||
"istanbul": "0.4.5",
|
||||
"mocha": "^5.1.1",
|
||||
@ -113,7 +114,8 @@
|
||||
"wdio-chromedriver-service": "^0.1.1",
|
||||
"wdio-mocha-framework": "^0.5.11",
|
||||
"wdio-spec-reporter": "^0.1.3",
|
||||
"webdriverio": "^4.9.11"
|
||||
"webdriverio": "^4.9.11",
|
||||
"node-red-node-test-helper": "^0.1.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
|
2
red.js
2
red.js
@ -276,7 +276,7 @@ function getListenPath() {
|
||||
}
|
||||
|
||||
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;
|
||||
if (settings.httpAdminRoot !== false) {
|
||||
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_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_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_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>"
|
||||
|
@ -68,10 +68,17 @@ var api = module.exports = {
|
||||
try {
|
||||
var safeSettings = {
|
||||
httpNodeRoot: runtime.settings.httpNodeRoot||"/",
|
||||
version: runtime.settings.version,
|
||||
user: opts.user
|
||||
version: runtime.settings.version
|
||||
}
|
||||
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)) {
|
||||
safeSettings.paletteCategories = runtime.settings.paletteCategories;
|
||||
}
|
||||
|
@ -237,8 +237,8 @@ function Flow(global,flow) {
|
||||
|
||||
this.handleError = function(node,logMessage,msg) {
|
||||
var count = 1;
|
||||
if (msg && msg.hasOwnProperty("error")) {
|
||||
if (msg.error.hasOwnProperty("source")) {
|
||||
if (msg && msg.hasOwnProperty("error") && msg.error !== null) {
|
||||
if (msg.error.hasOwnProperty("source") && msg.error.source !== null) {
|
||||
if (msg.error.source.id === node.id) {
|
||||
count = msg.error.source.count+1;
|
||||
if (count === 10) {
|
||||
|
@ -53,15 +53,13 @@ function getGitUser(user) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function Project(name) {
|
||||
this.name = name;
|
||||
this.path = fspath.join(projectsDir,name);
|
||||
function Project(path) {
|
||||
this.path = path;
|
||||
this.name = fspath.basename(path);
|
||||
this.paths = {};
|
||||
this.files = {};
|
||||
this.auth = {origin:{}};
|
||||
|
||||
this.missingFiles = [];
|
||||
|
||||
this.credentialSecret = null;
|
||||
}
|
||||
Project.prototype.load = function () {
|
||||
@ -70,7 +68,9 @@ Project.prototype.load = function () {
|
||||
// console.log(globalProjectSettings)
|
||||
var projectSettings = {};
|
||||
if (globalProjectSettings) {
|
||||
projectSettings = globalProjectSettings.projects[this.name]||{};
|
||||
if (globalProjectSettings.projects.hasOwnProperty(this.name)) {
|
||||
projectSettings = globalProjectSettings.projects[this.name] || {};
|
||||
}
|
||||
}
|
||||
|
||||
this.credentialSecret = projectSettings.credentialSecret;
|
||||
@ -81,9 +81,7 @@ Project.prototype.load = function () {
|
||||
|
||||
var promises = [];
|
||||
return checkProjectFiles(project).then(function(missingFiles) {
|
||||
if (missingFiles.length > 0) {
|
||||
project.missingFiles = missingFiles;
|
||||
}
|
||||
if (missingFiles.indexOf('package.json') === -1) {
|
||||
project.paths['package.json'] = fspath.join(project.path,"package.json");
|
||||
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) {
|
||||
var project = this;
|
||||
if (!this.empty) {
|
||||
throw new Error("Cannot initialise non-empty project");
|
||||
}
|
||||
// if (!this.empty) {
|
||||
// throw new Error("Cannot initialise non-empty project");
|
||||
// }
|
||||
var files = Object.keys(defaultFileSet);
|
||||
var promises = [];
|
||||
|
||||
@ -148,6 +146,8 @@ Project.prototype.initialise = function(user,data) {
|
||||
promises.push(settings.set('projects',projects));
|
||||
}
|
||||
|
||||
if (data.hasOwnProperty('files')) {
|
||||
if (data.files.hasOwnProperty('flow') && data.files.hasOwnProperty('credentials')) {
|
||||
project.files.flow = data.files.flow;
|
||||
project.files.credentials = data.files.credentials;
|
||||
var flowFilePath = fspath.join(project.path,project.files.flow);
|
||||
@ -156,9 +156,15 @@ Project.prototype.initialise = function(user,data) {
|
||||
promises.push(util.writeFile(credsFilePath,"{}"));
|
||||
files.push(project.files.flow);
|
||||
files.push(project.files.credentials);
|
||||
}
|
||||
}
|
||||
for (var file in defaultFileSet) {
|
||||
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());
|
||||
}
|
||||
|
||||
Project.prototype.toJSON = function () {
|
||||
Project.prototype.export = function () {
|
||||
|
||||
return {
|
||||
name: this.name,
|
||||
@ -780,28 +786,18 @@ function getBackupFilename(filename) {
|
||||
return fspath.join(ffDir,"."+ffName+".backup");
|
||||
}
|
||||
|
||||
function checkProjectExists(project) {
|
||||
var projectPath = fspath.join(projectsDir,project);
|
||||
function checkProjectExists(projectPath) {
|
||||
return fs.pathExists(projectPath).then(function(exists) {
|
||||
if (!exists) {
|
||||
var e = new Error("Project not found: "+project);
|
||||
var e = new Error("Project not found");
|
||||
e.code = "project_not_found";
|
||||
e.project = project;
|
||||
var name = fspath.basename(projectPath);
|
||||
e.project = name;
|
||||
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) {
|
||||
var projectPath = fspath.join(projectsDir,project.name);
|
||||
// Create a basic skeleton of a project
|
||||
@ -904,17 +900,23 @@ function createProject(user, metadata) {
|
||||
} else {
|
||||
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 projectPath = metadata.path;
|
||||
return new Promise(function(resolve,reject) {
|
||||
var projectPath = fspath.join(projectsDir,project);
|
||||
fs.stat(projectPath, function(err,stat) {
|
||||
if (!err) {
|
||||
var e = new Error("NLS: Project already exists");
|
||||
e.code = "project_exists";
|
||||
return reject(e);
|
||||
}
|
||||
createProjectDirectory(project).then(function() {
|
||||
fs.ensureDir(projectPath).then(function() {
|
||||
var projects = settings.get('projects');
|
||||
if (!projects) {
|
||||
projects = {
|
||||
@ -951,7 +953,7 @@ function createProject(user, metadata) {
|
||||
return createDefaultProject(user, metadata);
|
||||
}
|
||||
}).then(function() {
|
||||
resolve(getProject(project))
|
||||
resolve(loadProject(projectPath))
|
||||
}).catch(function(err) {
|
||||
fs.remove(projectPath,function() {
|
||||
reject(err);
|
||||
@ -961,50 +963,21 @@ function createProject(user, metadata) {
|
||||
})
|
||||
}
|
||||
|
||||
function deleteProject(user, name) {
|
||||
return checkProjectExists(name).then(function() {
|
||||
if (currentProject && currentProject.name === name) {
|
||||
var e = new Error("NLS: Can't delete the active project");
|
||||
e.code = "cannot_delete_active_project";
|
||||
throw e;
|
||||
}
|
||||
else {
|
||||
return deleteProjectDirectory(name).then(function() {
|
||||
function deleteProject(user, projectPath) {
|
||||
return checkProjectExists(projectPath).then(function() {
|
||||
return fs.remove(projectPath).then(function() {
|
||||
var name = fspath.basename(projectPath);
|
||||
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 = {
|
||||
init: init,
|
||||
get: getProject,
|
||||
load: loadProject,
|
||||
create: createProject,
|
||||
delete: deleteProject,
|
||||
list: listProjects
|
||||
|
||||
delete: deleteProject
|
||||
}
|
||||
|
@ -48,7 +48,9 @@ function runGitCommand(args,cwd,env) {
|
||||
var err = new Error(stderr);
|
||||
err.stdout = stdout;
|
||||
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
|
||||
err.code = "git_auth_failed";
|
||||
} 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)) {
|
||||
// TODO: handle host key verification errors separately
|
||||
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)) {
|
||||
err.code = "git_local_overwrite";
|
||||
} else if (/CONFLICT/.test(err.stdout)) {
|
||||
|
@ -127,10 +127,20 @@ function init(_settings, _runtime) {
|
||||
activeProject = globalSettings.projects.activeProject;
|
||||
}
|
||||
if (settings.flowFile) {
|
||||
// if flowFile is a known project name - use it
|
||||
if (globalSettings.projects.projects.hasOwnProperty(settings.flowFile)) {
|
||||
activeProject = settings.flowFile;
|
||||
globalSettings.projects.activeProject = settings.flowFile;
|
||||
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) {
|
||||
@ -148,6 +158,24 @@ function init(_settings, _runtime) {
|
||||
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) {
|
||||
var userSettings = settings.getUserSettings(user)||{};
|
||||
return userSettings.git;
|
||||
@ -160,7 +188,11 @@ function getBackupFilename(filename) {
|
||||
}
|
||||
|
||||
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;
|
||||
flowsFullPath = project.getFlowFile();
|
||||
flowsFileBackup = project.getFlowFileBackup();
|
||||
@ -170,26 +202,20 @@ function loadProject(name) {
|
||||
})
|
||||
}
|
||||
|
||||
function listProjects(user) {
|
||||
return Projects.list();
|
||||
}
|
||||
|
||||
function getProject(user, name) {
|
||||
checkActiveProject(name);
|
||||
//return when.resolve(activeProject.info);
|
||||
var username;
|
||||
if (!user) {
|
||||
username = "_";
|
||||
} else {
|
||||
username = user.username;
|
||||
}
|
||||
return Projects.get(name).then(function(project) {
|
||||
return project.toJSON();
|
||||
});
|
||||
return Promise.resolve(activeProject.export());
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -347,6 +373,7 @@ function createProject(user, metadata) {
|
||||
metadata.files.oldCredentials = credentialsFile;
|
||||
metadata.files.credentialSecret = currentEncryptionKey;
|
||||
}
|
||||
metadata.path = fspath.join(projectsDir,metadata.name);
|
||||
return Projects.create(user, metadata).then(function(p) {
|
||||
return setActiveProject(user, p.name);
|
||||
}).then(function() {
|
||||
@ -479,6 +506,12 @@ function getFlows() {
|
||||
error.code = "project_empty";
|
||||
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()) {
|
||||
log.warn("Project has no flow file");
|
||||
error = new Error("Project has no flow file");
|
||||
|
@ -80,9 +80,8 @@ module.exports = {
|
||||
*/
|
||||
writeFile: function(path,content,backupPath) {
|
||||
if (backupPath) {
|
||||
try {
|
||||
if (fs.existsSync(path)) {
|
||||
fs.renameSync(path,backupPath);
|
||||
} catch(err) {
|
||||
}
|
||||
}
|
||||
return when.promise(function(resolve,reject) {
|
||||
|
@ -23,6 +23,7 @@ module.exports = {
|
||||
uiPort: process.env.PORT || 1880,
|
||||
|
||||
// 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
|
||||
// example, the following would only allow connections from the local machine.
|
||||
//uiHost: "127.0.0.1",
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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');
|
||||
|
||||
describe('debug node', function() {
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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 osType = require("os").type();
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
@ -242,7 +242,7 @@ describe('function node', function() {
|
||||
});
|
||||
|
||||
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() {
|
||||
var n1 = helper.getNode("n1");
|
||||
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('id', 'n1');
|
||||
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();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
var should = require("should");
|
||||
|
||||
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;
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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 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) {
|
||||
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"} ];
|
||||
helper.load(triggerNode, flow, function() {
|
||||
var n1 = helper.getNode("n1");
|
||||
@ -434,7 +434,7 @@ describe('trigger node', function() {
|
||||
else {
|
||||
msg.should.have.a.property("payload", "World");
|
||||
//console.log(Date.now() - ss);
|
||||
(Date.now() - ss).should.be.greaterThan(70);
|
||||
(Date.now() - ss).should.be.greaterThan(140);
|
||||
done();
|
||||
}
|
||||
}
|
||||
@ -447,7 +447,7 @@ describe('trigger node', function() {
|
||||
},20);
|
||||
setTimeout( function() {
|
||||
n1.emit("input", {payload:"World"});
|
||||
},80);
|
||||
},150);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@
|
||||
var ws = require("ws");
|
||||
var when = require("when");
|
||||
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 sockets = [];
|
||||
|
@ -17,7 +17,7 @@
|
||||
var fs = require("fs-extra");
|
||||
var path = require("path");
|
||||
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");
|
||||
|
||||
|
||||
|
@ -17,7 +17,8 @@
|
||||
var net = require("net");
|
||||
var should = require("should");
|
||||
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");
|
||||
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
var net = require("net");
|
||||
var should = require("should");
|
||||
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");
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var dgram = require("dgram");
|
||||
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");
|
||||
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var dgram = require("dgram");
|
||||
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");
|
||||
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
var should = require("should");
|
||||
|
||||
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");
|
||||
|
||||
describe('switch Node', function() {
|
||||
@ -103,7 +103,7 @@ describe('switch Node', function() {
|
||||
helperNode1.on("input", function(msg) {
|
||||
try {
|
||||
if (shouldReceive === true) {
|
||||
msg.payload.should.equal(sendPayload);
|
||||
should.equal(msg.payload,sendPayload);
|
||||
done();
|
||||
} else {
|
||||
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) {
|
||||
genericSwitchTest("eq", "Hello", true, true, "Hello", done);
|
||||
});
|
||||
@ -258,6 +256,43 @@ describe('switch Node', function() {
|
||||
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) {
|
||||
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:[]}];
|
||||
@ -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"]]},
|
||||
{id:"helperNode1", type:"helper", wires:[]}];
|
||||
|
||||
|
||||
helper.load(switchNode, flow, function() {
|
||||
var switchNode1 = helper.getNode("switchNode1");
|
||||
var helperNode1 = helper.getNode("helperNode1");
|
||||
@ -787,5 +821,4 @@ describe('switch Node', function() {
|
||||
n1.receive({payload:1, parts:{index:0, count:4, id:222}});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -17,7 +17,7 @@
|
||||
var should = require("should");
|
||||
|
||||
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() {
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
var should = require("should");
|
||||
|
||||
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() {
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
var should = require("should");
|
||||
var splitNode = 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");
|
||||
|
||||
describe('SPLIT node', function() {
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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");
|
||||
|
||||
describe('SORT node', function() {
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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");
|
||||
|
||||
describe('BATCH node', function() {
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
|
@ -19,7 +19,7 @@ var path = require("path");
|
||||
var fs = require('fs-extra');
|
||||
|
||||
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() {
|
||||
|
||||
@ -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) {
|
||||
var flow = [{id:"n1",type:"html",property:"foo",wires:[["n2"]],func:"return msg;"},
|
||||
{id:"n2", type:"helper"}];
|
||||
@ -79,7 +79,7 @@ describe('html node', function() {
|
||||
var n2 = helper.getNode("n2");
|
||||
n2.on("input", function(msg) {
|
||||
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();
|
||||
});
|
||||
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) {
|
||||
fs.readFile(file, 'utf8', function(err, data) {
|
||||
var flow = [{id:"n1",type:"html",wires:[["n2"]],func:"return msg;"},
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
var should = require("should");
|
||||
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() {
|
||||
|
||||
|
@ -20,7 +20,7 @@ var os = require('os');
|
||||
var fs = require('fs-extra');
|
||||
var sinon = require('sinon');
|
||||
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() {
|
||||
|
||||
|
@ -20,7 +20,7 @@ var fs = require('fs-extra');
|
||||
var os = require('os');
|
||||
var sinon = require("sinon");
|
||||
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() {
|
||||
|
||||
|
@ -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) {
|
||||
info.init({
|
||||
settings: {
|
||||
@ -77,6 +99,42 @@ describe("runtime-api/settings", function() {
|
||||
res.body.should.have.property("testNodeSetting","helloWorld");
|
||||
res.body.should.not.have.property("foo",123);
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
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