mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Allow a project's flow file to be changed
This commit is contained in:
parent
5218a3fbac
commit
3d6468326a
@ -69,10 +69,15 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.text) {
|
if (msg.text) {
|
||||||
|
console.log(msg);
|
||||||
var text = RED._(msg.text,{default:msg.text});
|
var text = RED._(msg.text,{default:msg.text});
|
||||||
if (notificationId === "runtime-state") {
|
if (notificationId === "runtime-state") {
|
||||||
if (msg.error === "credentials_load_failed") {
|
if (msg.error === "credentials_load_failed") {
|
||||||
|
// TODO: NLS
|
||||||
text += '<p><a href="#" onclick="RED.projects.showCredentialsPrompt(); return false;">'+'Setup credentials'+'</a></p>';
|
text += '<p><a href="#" onclick="RED.projects.showCredentialsPrompt(); return false;">'+'Setup credentials'+'</a></p>';
|
||||||
|
} else if (msg.error === "missing_flow_file") {
|
||||||
|
// TODO: NLS
|
||||||
|
text += '<p><a href="#" onclick="RED.projects.showFilesPrompt(); return false;">'+'Setup project files'+'</a></p>';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!persistentNotifications.hasOwnProperty(notificationId)) {
|
if (!persistentNotifications.hasOwnProperty(notificationId)) {
|
||||||
|
@ -33,6 +33,7 @@ RED.popover = (function() {
|
|||||||
var trigger = options.trigger;
|
var trigger = options.trigger;
|
||||||
var content = options.content;
|
var content = options.content;
|
||||||
var delay = options.delay;
|
var delay = options.delay;
|
||||||
|
var autoClose = options.autoClose;
|
||||||
var width = options.width||"auto";
|
var width = options.width||"auto";
|
||||||
var size = options.size||"default";
|
var size = options.size||"default";
|
||||||
if (!deltaSizes[size]) {
|
if (!deltaSizes[size]) {
|
||||||
@ -92,7 +93,6 @@ RED.popover = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (trigger === 'hover') {
|
if (trigger === 'hover') {
|
||||||
|
|
||||||
target.on('mouseenter',function(e) {
|
target.on('mouseenter',function(e) {
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
active = true;
|
active = true;
|
||||||
@ -116,6 +116,11 @@ RED.popover = (function() {
|
|||||||
openPopup();
|
openPopup();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (autoClose) {
|
||||||
|
setTimeout(function() {
|
||||||
|
active = false;
|
||||||
|
closePopup();
|
||||||
|
},autoClose);
|
||||||
}
|
}
|
||||||
var res = {
|
var res = {
|
||||||
setContent: function(_content) {
|
setContent: function(_content) {
|
||||||
|
@ -170,7 +170,7 @@ RED.projects.settings = (function() {
|
|||||||
updateProjectSummary(activeProject.summary, container);
|
updateProjectSummary(activeProject.summary, container);
|
||||||
editButton.show();
|
editButton.show();
|
||||||
});
|
});
|
||||||
$('<button class="editor-button">Done</button>')
|
$('<button class="editor-button">Save</button>')
|
||||||
.appendTo(bg)
|
.appendTo(bg)
|
||||||
.click(function(evt) {
|
.click(function(evt) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
@ -422,211 +422,517 @@ RED.projects.settings = (function() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSettingsPane(activeProject) {
|
function showProjectFileListing(row,activeProject,current,done) {
|
||||||
var pane = $('<div id="project-settings-tab-settings" class="project-settings-tab-pane node-help"></div>');
|
var dialog;
|
||||||
$('<h3></h3>').text("Credentials").appendTo(pane);
|
var dialogBody;
|
||||||
var row = $('<div class="user-settings-row"></div>').appendTo(pane);
|
var filesList;
|
||||||
if (activeProject.settings.credentialsEncrypted) {
|
var selected;
|
||||||
$('<span style="margin-right: 20px;"><i class="fa fa-lock"></i> Credentials are encrypted</span>').appendTo(row);
|
var container = $('<div class="project-file-listing-container"></div>',{style:"position: relative; min-height: 175px; height: 175px;"}).appendTo(row);
|
||||||
} else {
|
var spinner = addSpinnerOverlay(container);
|
||||||
$('<span style="margin-right: 20px;"><i class="fa fa-unlock"></i> Credentials are not encrypted</span>').appendTo(row);
|
|
||||||
}
|
|
||||||
var resetButton;
|
|
||||||
var action;
|
|
||||||
var changeButton = $('<button id="" class="editor-button"></button>')
|
|
||||||
.text(activeProject.settings.credentialsEncrypted?"Change key":"Enable encryption")
|
|
||||||
.appendTo(row)
|
|
||||||
.click(function(evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
newKey.val("");
|
|
||||||
if (currentKey) {
|
|
||||||
currentKey.val("");
|
|
||||||
currentKey.removeClass("input-error");
|
|
||||||
}
|
|
||||||
checkInputs();
|
|
||||||
saveButton.text("Save");
|
|
||||||
|
|
||||||
$(".project-settings-credentials-row").show();
|
$.getJSON("/projects/"+activeProject.name+"/files",function(result) {
|
||||||
$(".project-settings-credentials-current-row").show();
|
var fileNames = Object.keys(result);
|
||||||
$(this).prop('disabled',true);
|
var files = {};
|
||||||
if (resetButton) {
|
fileNames.sort();
|
||||||
resetButton.prop('disabled',true);
|
fileNames.forEach(function(file) {
|
||||||
}
|
file.split("/").reduce(function(r,v,i,arr) { if (v) { if (i<arr.length-1) { r[v] = r[v]||{};} else { r[v] = true }return r[v];}},files);
|
||||||
action = 'change';
|
|
||||||
});
|
});
|
||||||
if (activeProject.settings.credentialsEncrypted) {
|
var sortFiles = function(key,value,fullPath) {
|
||||||
resetButton = $('<button id="" style="margin-left: 10px;" class="editor-button"></button>')
|
var result = {
|
||||||
.text("Reset key")
|
name: key||"/",
|
||||||
.appendTo(row)
|
path: fullPath+(fullPath?"/":"")+key,
|
||||||
.click(function(evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
newKey.val("");
|
|
||||||
if (currentKey) {
|
|
||||||
currentKey.val("");
|
|
||||||
currentKey.removeClass("input-error");
|
|
||||||
}
|
|
||||||
checkInputs();
|
|
||||||
saveButton.text("Reset key");
|
|
||||||
|
|
||||||
$(".project-settings-credentials-row").show();
|
|
||||||
$(".project-settings-credentials-reset-row").show();
|
|
||||||
|
|
||||||
$(this).prop('disabled',true);
|
|
||||||
changeButton.prop('disabled',true);
|
|
||||||
action = 'reset';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeProject.settings.credentialSecretInvalid) {
|
|
||||||
row = $('<div class="user-settings-row"></div>').appendTo(pane);
|
|
||||||
$('<div class="form-tips form-warning"><i class="fa fa-warning"></i> The current key is not valid. Set the correct key or reset credentials.</div>').appendTo(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
var credentialsContainer = $('<div>',{style:"position:relative"}).appendTo(pane);
|
|
||||||
var currentKey;
|
|
||||||
var newKey;
|
|
||||||
|
|
||||||
var checkInputs = function() {
|
|
||||||
var valid = true;
|
|
||||||
if (newKey.val().length === 0) {
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
if (currentKey && currentKey.val() === 0) {
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
saveButton.toggleClass('disabled',!valid);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (activeProject.settings.credentialsEncrypted) {
|
|
||||||
if (!activeProject.settings.credentialSecretInvalid) {
|
|
||||||
row = $('<div class="user-settings-row project-settings-credentials-current-row hide"></div>').appendTo(credentialsContainer);
|
|
||||||
$('<label for="">Current key</label>').appendTo(row);
|
|
||||||
currentKey = $('<input type="password">').appendTo(row);
|
|
||||||
currentKey.on("change keyup paste",function() {
|
|
||||||
if (popover) {
|
|
||||||
popover.close();
|
|
||||||
popover = null;
|
|
||||||
$(this).removeClass('input-error');
|
|
||||||
}
|
|
||||||
checkInputs();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
row = $('<div class="user-settings-row project-settings-credentials-reset-row hide"></div>').appendTo(credentialsContainer);
|
|
||||||
$('<div class="form-tips form-warning"><i class="fa fa-warning"></i> Resetting the key will delete all existing credentials</div>').appendTo(row);
|
|
||||||
|
|
||||||
}
|
|
||||||
// $('<label for="" style="margin-left:20px; width: auto;"><input type="radio" name="project-settings-credentials-current" value="lost"> Forgotten key?</label>').appendTo(row);
|
|
||||||
|
|
||||||
row = $('<div class="user-settings-row project-settings-credentials-row hide"></div>').appendTo(credentialsContainer);
|
|
||||||
$('<label for=""></label>').text((activeProject.settings.credentialsEncrypted&& !activeProject.settings.credentialSecretInvalid)?"New key":"Encryption key").appendTo(row);
|
|
||||||
newKey = $('<input type="password">').appendTo(row).on("change keyup paste",checkInputs);
|
|
||||||
|
|
||||||
row = $('<div class="user-settings-row project-settings-credentials-row hide"></div>').appendTo(credentialsContainer);
|
|
||||||
var bg = $('<div class="button-group" style="text-align: right; margin-right:20px;"></div>').appendTo(row);
|
|
||||||
$('<button class="editor-button">Cancel</button>')
|
|
||||||
.appendTo(bg)
|
|
||||||
.click(function(evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
if (popover) {
|
|
||||||
popover.close();
|
|
||||||
popover = null;
|
|
||||||
}
|
|
||||||
changeButton.prop('disabled',false);
|
|
||||||
if (resetButton) {
|
|
||||||
resetButton.prop('disabled',false);
|
|
||||||
}
|
|
||||||
$(".project-settings-credentials-row").hide();
|
|
||||||
$(".project-settings-credentials-current-row").hide();
|
|
||||||
$(".project-settings-credentials-reset-row").hide();
|
|
||||||
});
|
|
||||||
var saveButton = $('<button class="editor-button primary disabled"></button>')
|
|
||||||
.text("Save")
|
|
||||||
.appendTo(bg)
|
|
||||||
.click(function(evt) {
|
|
||||||
evt.preventDefault();
|
|
||||||
if ($(this).hasClass('disabled')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var spinner = addSpinnerOverlay(credentialsContainer);
|
|
||||||
var payload = {
|
|
||||||
credentialSecret: newKey.val()
|
|
||||||
};
|
};
|
||||||
if (activeProject.settings.credentialSecretInvalid) {
|
if (value === true) {
|
||||||
RED.deploy.setDeployInflight(true);
|
result.type = 'f';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.type = 'd';
|
||||||
|
result.children = [];
|
||||||
|
result.path = result.path;
|
||||||
|
var files = Object.keys(value);
|
||||||
|
files.forEach(function(file) {
|
||||||
|
result.children.push(sortFiles(file,value[file],result.path));
|
||||||
|
})
|
||||||
|
result.children.sort(function(A,B) {
|
||||||
|
if (A.hasOwnProperty("children") && !B.hasOwnProperty("children")) {
|
||||||
|
return -1;
|
||||||
|
} else if (!A.hasOwnProperty("children") && B.hasOwnProperty("children")) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return A.name.localeCompare(B.name);
|
||||||
|
})
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
var files = sortFiles("",files,"");
|
||||||
|
createFileSubList(container,files.children,current,done,"height: 175px");
|
||||||
|
spinner.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function createFileSubList(container, files, current, onselect, style) {
|
||||||
|
style = style || "";
|
||||||
|
var list = $('<ol>',{class:"projects-dialog-file-list", style:style}).appendTo(container).editableList({
|
||||||
|
addButton: false,
|
||||||
|
scrollOnAdd: false,
|
||||||
|
addItem: function(row,index,entry) {
|
||||||
|
var header = $('<div></div>',{class:"projects-dialog-file-list-entry"}).appendTo(row);
|
||||||
|
if (entry.children) {
|
||||||
|
$('<span class="projects-dialog-file-list-entry-folder"><i class="fa fa-angle-right"></i> <i class="fa fa-folder-o"></i></span>').appendTo(header);
|
||||||
|
if (entry.children.length > 0) {
|
||||||
|
var children = $('<div></div>',{style:"padding-left: 20px;"}).appendTo(row);
|
||||||
|
if (current.indexOf(entry.path+"/") === 0) {
|
||||||
|
header.addClass("expanded");
|
||||||
|
} else {
|
||||||
|
children.hide();
|
||||||
|
}
|
||||||
|
createFileSubList(children,entry.children,current,onselect);
|
||||||
|
header.addClass("selectable");
|
||||||
|
header.click(function(e) {
|
||||||
|
if ($(this).hasClass("expanded")) {
|
||||||
|
$(this).removeClass("expanded");
|
||||||
|
children.slideUp(200);
|
||||||
|
} else {
|
||||||
|
$(this).addClass("expanded");
|
||||||
|
children.slideDown(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeProject.settings.credentialsEncrypted) {
|
});
|
||||||
if (action === 'reset') {
|
|
||||||
payload.resetCredentialSecret = true;
|
|
||||||
} else if (!activeProject.settings.credentialSecretInvalid) {
|
|
||||||
payload.currentCredentialSecret = currentKey.val();
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
var fileIcon = "fa-file-o";
|
||||||
|
var fileClass = "";
|
||||||
|
if (/\.json$/i.test(entry.name)) {
|
||||||
|
fileIcon = "fa-file-code-o"
|
||||||
|
} else if (/\.md$/i.test(entry.name)) {
|
||||||
|
fileIcon = "fa-book";
|
||||||
|
} else if (/^\.git/i.test(entry.name)) {
|
||||||
|
fileIcon = "fa-code-fork";
|
||||||
|
header.addClass("projects-dialog-file-list-entry-file-type-git");
|
||||||
}
|
}
|
||||||
var done = function(err,res) {
|
$('<span class="projects-dialog-file-list-entry-file"> <i class="fa '+fileIcon+'"></i></span>').appendTo(header);
|
||||||
|
header.addClass("selectable");
|
||||||
|
if (entry.path === current) {
|
||||||
|
header.addClass("selected");
|
||||||
|
}
|
||||||
|
header.click(function(e) {
|
||||||
|
$(".projects-dialog-file-list-entry.selected").removeClass("selected");
|
||||||
|
$(this).addClass("selected");
|
||||||
|
onselect(entry.path);
|
||||||
|
})
|
||||||
|
header.dblclick(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
onselect(entry.path,true);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
$('<span class="projects-dialog-file-list-entry-name" style=""></span>').text(entry.name).appendTo(header);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!style) {
|
||||||
|
list.parent().css("overflow-y","");
|
||||||
|
}
|
||||||
|
files.forEach(function(f) {
|
||||||
|
list.editableList('addItem',f);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// function editFiles(activeProject, container,flowFile, flowFileLabel) {
|
||||||
|
// var editButton = container.children().first();
|
||||||
|
// editButton.hide();
|
||||||
|
//
|
||||||
|
// var flowFileInput = $('<input id="" type="text" style="width: calc(100% - 300px);">').val(flowFile).insertAfter(flowFileLabel);
|
||||||
|
//
|
||||||
|
// var flowFileInputSearch = $('<button class="editor-button" style="margin-left: 10px"><i class="fa fa-folder-open-o"></i></button>')
|
||||||
|
// .insertAfter(flowFileInput)
|
||||||
|
// .click(function(e) {
|
||||||
|
// showProjectFileListing(activeProject,'Select flow file',flowFileInput.val(),function(result) {
|
||||||
|
// flowFileInput.val(result);
|
||||||
|
// checkFiles();
|
||||||
|
// })
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// var checkFiles = function() {
|
||||||
|
// saveButton.toggleClass('disabled',flowFileInput.val()==="");
|
||||||
|
// saveButton.prop('disabled',flowFileInput.val()==="");
|
||||||
|
// }
|
||||||
|
// flowFileInput.on("change keyup paste",checkFiles);
|
||||||
|
// flowFileLabel.hide();
|
||||||
|
//
|
||||||
|
// var bg = $('<span class="button-group" style="position: relative; float: right; margin-right:0;"></span>').prependTo(container);
|
||||||
|
// $('<button class="editor-button">Cancel</button>')
|
||||||
|
// .appendTo(bg)
|
||||||
|
// .click(function(evt) {
|
||||||
|
// evt.preventDefault();
|
||||||
|
//
|
||||||
|
// flowFileLabel.show();
|
||||||
|
// flowFileInput.remove();
|
||||||
|
// flowFileInputSearch.remove();
|
||||||
|
// bg.remove();
|
||||||
|
// editButton.show();
|
||||||
|
// });
|
||||||
|
// var saveButton = $('<button class="editor-button">Save</button>')
|
||||||
|
// .appendTo(bg)
|
||||||
|
// .click(function(evt) {
|
||||||
|
// evt.preventDefault();
|
||||||
|
// var newFlowFile = flowFileInput.val();
|
||||||
|
// var newCredsFile = credentialsFileInput.val();
|
||||||
|
// var spinner = addSpinnerOverlay(container);
|
||||||
|
// var done = function(err,res) {
|
||||||
|
// if (err) {
|
||||||
|
// spinner.remove();
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// activeProject.summary = v;
|
||||||
|
// spinner.remove();
|
||||||
|
// flowFileLabel.text(newFlowFile);
|
||||||
|
// flowFileLabel.show();
|
||||||
|
// flowFileInput.remove();
|
||||||
|
// flowFileInputSearch.remove();
|
||||||
|
// bg.remove();
|
||||||
|
// editButton.show();
|
||||||
|
// }
|
||||||
|
// // utils.sendRequest({
|
||||||
|
// // url: "projects/"+activeProject.name,
|
||||||
|
// // type: "PUT",
|
||||||
|
// // responses: {
|
||||||
|
// // 0: function(error) {
|
||||||
|
// // done(error,null);
|
||||||
|
// // },
|
||||||
|
// // 200: function(data) {
|
||||||
|
// // done(null,data);
|
||||||
|
// // },
|
||||||
|
// // 400: {
|
||||||
|
// // 'unexpected_error': function(error) {
|
||||||
|
// // done(error,null);
|
||||||
|
// // }
|
||||||
|
// // },
|
||||||
|
// // }
|
||||||
|
// // },{summary:v});
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// checkFiles();
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
function createFilesSection(activeProject,pane) {
|
||||||
|
var title = $('<h3></h3>').text("Files").appendTo(pane);
|
||||||
|
var filesContainer = $('<div class="user-settings-section"></div>').appendTo(pane);
|
||||||
|
var editButton = $('<button class="editor-button editor-button-small" style="float: right;">edit</button>')
|
||||||
|
.appendTo(title)
|
||||||
|
.click(function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
formButtons.show();
|
||||||
|
editButton.hide();
|
||||||
|
flowFileLabelText.hide();
|
||||||
|
flowFileInput.show();
|
||||||
|
flowFileInputSearch.show();
|
||||||
|
credFileLabel.hide();
|
||||||
|
credFileInput.show();
|
||||||
|
flowFileInput.focus();
|
||||||
|
// credentialStateLabel.parent().hide();
|
||||||
|
credentialStateLabel.addClass("uneditable-input");
|
||||||
|
$(".user-settings-row-credentials").show();
|
||||||
|
credentialStateLabel.css('height','auto');
|
||||||
|
credentialFormRows.hide();
|
||||||
|
credentialSecretButtons.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
var row;
|
||||||
|
|
||||||
|
// Flow files
|
||||||
|
row = $('<div class="user-settings-row"></div>').appendTo(filesContainer);
|
||||||
|
$('<label for=""></label>').text('Flow').appendTo(row);
|
||||||
|
var flowFileLabel = $('<div class="uneditable-input" style="padding:0">').appendTo(row);
|
||||||
|
var flowFileLabelText = $('<span style="display:inline-block; padding: 6px">').text(activeProject.files.flow).appendTo(flowFileLabel);
|
||||||
|
|
||||||
|
var flowFileInput = $('<input id="" type="text" style="margin-bottom: 0;width: 100%; border: none;">').val(activeProject.files.flow).hide().appendTo(flowFileLabel);
|
||||||
|
var flowFileInputSearch = $('<button class="editor-button" style="width: 36px; height: 36px; position: absolute; top: -1px; right: -1px;"><i class="fa fa-folder-open-o"></i></button>')
|
||||||
|
.hide()
|
||||||
|
.appendTo(flowFileLabel)
|
||||||
|
.click(function(e) {
|
||||||
|
if ($(this).hasClass('selected')) {
|
||||||
|
$(this).removeClass('selected');
|
||||||
|
flowFileLabel.find('.project-file-listing-container').remove();
|
||||||
|
flowFileLabel.css('height','');
|
||||||
|
flowFileLabel.css('color','');
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$(this).addClass('selected');
|
||||||
|
flowFileLabel.css('height','auto');
|
||||||
|
flowFileLabel.css('color','inherit');
|
||||||
|
showProjectFileListing(flowFileLabel,activeProject,flowFileInput.val(),function(result,isDblClick) {
|
||||||
|
if (result) {
|
||||||
|
flowFileInput.val(result);
|
||||||
|
}
|
||||||
|
if (isDblClick) {
|
||||||
|
$(flowFileInputSearch).click();
|
||||||
|
}
|
||||||
|
checkFiles();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
row = $('<div class="user-settings-row"></div>').appendTo(filesContainer);
|
||||||
|
$('<label for=""></label>').text('Credentials').appendTo(row);
|
||||||
|
var credFileLabel = $('<div class="uneditable-input">').text(activeProject.files.credentials).appendTo(row);
|
||||||
|
var credFileInput = $('<div class="uneditable-input">').text(activeProject.files.credentials).hide().insertAfter(credFileLabel);
|
||||||
|
|
||||||
|
var checkFiles = function() {
|
||||||
|
var saveDisabled;
|
||||||
|
var currentFlowValue = flowFileInput.val();
|
||||||
|
var m = /^(.+?)(\.[^.]*)?$/.exec(currentFlowValue);
|
||||||
|
if (m) {
|
||||||
|
credFileInput.text(m[1]+"_cred"+(m[2]||".json"));
|
||||||
|
} else if (currentFlowValue === "") {
|
||||||
|
credFileInput.text("");
|
||||||
|
}
|
||||||
|
var isFlowInvalid = currentFlowValue==="" ||
|
||||||
|
/\.\./.test(currentFlowValue) ||
|
||||||
|
/\/$/.test(currentFlowValue);
|
||||||
|
|
||||||
|
saveDisabled = isFlowInvalid || credFileInput.text()==="";
|
||||||
|
|
||||||
|
if (credentialSecretExistingInput.is(":visible")) {
|
||||||
|
credentialSecretExistingInput.toggleClass("input-error", credentialSecretExistingInput.val() === "");
|
||||||
|
saveDisabled = saveDisabled || credentialSecretExistingInput.val() === "";
|
||||||
|
}
|
||||||
|
if (credentialSecretNewInput.is(":visible")) {
|
||||||
|
credentialSecretNewInput.toggleClass("input-error", credentialSecretNewInput.val() === "");
|
||||||
|
saveDisabled = saveDisabled || credentialSecretNewInput.val() === "";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
flowFileInput.toggleClass("input-error", isFlowInvalid);
|
||||||
|
credFileInput.toggleClass("input-error",credFileInput.text()==="");
|
||||||
|
saveButton.toggleClass('disabled',saveDisabled);
|
||||||
|
saveButton.prop('disabled',saveDisabled);
|
||||||
|
}
|
||||||
|
flowFileInput.on("change keyup paste",checkFiles);
|
||||||
|
|
||||||
|
|
||||||
|
if (!activeProject.files.flow) {
|
||||||
|
$('<span class="form-warning"><i class="fa fa-warning"></i> Missing</span>').appendTo(flowFileLabelText);
|
||||||
|
}
|
||||||
|
if (!activeProject.files.credentials) {
|
||||||
|
$('<span class="form-warning"><i class="fa fa-warning"></i> Missing</span>').appendTo(credFileLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
row = $('<div class="user-settings-row"></div>').appendTo(filesContainer);
|
||||||
|
|
||||||
|
$('<label></label>').appendTo(row);
|
||||||
|
var credentialStateLabel = $('<span><i class="user-settings-credentials-state-icon fa"></i> <span class="user-settings-credentials-state"></span></span>').appendTo(row);
|
||||||
|
var credentialSecretButtons = $('<span class="button-group" style="margin-left: -72px;">').hide().appendTo(row);
|
||||||
|
|
||||||
|
credentialStateLabel.css('color','#666');
|
||||||
|
credentialSecretButtons.css('vertical-align','top');
|
||||||
|
var credentialSecretResetButton = $('<button class="editor-button" style="vertical-align: top; width: 36px; margin-bottom: 10px"><i class="fa fa-trash-o"></i></button>')
|
||||||
|
.appendTo(credentialSecretButtons)
|
||||||
|
.click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!$(this).hasClass('selected')) {
|
||||||
|
credentialSecretNewInput.val("");
|
||||||
|
credentialSecretExistingRow.hide();
|
||||||
|
credentialSecretNewRow.show();
|
||||||
|
$(this).addClass("selected");
|
||||||
|
credentialSecretEditButton.removeClass("selected");
|
||||||
|
credentialResetLabel.show();
|
||||||
|
credentialResetWarning.show();
|
||||||
|
credentialSetLabel.hide();
|
||||||
|
credentialChangeLabel.hide();
|
||||||
|
|
||||||
|
credentialFormRows.show();
|
||||||
|
} else {
|
||||||
|
$(this).removeClass("selected");
|
||||||
|
credentialFormRows.hide();
|
||||||
|
}
|
||||||
|
checkFiles();
|
||||||
|
});
|
||||||
|
var credentialSecretEditButton = $('<button class="editor-button" style="vertical-align: top; width: 36px; margin-bottom: 10px"><i class="fa fa-pencil"></i></button>')
|
||||||
|
.appendTo(credentialSecretButtons)
|
||||||
|
.click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!$(this).hasClass('selected')) {
|
||||||
|
credentialSecretExistingInput.val("");
|
||||||
|
credentialSecretNewInput.val("");
|
||||||
|
if (activeProject.settings.credentialSecretInvalid || !activeProject.settings.credentialsEncrypted) {
|
||||||
|
credentialSetLabel.show();
|
||||||
|
credentialChangeLabel.hide();
|
||||||
|
credentialSecretExistingRow.hide();
|
||||||
|
} else {
|
||||||
|
credentialSecretExistingRow.show();
|
||||||
|
credentialSetLabel.hide();
|
||||||
|
credentialChangeLabel.show();
|
||||||
|
}
|
||||||
|
credentialSecretNewRow.show();
|
||||||
|
credentialSecretEditButton.addClass("selected");
|
||||||
|
credentialSecretResetButton.removeClass("selected");
|
||||||
|
|
||||||
|
credentialResetLabel.hide();
|
||||||
|
credentialResetWarning.hide();
|
||||||
|
credentialFormRows.show();
|
||||||
|
} else {
|
||||||
|
$(this).removeClass("selected");
|
||||||
|
credentialFormRows.hide();
|
||||||
|
}
|
||||||
|
checkFiles();
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
row = $('<div class="user-settings-row user-settings-row-credentials"></div>').hide().appendTo(filesContainer);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var credentialFormRows = $('<div>',{style:"margin-top:10px"}).hide().appendTo(credentialStateLabel);
|
||||||
|
|
||||||
|
var credentialSetLabel = $('<div style="margin: 20px 0 10px 5px;">Set the encryption key:</div>').hide().appendTo(credentialFormRows);
|
||||||
|
var credentialChangeLabel = $('<div style="margin: 20px 0 10px 5px;">Change the encryption key:</div>').hide().appendTo(credentialFormRows);
|
||||||
|
var credentialResetLabel = $('<div style="margin: 20px 0 10px 5px;">Reset the encryption key:</div>').hide().appendTo(credentialFormRows);
|
||||||
|
|
||||||
|
var credentialSecretExistingRow = $('<div class="user-settings-row user-settings-row-credentials"></div>').appendTo(credentialFormRows);
|
||||||
|
$('<label for=""></label>').text('Current key').appendTo(credentialSecretExistingRow);
|
||||||
|
var credentialSecretExistingInput = $('<input type="password">').appendTo(credentialSecretExistingRow)
|
||||||
|
.on("change keyup paste",function() {
|
||||||
|
if (popover) {
|
||||||
|
popover.close();
|
||||||
|
popover = null;
|
||||||
|
}
|
||||||
|
checkFiles();
|
||||||
|
});
|
||||||
|
|
||||||
|
var credentialSecretNewRow = $('<div class="user-settings-row user-settings-row-credentials"></div>').appendTo(credentialFormRows);
|
||||||
|
|
||||||
|
|
||||||
|
$('<label for=""></label>').text('New key').appendTo(credentialSecretNewRow);
|
||||||
|
var credentialSecretNewInput = $('<input type="password">').appendTo(credentialSecretNewRow).on("change keyup paste",checkFiles);
|
||||||
|
|
||||||
|
var credentialResetWarning = $('<div class="form-tips form-warning" style="margin: 10px;"><i class="fa fa-warning"></i> This will delete all existing credentials</div>').hide().appendTo(credentialFormRows);
|
||||||
|
|
||||||
|
|
||||||
|
var hideEditForm = function() {
|
||||||
|
editButton.show();
|
||||||
|
formButtons.hide();
|
||||||
|
flowFileLabelText.show();
|
||||||
|
flowFileInput.hide();
|
||||||
|
flowFileInputSearch.hide();
|
||||||
|
credFileLabel.show();
|
||||||
|
credFileInput.hide();
|
||||||
|
// credentialStateLabel.parent().show();
|
||||||
|
credentialStateLabel.removeClass("uneditable-input");
|
||||||
|
credentialStateLabel.css('height','');
|
||||||
|
|
||||||
|
$(".user-settings-row-credentials").hide();
|
||||||
|
credentialFormRows.hide();
|
||||||
|
credentialSecretButtons.hide();
|
||||||
|
credentialSecretResetButton.removeClass("selected");
|
||||||
|
credentialSecretEditButton.removeClass("selected");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var formButtons = $('<span class="button-group" style="position: relative; float: right; margin-right:0;"></span>').hide().appendTo(filesContainer);
|
||||||
|
var cancelButton = $('<button class="editor-button">Cancel</button>')
|
||||||
|
.appendTo(formButtons)
|
||||||
|
.click(function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
hideEditForm();
|
||||||
|
});
|
||||||
|
var saveButton = $('<button class="editor-button">Save</button>')
|
||||||
|
.appendTo(formButtons)
|
||||||
|
.click(function(evt) {
|
||||||
|
evt.preventDefault();
|
||||||
|
var spinner = addSpinnerOverlay(filesContainer);
|
||||||
|
var done = function(err) {
|
||||||
spinner.remove();
|
spinner.remove();
|
||||||
if (err) {
|
if (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
flowFileLabelText.text(flowFileInput.val());
|
||||||
|
credFileLabel.text(credFileInput.text());
|
||||||
|
hideEditForm();
|
||||||
}
|
}
|
||||||
|
var payload = {
|
||||||
|
files: {
|
||||||
|
flow: flowFileInput.val(),
|
||||||
|
credentials: credFileInput.text()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (credentialSecretResetButton.hasClass('selected')) {
|
||||||
|
payload.resetCredentialSecret = true;
|
||||||
|
}
|
||||||
|
if (credentialSecretResetButton.hasClass('selected') || credentialSecretEditButton.hasClass('selected')) {
|
||||||
|
payload.credentialSecret = credentialSecretNewInput.val();
|
||||||
|
if (credentialSecretExistingInput.is(":visible")) {
|
||||||
|
payload.currentCredentialSecret = credentialSecretExistingInput.val();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// console.log(JSON.stringify(payload,null,4));
|
||||||
|
RED.deploy.setDeployInflight(true);
|
||||||
utils.sendRequest({
|
utils.sendRequest({
|
||||||
url: "projects/"+activeProject.name,
|
url: "projects/"+activeProject.name,
|
||||||
type: "PUT",
|
type: "PUT",
|
||||||
responses: {
|
responses: {
|
||||||
0: function(error) {
|
0: function(error) {
|
||||||
done(error,null);
|
done(error);
|
||||||
},
|
},
|
||||||
200: function(data) {
|
200: function(data) {
|
||||||
if (popover) {
|
activeProject = data;
|
||||||
popover.close();
|
console.log("updating form");
|
||||||
popover = null;
|
updateForm();
|
||||||
}
|
done();
|
||||||
changeButton.prop('disabled',false);
|
|
||||||
if (resetButton) {
|
|
||||||
resetButton.prop('disabled',false);
|
|
||||||
}
|
|
||||||
$(".project-settings-credentials-row").hide();
|
|
||||||
$(".project-settings-credentials-current-row").hide();
|
|
||||||
$(".project-settings-credentials-reset-row").hide();
|
|
||||||
},
|
},
|
||||||
400: {
|
400: {
|
||||||
|
'credentials_load_failed': function(error) {
|
||||||
|
done(error);
|
||||||
|
},
|
||||||
'unexpected_error': function(error) {
|
'unexpected_error': function(error) {
|
||||||
done(error,null);
|
console.log(error);
|
||||||
|
done(error);
|
||||||
},
|
},
|
||||||
'missing_current_credential_key': function(error) {
|
'missing_current_credential_key': function(error) {
|
||||||
currentKey.addClass("input-error");
|
credentialSecretExistingInput.addClass("input-error");
|
||||||
popover = RED.popover.create({
|
popover = RED.popover.create({
|
||||||
target: currentKey,
|
target: credentialSecretExistingInput,
|
||||||
direction: 'right',
|
direction: 'right',
|
||||||
size: 'small',
|
size: 'small',
|
||||||
content: "Incorrect key"
|
content: "Incorrect key",
|
||||||
|
autoClose: 3000
|
||||||
}).open();
|
}).open();
|
||||||
done();
|
done(error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},payload).always(function() {
|
},payload).always(function() {
|
||||||
if (activeProject.settings.credentialSecretInvalid) {
|
|
||||||
RED.deploy.setDeployInflight(false);
|
RED.deploy.setDeployInflight(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
var updateForm = function() {
|
||||||
|
if (activeProject.settings.credentialSecretInvalid) {
|
||||||
|
credentialStateLabel.find(".user-settings-credentials-state-icon").removeClass().addClass("user-settings-credentials-state-icon fa fa-warning");
|
||||||
|
credentialStateLabel.find(".user-settings-credentials-state").text("Invalid encryption key");
|
||||||
|
} else if (activeProject.settings.credentialsEncrypted) {
|
||||||
|
credentialStateLabel.find(".user-settings-credentials-state-icon").removeClass().addClass("user-settings-credentials-state-icon fa fa-lock");
|
||||||
|
credentialStateLabel.find(".user-settings-credentials-state").text("Encryption enabled");
|
||||||
|
} else {
|
||||||
|
credentialStateLabel.find(".user-settings-credentials-state-icon").removeClass().addClass("user-settings-credentials-state-icon fa fa-unlock");
|
||||||
|
credentialStateLabel.find(".user-settings-credentials-state").text("Encryption disabled");
|
||||||
|
}
|
||||||
|
credentialSecretResetButton.toggleClass('disabled',!activeProject.settings.credentialsEncrypted);
|
||||||
|
credentialSecretResetButton.prop('disabled',!activeProject.settings.credentialsEncrypted);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// $('<h3></h3>').text("Credentials").appendTo(pane);
|
|
||||||
// row = $('<div class="user-settings-row"></div>').appendTo(pane);
|
|
||||||
// $('<span style="margin-right: 20px;"><i class="fa fa-unlock"></i> Credentials are not encrypted</span>').appendTo(row);
|
|
||||||
// $('<button id="" class="editor-button">Set key</button>').appendTo(row);
|
|
||||||
|
|
||||||
|
|
||||||
$('<h3></h3>').text("Repository").appendTo(pane);
|
|
||||||
row = $('<div class="user-settings-row"></div>').appendTo(pane);
|
|
||||||
var input;
|
|
||||||
$('<label for="">'+'Remote'+'</label>').appendTo(row);
|
|
||||||
$('<input id="" type="text">').appendTo(row);
|
|
||||||
|
|
||||||
|
|
||||||
|
checkFiles();
|
||||||
|
updateForm();
|
||||||
|
}
|
||||||
|
function createSettingsPane(activeProject) {
|
||||||
|
var pane = $('<div id="project-settings-tab-settings" class="project-settings-tab-pane node-help"></div>');
|
||||||
|
createFilesSection(activeProject,pane);
|
||||||
return pane;
|
return pane;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -933,6 +933,9 @@ function refresh() {
|
|||||||
showCredentialsPrompt: function() { //TODO: rename this function
|
showCredentialsPrompt: function() { //TODO: rename this function
|
||||||
RED.projects.settings.show('settings');
|
RED.projects.settings.show('settings');
|
||||||
},
|
},
|
||||||
|
showFilesPrompt: function() { //TODO: rename this function
|
||||||
|
RED.projects.settings.show('settings');
|
||||||
|
},
|
||||||
// showSidebar: showSidebar,
|
// showSidebar: showSidebar,
|
||||||
refresh: refresh,
|
refresh: refresh,
|
||||||
editProject: function() {
|
editProject: function() {
|
||||||
|
@ -371,3 +371,76 @@
|
|||||||
transition: all 0.2s ease-in-out;
|
transition: all 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.project-file-listing-container > .red-ui-editableList > .red-ui-editableList-border {
|
||||||
|
border-radius: 0;
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid $secondary-border-color;
|
||||||
|
|
||||||
|
}
|
||||||
|
.red-ui-editableList-container .projects-dialog-file-list {
|
||||||
|
.red-ui-editableList-border {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
padding: 0 !important;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.red-ui-editableList-container {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.projects-dialog-file-list-entry {
|
||||||
|
padding: 3px 0;
|
||||||
|
border-left: 2px solid #fff;
|
||||||
|
border-right: 2px solid #fff;
|
||||||
|
&.projects-list-entry-current {
|
||||||
|
&:not(.selectable) {
|
||||||
|
background: #f9f9f9;
|
||||||
|
}
|
||||||
|
i {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.selectable {
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
background: #f3f3f3;
|
||||||
|
border-left-color:#999;
|
||||||
|
border-right-color:#999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #999;
|
||||||
|
width: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
&.selected {
|
||||||
|
background: #efefef;
|
||||||
|
border-left-color:#999;
|
||||||
|
border-right-color:#999;
|
||||||
|
}
|
||||||
|
span {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align:middle;
|
||||||
|
}
|
||||||
|
.projects-dialog-file-list-entry-folder {
|
||||||
|
margin: 0 10px 0 0px;
|
||||||
|
|
||||||
|
.fa-angle-right {
|
||||||
|
color: #333;
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.projects-dialog-file-list-entry-file {
|
||||||
|
margin: 0 10px 0 20px;
|
||||||
|
}
|
||||||
|
.projects-dialog-file-list-entry-name {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
&.expanded .fa-angle-right {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.projects-dialog-file-list-entry-file-type-git { color: #999 }
|
||||||
|
@ -43,8 +43,11 @@
|
|||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
input {
|
input, div.uneditable-input {
|
||||||
margin-bottom: 0;
|
//margin-bottom: 0;
|
||||||
|
}
|
||||||
|
div.uneditable-input {
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
input[type='number'] {
|
input[type='number'] {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
@ -57,3 +60,14 @@
|
|||||||
.user-settings-row {
|
.user-settings-row {
|
||||||
padding: 5px 10px 2px;
|
padding: 5px 10px 2px;
|
||||||
}
|
}
|
||||||
|
.user-settings-section {
|
||||||
|
position: relative;
|
||||||
|
&:after {
|
||||||
|
content: "";
|
||||||
|
display: table;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.uneditable-input, input {
|
||||||
|
width: calc(100% - 150px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -33,6 +33,7 @@ function handleStatus(event) {
|
|||||||
publish("status/"+event.id,event.status,true);
|
publish("status/"+event.id,event.status,true);
|
||||||
}
|
}
|
||||||
function handleRuntimeEvent(event) {
|
function handleRuntimeEvent(event) {
|
||||||
|
log.trace("runtime event: "+JSON.stringify(event));
|
||||||
publish("notification/"+event.id,event.payload||{},event.retain);
|
publish("notification/"+event.id,event.payload||{},event.retain);
|
||||||
}
|
}
|
||||||
function init(_server,runtime) {
|
function init(_server,runtime) {
|
||||||
|
@ -85,7 +85,8 @@
|
|||||||
"nodeActionDisabled": "node actions disabled within subflow",
|
"nodeActionDisabled": "node actions disabled within subflow",
|
||||||
"missing-types": "Flows stopped due to missing node types. Check logs for details.",
|
"missing-types": "Flows stopped due to missing node types. Check logs for details.",
|
||||||
"restartRequired": "Node-RED must be restarted to enable upgraded modules",
|
"restartRequired": "Node-RED must be restarted to enable upgraded modules",
|
||||||
"invalid-credentials-secret": "Flows stopped due to missing or invalid credentialSecret"
|
"credentials_load_failed": "Flows stopped due to missing or invalid credentialSecret",
|
||||||
|
"missing_flow_file": "Could not find the project flow file"
|
||||||
},
|
},
|
||||||
|
|
||||||
"error": "<strong>Error</strong>: __message__",
|
"error": "<strong>Error</strong>: __message__",
|
||||||
|
@ -77,14 +77,15 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
res.redirect(303,req.baseUrl + '/');
|
res.redirect(303,req.baseUrl + '/'+ req.params.id);
|
||||||
}
|
}
|
||||||
} else if (req.body.hasOwnProperty('credentialSecret') ||
|
} else if (req.body.hasOwnProperty('credentialSecret') ||
|
||||||
req.body.hasOwnProperty('description') ||
|
req.body.hasOwnProperty('description') ||
|
||||||
req.body.hasOwnProperty('dependencies')||
|
req.body.hasOwnProperty('dependencies')||
|
||||||
req.body.hasOwnProperty('summary')) {
|
req.body.hasOwnProperty('summary') ||
|
||||||
|
req.body.hasOwnProperty('files')) {
|
||||||
runtime.storage.projects.updateProject(req.params.id, req.body).then(function() {
|
runtime.storage.projects.updateProject(req.params.id, req.body).then(function() {
|
||||||
res.redirect(303,req.baseUrl + '/');
|
res.redirect(303,req.baseUrl + '/'+ req.params.id);
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
if (err.code) {
|
if (err.code) {
|
||||||
res.status(400).json({error:err.code, message: err.message});
|
res.status(400).json({error:err.code, message: err.message});
|
||||||
@ -92,6 +93,8 @@ module.exports = {
|
|||||||
res.status(400).json({error:"unexpected_error", message:err.toString()});
|
res.status(400).json({error:"unexpected_error", message:err.toString()});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
res.status(400).json({error:"unexpected_error", message:"invalid_request"});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -78,7 +78,7 @@ function loadFlows() {
|
|||||||
});
|
});
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
activeConfig = null;
|
activeConfig = null;
|
||||||
events.emit("runtime-event",{id:"runtime-state",payload:{type:"warning",error:"credentials_load_failed",text:"notification.warnings.invalid-credentials-secret"},retain:true});
|
events.emit("runtime-event",{id:"runtime-state",payload:{type:"warning",error:err.code,text:"notification.warnings."+err.code},retain:true});
|
||||||
log.warn(log._("nodes.flows.error",{message:err.toString()}));
|
log.warn(log._("nodes.flows.error",{message:err.toString()}));
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
@ -49,11 +49,11 @@ Project.prototype.load = function () {
|
|||||||
|
|
||||||
this.credentialSecret = projectSettings.credentialSecret;
|
this.credentialSecret = projectSettings.credentialSecret;
|
||||||
|
|
||||||
this.paths.flowFile = fspath.join(this.path,"flow.json");
|
// this.paths.flowFile = fspath.join(this.path,"flow.json");
|
||||||
this.paths.credentialsFile = fspath.join(this.path,"flow_cred.json");
|
// this.paths.credentialsFile = fspath.join(this.path,"flow_cred.json");
|
||||||
|
|
||||||
var promises = [];
|
var promises = [];
|
||||||
return checkProjectFiles(project.name).then(function(missingFiles) {
|
return checkProjectFiles(project).then(function(missingFiles) {
|
||||||
if (missingFiles.length > 0) {
|
if (missingFiles.length > 0) {
|
||||||
project.missingFiles = missingFiles;
|
project.missingFiles = missingFiles;
|
||||||
}
|
}
|
||||||
@ -61,7 +61,18 @@ Project.prototype.load = function () {
|
|||||||
project.paths['package.json'] = fspath.join(project.path,"package.json");
|
project.paths['package.json'] = fspath.join(project.path,"package.json");
|
||||||
promises.push(fs.readFile(project.paths['package.json'],"utf8").then(function(content) {
|
promises.push(fs.readFile(project.paths['package.json'],"utf8").then(function(content) {
|
||||||
project.package = util.parseJSON(content);
|
project.package = util.parseJSON(content);
|
||||||
|
if (project.package.hasOwnProperty('node-red')) {
|
||||||
|
if (project.package['node-red'].hasOwnProperty('settings')) {
|
||||||
|
project.paths.flowFile = project.package['node-red'].settings.flowFile;
|
||||||
|
project.paths.credentialsFile = project.package['node-red'].settings.credentialsFile;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// TODO: package.json doesn't have a node-red section
|
||||||
|
// is that a bad thing?
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
} else {
|
||||||
|
project.package = {};
|
||||||
}
|
}
|
||||||
if (missingFiles.indexOf('README.md') === -1) {
|
if (missingFiles.indexOf('README.md') === -1) {
|
||||||
project.paths['README.md'] = fspath.join(project.path,"README.md");
|
project.paths['README.md'] = fspath.join(project.path,"README.md");
|
||||||
@ -71,6 +82,16 @@ Project.prototype.load = function () {
|
|||||||
} else {
|
} else {
|
||||||
project.description = "";
|
project.description = "";
|
||||||
}
|
}
|
||||||
|
// if (missingFiles.indexOf('flow.json') !== -1) {
|
||||||
|
// console.log("MISSING FLOW FILE");
|
||||||
|
// } else {
|
||||||
|
// project.paths.flowFile = fspath.join(project.path,"flow.json");
|
||||||
|
// }
|
||||||
|
// if (missingFiles.indexOf('flow_cred.json') !== -1) {
|
||||||
|
// console.log("MISSING CREDS FILE");
|
||||||
|
// } else {
|
||||||
|
// project.paths.credentialsFile = fspath.join(project.path,"flow_cred.json");
|
||||||
|
// }
|
||||||
|
|
||||||
return when.settle(promises).then(function() {
|
return when.settle(promises).then(function() {
|
||||||
return project;
|
return project;
|
||||||
@ -79,11 +100,23 @@ Project.prototype.load = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Project.prototype.update = function (data) {
|
Project.prototype.update = function (data) {
|
||||||
var filesToUpdate = {};
|
|
||||||
var promises = [];
|
var promises = [];
|
||||||
var project = this;
|
var project = this;
|
||||||
|
var saveSettings = false;
|
||||||
|
var saveREADME = false;
|
||||||
|
var savePackage = false;
|
||||||
|
var flowFilesChanged = false;
|
||||||
|
var credentialSecretChanged = false;
|
||||||
|
|
||||||
if (data.credentialSecret) {
|
var globalProjectSettings = settings.get("projects");
|
||||||
|
if (!globalProjectSettings.projects.hasOwnProperty(this.name)) {
|
||||||
|
globalProjectSettings.projects[this.name] = {};
|
||||||
|
saveSettings = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (data.credentialSecret && data.credentialSecret !== this.credentialSecret) {
|
||||||
var existingSecret = data.currentCredentialSecret;
|
var existingSecret = data.currentCredentialSecret;
|
||||||
var isReset = data.resetCredentialSecret;
|
var isReset = data.resetCredentialSecret;
|
||||||
var secret = data.credentialSecret;
|
var secret = data.credentialSecret;
|
||||||
@ -100,42 +133,62 @@ Project.prototype.update = function (data) {
|
|||||||
this.credentialSecret !== existingSecret) { // key doesn't match provided existing key
|
this.credentialSecret !== existingSecret) { // key doesn't match provided existing key
|
||||||
var e = new Error("Cannot change credentialSecret without current key");
|
var e = new Error("Cannot change credentialSecret without current key");
|
||||||
e.code = "missing_current_credential_key";
|
e.code = "missing_current_credential_key";
|
||||||
throw e;
|
return when.reject(e);
|
||||||
}
|
}
|
||||||
this.credentialSecret = secret;
|
this.credentialSecret = secret;
|
||||||
|
|
||||||
var globalProjectSettings = settings.get("projects");
|
|
||||||
globalProjectSettings.projects[this.name] = globalProjectSettings.projects[this.name]||{}
|
|
||||||
globalProjectSettings.projects[this.name].credentialSecret = project.credentialSecret;
|
globalProjectSettings.projects[this.name].credentialSecret = project.credentialSecret;
|
||||||
delete this.credentialSecretInvalid;
|
delete this.credentialSecretInvalid;
|
||||||
|
saveSettings = true;
|
||||||
return settings.set("projects",globalProjectSettings);
|
credentialSecretChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.hasOwnProperty('description')) {
|
if (data.hasOwnProperty('description')) {
|
||||||
filesToUpdate[this.paths['README.md']] = function() {
|
saveREADME = true;
|
||||||
return data.description;
|
|
||||||
};
|
|
||||||
this.description = data.description;
|
this.description = data.description;
|
||||||
}
|
}
|
||||||
if (data.hasOwnProperty('dependencies')) {
|
if (data.hasOwnProperty('dependencies')) {
|
||||||
filesToUpdate[this.paths['package.json']] = function() {
|
savePackage = true;
|
||||||
return JSON.stringify(project.package,"",4)
|
|
||||||
};
|
|
||||||
this.package.dependencies = data.dependencies;
|
this.package.dependencies = data.dependencies;
|
||||||
}
|
}
|
||||||
if (data.hasOwnProperty('summary')) {
|
if (data.hasOwnProperty('summary')) {
|
||||||
filesToUpdate[this.paths['package.json']] = function() {
|
savePackage = true;
|
||||||
return JSON.stringify(project.package,"",4)
|
|
||||||
};
|
|
||||||
this.package.description = data.summary;
|
this.package.description = data.summary;
|
||||||
}
|
}
|
||||||
|
|
||||||
var files = Object.keys(filesToUpdate);
|
if (data.hasOwnProperty('files')) {
|
||||||
files.forEach(function(f) {
|
this.package['node-red'] = this.package['node-red'] || { settings: {}};
|
||||||
promises.push(util.writeFile(f,filesToUpdate[f]()));
|
if (data.files.hasOwnProperty('flow') && this.package['node-red'].settings.flowFile !== data.files.flow) {
|
||||||
});
|
this.paths.flowFile = data.files.flow;
|
||||||
return when.settle(promises);
|
this.package['node-red'].settings.flowFile = data.files.flow;
|
||||||
|
savePackage = true;
|
||||||
|
flowFilesChanged = true;
|
||||||
|
}
|
||||||
|
if (data.files.hasOwnProperty('credentials') && this.package['node-red'].settings.credentialsFile !== data.files.credentials) {
|
||||||
|
this.paths.credentialsFile = data.files.credentials;
|
||||||
|
this.package['node-red'].settings.credentialsFile = data.files.credentials;
|
||||||
|
// Don't know if the credSecret is invalid or not so clear the flag
|
||||||
|
delete this.credentialSecretInvalid;
|
||||||
|
|
||||||
|
savePackage = true;
|
||||||
|
flowFilesChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (saveSettings) {
|
||||||
|
promises.push(settings.set("projects",globalProjectSettings));
|
||||||
|
}
|
||||||
|
if (saveREADME) {
|
||||||
|
promises.push(util.writeFile(this.paths['README.md'], this.description));
|
||||||
|
}
|
||||||
|
if (savePackage) {
|
||||||
|
promises.push(util.writeFile(this.paths['package.json'], JSON.stringify(this.package,"",4)));
|
||||||
|
}
|
||||||
|
return when.settle(promises).then(function(res) {
|
||||||
|
return {
|
||||||
|
flowFilesChanged: flowFilesChanged,
|
||||||
|
credentialSecretChanged: credentialSecretChanged
|
||||||
|
}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
Project.prototype.getFiles = function () {
|
Project.prototype.getFiles = function () {
|
||||||
@ -161,13 +214,23 @@ Project.prototype.getCommit = function(sha) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Project.prototype.getFlowFile = function() {
|
Project.prototype.getFlowFile = function() {
|
||||||
return this.paths.flowFile;
|
console.log("Project.getFlowFile = ",this.paths.flowFile);
|
||||||
|
if (this.paths.flowFile) {
|
||||||
|
return fspath.join(this.path,this.paths.flowFile);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Project.prototype.getFlowFileBackup = function() {
|
Project.prototype.getFlowFileBackup = function() {
|
||||||
return getBackupFilename(this.getFlowFile());
|
return getBackupFilename(this.getFlowFile());
|
||||||
}
|
}
|
||||||
Project.prototype.getCredentialsFile = function() {
|
Project.prototype.getCredentialsFile = function() {
|
||||||
|
console.log("Project.getCredentialsFile = ",this.paths.credentialsFile);
|
||||||
|
if (this.paths.credentialsFile) {
|
||||||
|
return fspath.join(this.path,this.paths.credentialsFile);
|
||||||
|
} else {
|
||||||
return this.paths.credentialsFile;
|
return this.paths.credentialsFile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Project.prototype.getCredentialsFileBackup = function() {
|
Project.prototype.getCredentialsFileBackup = function() {
|
||||||
return getBackupFilename(this.getCredentialsFile());
|
return getBackupFilename(this.getCredentialsFile());
|
||||||
@ -186,8 +249,8 @@ Project.prototype.toJSON = function () {
|
|||||||
credentialSecretInvalid: this.credentialSecretInvalid
|
credentialSecretInvalid: this.credentialSecretInvalid
|
||||||
},
|
},
|
||||||
files: {
|
files: {
|
||||||
flowFile: this.paths.flowFile&&this.paths.flowFile.substring(this.path.length),
|
flow: this.paths.flowFile,
|
||||||
credentialsFile: this.paths.credentialsFile&&this.paths.credentialsFile.substring(this.path.length)
|
credentials: this.paths.credentialsFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -233,7 +296,7 @@ function createDefaultProject(project) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function checkProjectFiles(project) {
|
function checkProjectFiles(project) {
|
||||||
var projectPath = fspath.join(projectsDir,project);
|
var projectPath = project.path;
|
||||||
var promises = [];
|
var promises = [];
|
||||||
var paths = [];
|
var paths = [];
|
||||||
for (var file in defaultFileSet) {
|
for (var file in defaultFileSet) {
|
||||||
@ -279,6 +342,9 @@ function createProject(metadata) {
|
|||||||
if (metadata.credentialSecret) {
|
if (metadata.credentialSecret) {
|
||||||
projects.projects[project].credentialSecret = metadata.credentialSecret;
|
projects.projects[project].credentialSecret = metadata.credentialSecret;
|
||||||
}
|
}
|
||||||
|
if (metadata.remote) {
|
||||||
|
projects.projects[project].remote = metadata.remote;
|
||||||
|
}
|
||||||
return settings.set('projects',projects);
|
return settings.set('projects',projects);
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
if (metadata.remote) {
|
if (metadata.remote) {
|
||||||
|
@ -111,7 +111,7 @@ function getFiles(localRepo) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
files[fullName] = {
|
files[fullName] = {
|
||||||
type: "f"
|
type: /\/$/.test(fullName)?"d":"f"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return runCommand(gitCommand,["status","--porcelain"],localRepo).then(function(output) {
|
return runCommand(gitCommand,["status","--porcelain"],localRepo).then(function(output) {
|
||||||
|
@ -200,11 +200,36 @@ function updateProject(project,data) {
|
|||||||
// TODO standardise
|
// TODO standardise
|
||||||
throw new Error("Cannot update inactive project");
|
throw new Error("Cannot update inactive project");
|
||||||
}
|
}
|
||||||
if (data.hasOwnProperty('credentialSecret')) {
|
// In case this triggers a credential secret change
|
||||||
return setCredentialSecret(data);
|
var isReset = data.resetCredentialSecret;
|
||||||
} else {
|
var wasInvalid = activeProject.credentialSecretInvalid;
|
||||||
return activeProject.update(data);
|
|
||||||
|
return activeProject.update(data).then(function(result) {
|
||||||
|
|
||||||
|
if (result.flowFilesChanged) {
|
||||||
|
flowsFullPath = activeProject.getFlowFile();
|
||||||
|
flowsFileBackup = activeProject.getFlowFileBackup();
|
||||||
|
credentialsFile = activeProject.getCredentialsFile();
|
||||||
|
credentialsFileBackup = activeProject.getCredentialsFileBackup();
|
||||||
|
return reloadActiveProject();
|
||||||
|
} else if (result.credentialSecretChanged) {
|
||||||
|
if (isReset || !wasInvalid) {
|
||||||
|
if (isReset) {
|
||||||
|
runtime.nodes.clearCredentials();
|
||||||
}
|
}
|
||||||
|
runtime.nodes.setCredentialSecret(activeProject.credentialSecret);
|
||||||
|
return runtime.nodes.exportCredentials()
|
||||||
|
.then(runtime.storage.saveCredentials)
|
||||||
|
.then(function() {
|
||||||
|
if (wasInvalid) {
|
||||||
|
return reloadActiveProject();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (wasInvalid) {
|
||||||
|
return reloadActiveProject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
function setCredentialSecret(data) { //existingSecret,secret) {
|
function setCredentialSecret(data) { //existingSecret,secret) {
|
||||||
var isReset = data.resetCredentialSecret;
|
var isReset = data.resetCredentialSecret;
|
||||||
@ -251,6 +276,14 @@ function getFlows() {
|
|||||||
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
|
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (activeProject) {
|
||||||
|
if (!activeProject.getFlowFile()) {
|
||||||
|
log.warn("NLS: project has no flow file");
|
||||||
|
var error = new Error("NLS: project has no flow file");
|
||||||
|
error.code = "missing_flow_file";
|
||||||
|
return when.reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
return util.readFile(flowsFullPath,flowsFileBackup,[],'flow');
|
return util.readFile(flowsFullPath,flowsFileBackup,[],'flow');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user