mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Add first-run dialog to migrate files to project
This commit is contained in:
parent
474f4572f2
commit
33a5b84181
@ -44,10 +44,21 @@
|
|||||||
$("#palette-search").removeClass("hide");
|
$("#palette-search").removeClass("hide");
|
||||||
loadFlows(function() {
|
loadFlows(function() {
|
||||||
if (RED.settings.theme("projects.enabled",true)) {
|
if (RED.settings.theme("projects.enabled",true)) {
|
||||||
RED.projects.refresh(function() {
|
RED.projects.refresh(function(activeProject) {
|
||||||
RED.sidebar.info.refresh()
|
RED.sidebar.info.refresh()
|
||||||
|
if (!activeProject) {
|
||||||
|
// Projects enabled but no active project
|
||||||
|
RED.menu.setDisabled('menu-item-projects-open',true);
|
||||||
|
RED.menu.setDisabled('menu-item-projects-delete',true);
|
||||||
|
if (activeProject === false) {
|
||||||
|
// User previously decline the migration to projects.
|
||||||
|
} else { // null/undefined
|
||||||
|
RED.projects.showStartup();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// Projects disabled by the user
|
||||||
RED.sidebar.info.refresh()
|
RED.sidebar.info.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,7 +229,7 @@
|
|||||||
function loadEditor() {
|
function loadEditor() {
|
||||||
var menuOptions = [];
|
var menuOptions = [];
|
||||||
if (RED.settings.theme("projects.enabled",true)) {
|
if (RED.settings.theme("projects.enabled",true)) {
|
||||||
menuOptions.push({id:"menu-item-projects-menu",label:"NLS: Projects",options:[
|
menuOptions.push({id:"menu-item-projects-menu",label:"Projects",options:[
|
||||||
{id:"menu-item-projects-new",label:"New...",disabled:false,onselect:"core:new-project"},
|
{id:"menu-item-projects-new",label:"New...",disabled:false,onselect:"core:new-project"},
|
||||||
{id:"menu-item-projects-open",label:"Open...",disabled:false,onselect:"core:open-project"},
|
{id:"menu-item-projects-open",label:"Open...",disabled:false,onselect:"core:open-project"},
|
||||||
{id:"menu-item-projects-delete",label:"Delete...",disabled:false,onselect:"core:delete-project"},
|
{id:"menu-item-projects-delete",label:"Delete...",disabled:false,onselect:"core:delete-project"},
|
||||||
|
@ -22,24 +22,435 @@ RED.projects = (function() {
|
|||||||
|
|
||||||
var screens = {};
|
var screens = {};
|
||||||
function initScreens() {
|
function initScreens() {
|
||||||
|
var migrateProjectHeader = $('<div class="projects-dialog-screen-start-hero"></div>');
|
||||||
|
$('<span><i class="fa fa-files-o fa-2x"></i> <i class="fa fa-long-arrow-right fa-2x"></i> <i class="fa fa-archive fa-2x"></i></span>').appendTo(migrateProjectHeader)
|
||||||
|
$('<hr>').appendTo(migrateProjectHeader);
|
||||||
|
|
||||||
|
var createProjectOptions = {};
|
||||||
|
|
||||||
screens = {
|
screens = {
|
||||||
'welcome': {
|
'welcome': {
|
||||||
content: function() {
|
content: function(options) {
|
||||||
|
|
||||||
var container = $('<div class="projects-dialog-screen-start"></div>');
|
var container = $('<div class="projects-dialog-screen-start"></div>');
|
||||||
var buttons = $('<div style="margin: 30px"></div>').appendTo(container);
|
|
||||||
var createNew = $('<button class="editor-button"><i class="fa fa-archive fa-2x"></i><i class="fa fa-plus-circle" style="position:absolute"></i><br/>Create a project</button>').appendTo(buttons);
|
migrateProjectHeader.appendTo(container);
|
||||||
createNew.click(function(e) {
|
|
||||||
e.preventDefault();
|
var body = $('<div class="projects-dialog-screen-start-body"></div>').appendTo(container);
|
||||||
show('create');
|
$('<p>').text("Hello! We have introduced 'projects' to Node-RED.").appendTo(body);
|
||||||
});
|
$('<p>').text("This is a new way for you to manage your flow files and includes version control of your flows.").appendTo(body);
|
||||||
var openExisting = $('<button class="editor-button"><i class="fa fa-folder-open-o fa-2x"></i><br/>Open a project</button>').appendTo(buttons);
|
$('<p>').text("To get started you can create your first project using your current flow files in a few easy steps.").appendTo(body);
|
||||||
openExisting.click(function(e) {
|
$('<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 option at any time.").appendTo(body);
|
||||||
e.preventDefault();
|
|
||||||
show('open')
|
// var buttons = $('<div style="margin: 30px"></div>').appendTo(container);
|
||||||
});
|
// var createNew = $('<button class="editor-button"><i class="fa fa-archive fa-2x"></i><i class="fa fa-plus-circle" style="position:absolute"></i><br/>Create a project</button>').appendTo(buttons);
|
||||||
|
// createNew.click(function(e) {
|
||||||
|
// e.preventDefault();
|
||||||
|
// show('create');
|
||||||
|
// });
|
||||||
|
// var openExisting = $('<button class="editor-button"><i class="fa fa-folder-open-o fa-2x"></i><br/>Open a project</button>').appendTo(buttons);
|
||||||
|
// openExisting.click(function(e) {
|
||||||
|
// e.preventDefault();
|
||||||
|
// show('open')
|
||||||
|
// });
|
||||||
return container;
|
return container;
|
||||||
},
|
},
|
||||||
buttons: [
|
buttons: [
|
||||||
|
{
|
||||||
|
// id: "clipboard-dialog-cancel",
|
||||||
|
text: "Not right now",
|
||||||
|
click: function() {
|
||||||
|
createProjectOptions = {};
|
||||||
|
$( this ).dialog( "close" );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Create your first project", // TODO: nls
|
||||||
|
class: "primary",
|
||||||
|
click: function() {
|
||||||
|
show('git-config');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'git-config': (function() {
|
||||||
|
var gitUsernameInput;
|
||||||
|
var gitEmailInput;
|
||||||
|
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("Step 1 : Setup your version control client").appendTo(body);
|
||||||
|
$('<p>').text("Node-RED uses the open source tool Git for version control. It tracks changes to your project files and lets you push them to remote repositories.").appendTo(body);
|
||||||
|
$('<p>').text("When you commit a set of changes, Git records who made the changes with a username and email address. The Username can be anything you want - it does not need to be your real name.").appendTo(body);
|
||||||
|
$('<p>').text("If your Git client is already configured, you can skip this step.").appendTo(body);
|
||||||
|
|
||||||
|
var currentGitSettings = RED.settings.get('git') || {};
|
||||||
|
currentGitSettings.user = currentGitSettings.user || {};
|
||||||
|
|
||||||
|
|
||||||
|
var row = $('<div class="form-row"></div>').appendTo(body);
|
||||||
|
$('<label for="">Username</label>').appendTo(row);
|
||||||
|
gitUsernameInput = $('<input type="text">').val(currentGitSettings.user.name||"").appendTo(row);
|
||||||
|
// $('<div style="position:relative;"></div>').text("This does not need to be your real name").appendTo(row);
|
||||||
|
|
||||||
|
row = $('<div class="form-row"></div>').appendTo(body);
|
||||||
|
$('<label for="">Email</label>').appendTo(row);
|
||||||
|
gitEmailInput = $('<input type="text">').val(currentGitSettings.user.email||"").appendTo(row);
|
||||||
|
// $('<div style="position:relative;"></div>').text("Something something email").appendTo(row);
|
||||||
|
setTimeout(function() {
|
||||||
|
gitUsernameInput.focus();
|
||||||
|
},50);
|
||||||
|
return container;
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
// id: "clipboard-dialog-cancel",
|
||||||
|
text: "Back",
|
||||||
|
click: function() {
|
||||||
|
show('welcome');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Next", // TODO: nls
|
||||||
|
class: "primary",
|
||||||
|
click: function() {
|
||||||
|
var currentGitSettings = RED.settings.get('git') || {};
|
||||||
|
currentGitSettings.user = currentGitSettings.user || {};
|
||||||
|
currentGitSettings.user.name = gitUsernameInput.val();
|
||||||
|
currentGitSettings.user.email = gitEmailInput.val();
|
||||||
|
RED.settings.set('git', currentGitSettings);
|
||||||
|
show('project-details');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
})(),
|
||||||
|
'project-details': (function() {
|
||||||
|
var projectNameInput;
|
||||||
|
var projectSummaryInput;
|
||||||
|
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("Step 2 : Create your project").appendTo(body);
|
||||||
|
$('<p>').text("A project contains your flow files, a README file, a package.json file and a settings file. It makes it much easier to share your flows with others and to collaborate on them.").appendTo(body);
|
||||||
|
$('<p>').text("You can create multiple projects and quickly switch between them from the editor.").appendTo(body);
|
||||||
|
$('<p>').text("To begin, your project needs a name and an optional description.").appendTo(body);
|
||||||
|
|
||||||
|
var validateForm = function() {
|
||||||
|
var projectName = projectNameInput.val();
|
||||||
|
var valid = true;
|
||||||
|
if (projectNameInputChanged) {
|
||||||
|
projectNameStatus.empty();
|
||||||
|
if (!/^[a-zA-Z0-9\-_]+$/.test(projectName)) {
|
||||||
|
projectNameInput.addClass("input-error");
|
||||||
|
$('<i style="margin-top: 8px;" class="fa fa-exclamation-triangle"></i>').appendTo(projectNameStatus);
|
||||||
|
projectNameValid = false;
|
||||||
|
valid = false;
|
||||||
|
} else {
|
||||||
|
projectNameInput.removeClass("input-error");
|
||||||
|
$('<i style="margin-top: 8px;" class="fa fa-check"></i>').appendTo(projectNameStatus);
|
||||||
|
projectNameValid = true;
|
||||||
|
}
|
||||||
|
projectNameLastChecked = projectName;
|
||||||
|
}
|
||||||
|
valid = projectNameValid;
|
||||||
|
$("#projects-dialog-create-name").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
var row = $('<div class="form-row"></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>').val(createProjectOptions.name||"").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");
|
||||||
|
|
||||||
|
// Empty Project
|
||||||
|
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-empty"></div>').appendTo(body);
|
||||||
|
$('<label for="projects-dialog-screen-create-project-desc">Description</label>').appendTo(row);
|
||||||
|
projectSummaryInput = $('<input id="projects-dialog-screen-create-project-desc" type="text">').val(createProjectOptions.summary||"").appendTo(row);
|
||||||
|
$('<label class="projects-edit-form-sublabel"><small>Optional</small></label>').appendTo(row);
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
projectNameInput.focus();
|
||||||
|
},50);
|
||||||
|
return container;
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: "Back",
|
||||||
|
click: function() {
|
||||||
|
show('git-config');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "projects-dialog-create-name",
|
||||||
|
disabled: true,
|
||||||
|
text: "Next", // TODO: nls
|
||||||
|
class: "primary disabled",
|
||||||
|
click: function() {
|
||||||
|
createProjectOptions.name = projectNameInput.val();
|
||||||
|
createProjectOptions.summary = projectSummaryInput.val();
|
||||||
|
createProjectOptions.files = { migrateFiles: true };
|
||||||
|
show('encryption-config');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
})(),
|
||||||
|
|
||||||
|
'encryption-config': (function() {
|
||||||
|
var emptyProjectCredentialInput;
|
||||||
|
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("Step 3 : Setup encryption of your credentials file").appendTo(body);
|
||||||
|
// $('<p>').text("Your flow credentials file can be encrypted to keep its contents secure.").appendTo(body);
|
||||||
|
|
||||||
|
if (RED.settings.flowEncryptionType === 'disabled') {
|
||||||
|
$('<p>').text("Your flow credentials file is not currently encrypted.").appendTo(body);
|
||||||
|
$('<p>').text("That means its contents, such as passwords and access tokens, can be read by anyone with access to the file.").appendTo(body);
|
||||||
|
$('<p>').text("If you want to store these credentials in a public Git repository, you must encrypt them by providing a secret key phrase.").appendTo(body);
|
||||||
|
} else {
|
||||||
|
if (RED.settings.flowEncryptionType === 'user') {
|
||||||
|
$('<p>').text("Your flow credentials file is currently encrypted using the credentialSecret property from your settings file as the key.").appendTo(body);
|
||||||
|
} else if (RED.settings.flowEncryptionType === 'system') {
|
||||||
|
$('<p>').text("Your flow credentials file is currently encrypted using a system-generated secret as the key. You should provide a new secret key for this project.").appendTo(body);
|
||||||
|
}
|
||||||
|
$('<p>').text("The secret will be copied into the settings for your new project. You can then manage the secret within the editor.").appendTo(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
// var row = $('<div class="form-row"></div>').appendTo(body);
|
||||||
|
// $('<label for="">Username</label>').appendTo(row);
|
||||||
|
// var gitUsernameInput = $('<input type="text">').val(currentGitSettings.user.name||"").appendTo(row);
|
||||||
|
// // $('<div style="position:relative;"></div>').text("This does not need to be your real name").appendTo(row);
|
||||||
|
//
|
||||||
|
// row = $('<div class="form-row"></div>').appendTo(body);
|
||||||
|
// $('<label for="">Email</label>').appendTo(row);
|
||||||
|
// var gitEmailInput = $('<input type="text">').val(currentGitSettings.user.email||"").appendTo(row);
|
||||||
|
// // $('<div style="position:relative;"></div>').text("Something something email").appendTo(row);
|
||||||
|
|
||||||
|
var validateForm = function() {
|
||||||
|
var valid = true;
|
||||||
|
var encryptionState = $("input[name=projects-encryption-type]:checked").val();
|
||||||
|
if (encryptionState === 'enabled') {
|
||||||
|
var encryptionKeyType = $("input[name=projects-encryption-key]:checked").val();
|
||||||
|
if (encryptionKeyType === 'custom') {
|
||||||
|
valid = valid && emptyProjectCredentialInput.val()!=='';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$("#projects-dialog-create-encryption").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-empty"></div>').appendTo(body);
|
||||||
|
$('<label>Credentials</label>').appendTo(row);
|
||||||
|
|
||||||
|
var credentialsBox = $('<div style="width: 550px">').appendTo(row);
|
||||||
|
var credentialsRightBox = $('<div style="min-height:150px; box-sizing: border-box; float: right; vertical-align: top; width: 331px; margin-left: -1px; padding: 15px; margin-top: -15px; border: 1px solid #ccc; border-radius: 3px; display: inline-block">').appendTo(credentialsBox);
|
||||||
|
var credentialsLeftBox = $('<div style="vertical-align: top; width: 220px; display: inline-block">').appendTo(credentialsBox);
|
||||||
|
|
||||||
|
var credentialsEnabledBox = $('<div class="form-row" style="padding: 7px 8px 3px 8px;border: 1px solid #ccc;border-radius: 4px;border-top-right-radius: 0;border-bottom-right-radius: 0;border-right-color: white;"></div>').appendTo(credentialsLeftBox);
|
||||||
|
$('<label class="projects-edit-form-inline-label" style="margin-left: 5px"><input type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" name="projects-encryption-type" value="enabled"> <i style="font-size: 1.4em; margin-right: 8px; vertical-align: middle; color: #888;" class="fa fa-lock"></i> <span style="vertical-align: middle;">Enable encryption</span></label>').appendTo(credentialsEnabledBox);
|
||||||
|
var credentialsDisabledBox = $('<div class="form-row" style="padding: 7px 8px 3px 8px;border: 1px solid white;border-radius: 4px;border-top-right-radius: 0;border-bottom-right-radius: 0;border-right-color: #ccc; "></div>').appendTo(credentialsLeftBox);
|
||||||
|
$('<label class="projects-edit-form-inline-label" style="margin-left: 5px"><input type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" name="projects-encryption-type" value="disabled"> <i style="font-size: 1.4em; margin-right: 8px; vertical-align: middle; color: #888;" class="fa fa-unlock"></i> <span style="vertical-align: middle;">Disable encryption</span></label>').appendTo(credentialsDisabledBox);
|
||||||
|
|
||||||
|
credentialsLeftBox.find("input[name=projects-encryption-type]").click(function(e) {
|
||||||
|
var val = $(this).val();
|
||||||
|
var toEnable;
|
||||||
|
var toDisable;
|
||||||
|
if (val === 'enabled') {
|
||||||
|
toEnable = credentialsEnabledBox;
|
||||||
|
toDisable = credentialsDisabledBox;
|
||||||
|
$(".projects-encryption-enabled-row").show();
|
||||||
|
$(".projects-encryption-disabled-row").hide();
|
||||||
|
if ($("input[name=projects-encryption-key]:checked").val() === 'custom') {
|
||||||
|
emptyProjectCredentialInput.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
toDisable = credentialsEnabledBox;
|
||||||
|
toEnable = credentialsDisabledBox;
|
||||||
|
$(".projects-encryption-enabled-row").hide();
|
||||||
|
$(".projects-encryption-disabled-row").show();
|
||||||
|
}
|
||||||
|
|
||||||
|
toEnable.css({
|
||||||
|
borderColor: "#ccc",
|
||||||
|
borderRightColor: "white"
|
||||||
|
});
|
||||||
|
toDisable.css({
|
||||||
|
borderColor: "white",
|
||||||
|
borderRightColor: "#ccc"
|
||||||
|
});
|
||||||
|
|
||||||
|
validateForm();
|
||||||
|
})
|
||||||
|
|
||||||
|
row = $('<div class="form-row projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
|
||||||
|
$('<label class="projects-edit-form-inline-label '+((RED.settings.flowEncryptionType !== 'user')?'disabled':'')+'" style="margin-left: 5px"><input '+((RED.settings.flowEncryptionType !== 'user')?'disabled':'')+' type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" value="default" name="projects-encryption-key"> <span style="vertical-align: middle;">Copy over existing key</span></label>').appendTo(row);
|
||||||
|
row = $('<div class="form-row projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
|
||||||
|
$('<label class="projects-edit-form-inline-label" style="margin-left: 5px"><input type="radio" style="vertical-align: middle; margin-top:0; margin-right: 10px;" value="custom" name="projects-encryption-key"> <span style="vertical-align: middle;">Use custom key</span></label>').appendTo(row);
|
||||||
|
row = $('<div class="projects-encryption-enabled-row"></div>').appendTo(credentialsRightBox);
|
||||||
|
emptyProjectCredentialInput = $('<input disabled type="password" style="margin-left: 25px; width: calc(100% - 30px);"></input>').appendTo(row);
|
||||||
|
emptyProjectCredentialInput.on("change keyup paste", validateForm);
|
||||||
|
|
||||||
|
row = $('<div class="form-row projects-encryption-disabled-row"></div>').hide().appendTo(credentialsRightBox);
|
||||||
|
$('<div class="" style="padding: 5px 20px;"><i class="fa fa-warning"></i> The credentials file will not be encrypted and its contents easily read</div>').appendTo(row);
|
||||||
|
|
||||||
|
credentialsRightBox.find("input[name=projects-encryption-key]").click(function() {
|
||||||
|
var val = $(this).val();
|
||||||
|
emptyProjectCredentialInput.attr("disabled",val === 'default');
|
||||||
|
if (val === "custom") {
|
||||||
|
emptyProjectCredentialInput.focus();
|
||||||
|
}
|
||||||
|
validateForm();
|
||||||
|
});
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
credentialsLeftBox.find("input[name=projects-encryption-type][value=enabled]").click();
|
||||||
|
if (RED.settings.flowEncryptionType !== 'user') {
|
||||||
|
credentialsRightBox.find("input[name=projects-encryption-key][value=custom]").click();
|
||||||
|
} else {
|
||||||
|
credentialsRightBox.find("input[name=projects-encryption-key][value=default]").click();
|
||||||
|
}
|
||||||
|
validateForm();
|
||||||
|
},100);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
// id: "clipboard-dialog-cancel",
|
||||||
|
text: "Back",
|
||||||
|
click: function() {
|
||||||
|
show('project-details');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "projects-dialog-create-encryption",
|
||||||
|
text: "Create project", // TODO: nls
|
||||||
|
class: "primary disabled",
|
||||||
|
disabled: true,
|
||||||
|
click: function() {
|
||||||
|
var encryptionState = $("input[name=projects-encryption-type]:checked").val();
|
||||||
|
if (encryptionState === 'enabled') {
|
||||||
|
var encryptionKeyType = $("input[name=projects-encryption-key]:checked").val();
|
||||||
|
if (encryptionKeyType === 'custom') {
|
||||||
|
createProjectOptions.credentialSecret = emptyProjectCredentialInput.val();
|
||||||
|
} else {
|
||||||
|
// If 'use existing', leave createProjectOptions.credentialSecret blank
|
||||||
|
// - that will trigger it to use the existing key
|
||||||
|
// TODO: this option should be disabled if encryption is disabled
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Disabled encryption by explicitly setting credSec to false
|
||||||
|
createProjectOptions.credentialSecret = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RED.deploy.setDeployInflight(true);
|
||||||
|
RED.projects.settings.switchProject(createProjectOptions.name);
|
||||||
|
|
||||||
|
sendRequest({
|
||||||
|
url: "projects",
|
||||||
|
type: "POST",
|
||||||
|
requireCleanWorkspace: true,
|
||||||
|
handleAuthFail: false,
|
||||||
|
responses: {
|
||||||
|
200: function(data) {
|
||||||
|
createProjectOptions = {};
|
||||||
|
show('create-success');
|
||||||
|
},
|
||||||
|
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");
|
||||||
|
},
|
||||||
|
'git_auth_failed': function(error) {
|
||||||
|
projectRepoUserInput.addClass("input-error");
|
||||||
|
projectRepoPasswordInput.addClass("input-error");
|
||||||
|
// getRepoAuthDetails(req);
|
||||||
|
console.log("git auth error",error);
|
||||||
|
},
|
||||||
|
'unexpected_error': function(error) {
|
||||||
|
console.log("unexpected_error",error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},createProjectOptions).always(function() {
|
||||||
|
RED.deploy.setDeployInflight(false);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})(),
|
||||||
|
'create-success': {
|
||||||
|
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("You have successfully created your first project!").appendTo(body);
|
||||||
|
$('<p>').text("You can now continue to use Node-RED just as you always have.").appendTo(body);
|
||||||
|
$('<p>').text("The 'info' tab in the sidebar shows you what your current active project is. "+
|
||||||
|
"The button next to the name can be used to access the project settings view.").appendTo(body);
|
||||||
|
$('<p>').text("The new 'history' tab in the sidebar can be used to view files that have changed "+
|
||||||
|
"in your project and to commit them. It shows you a complete history of your commits and "+
|
||||||
|
"allows you to push your changes to a remote repository.").appendTo(body);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
},
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
text: "Done",
|
||||||
|
click: function() {
|
||||||
|
$( this ).dialog( "close" );
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
'create': (function() {
|
'create': (function() {
|
||||||
@ -245,6 +656,9 @@ RED.projects = (function() {
|
|||||||
toDisable = credentialsDisabledBox;
|
toDisable = credentialsDisabledBox;
|
||||||
$(".projects-encryption-enabled-row").show();
|
$(".projects-encryption-enabled-row").show();
|
||||||
$(".projects-encryption-disabled-row").hide();
|
$(".projects-encryption-disabled-row").hide();
|
||||||
|
if ($("input[name=projects-encryption-key]:checked").val() === 'custom') {
|
||||||
|
emptyProjectCredentialInput.focus();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
toDisable = credentialsEnabledBox;
|
toDisable = credentialsEnabledBox;
|
||||||
toEnable = credentialsDisabledBox;
|
toEnable = credentialsDisabledBox;
|
||||||
@ -278,6 +692,9 @@ RED.projects = (function() {
|
|||||||
credentialsRightBox.find("input[name=projects-encryption-key]").click(function() {
|
credentialsRightBox.find("input[name=projects-encryption-key]").click(function() {
|
||||||
var val = $(this).val();
|
var val = $(this).val();
|
||||||
emptyProjectCredentialInput.attr("disabled",val === 'default');
|
emptyProjectCredentialInput.attr("disabled",val === 'default');
|
||||||
|
if (val === "custom") {
|
||||||
|
emptyProjectCredentialInput.focus();
|
||||||
|
}
|
||||||
validateForm();
|
validateForm();
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -633,7 +1050,7 @@ RED.projects = (function() {
|
|||||||
RED.projects.init();
|
RED.projects.init();
|
||||||
}
|
}
|
||||||
var screen = screens[s];
|
var screen = screens[s];
|
||||||
var container = screen.content();
|
var container = screen.content(options);
|
||||||
|
|
||||||
dialogBody.empty();
|
dialogBody.empty();
|
||||||
dialog.dialog('option','buttons',screen.buttons);
|
dialog.dialog('option','buttons',screen.buttons);
|
||||||
@ -1030,9 +1447,13 @@ RED.projects = (function() {
|
|||||||
// updateProjectDependencies();
|
// updateProjectDependencies();
|
||||||
RED.sidebar.versionControl.refresh(true);
|
RED.sidebar.versionControl.refresh(true);
|
||||||
if (done) {
|
if (done) {
|
||||||
done();
|
done(activeProject);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
if (done) {
|
||||||
|
done(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1045,7 +1466,11 @@ RED.projects = (function() {
|
|||||||
show('welcome');
|
show('welcome');
|
||||||
},
|
},
|
||||||
newProject: function() {
|
newProject: function() {
|
||||||
|
if (!activeProject) {
|
||||||
|
show('welcome');
|
||||||
|
} else {
|
||||||
show('create')
|
show('create')
|
||||||
|
}
|
||||||
},
|
},
|
||||||
selectProject: function() {
|
selectProject: function() {
|
||||||
show('open')
|
show('open')
|
||||||
|
@ -897,6 +897,7 @@ RED.sidebar.versionControl = (function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('<div class="component-shade sidebar-version-control-shade">').appendTo(sidebarContent);
|
||||||
|
|
||||||
RED.sidebar.addTab({
|
RED.sidebar.addTab({
|
||||||
id: "version-control",
|
id: "version-control",
|
||||||
@ -1223,8 +1224,10 @@ RED.sidebar.versionControl = (function() {
|
|||||||
$("#sidebar-version-control-repo-status-button").hide();
|
$("#sidebar-version-control-repo-status-button").hide();
|
||||||
}
|
}
|
||||||
refreshInProgress = false;
|
refreshInProgress = false;
|
||||||
|
$('.sidebar-version-control-shade').hide();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
$('.sidebar-version-control-shade').show();
|
||||||
unstagedChangesList.editableList('empty');
|
unstagedChangesList.editableList('empty');
|
||||||
stagedChangesList.editableList('empty');
|
stagedChangesList.editableList('empty');
|
||||||
unmergedChangesList.editableList('empty');
|
unmergedChangesList.editableList('empty');
|
||||||
|
@ -66,4 +66,4 @@ $editor-button-background-primary-hover: #6E0A1E;
|
|||||||
$editor-button-color: #999;
|
$editor-button-color: #999;
|
||||||
$editor-button-background: #fff;
|
$editor-button-background: #fff;
|
||||||
|
|
||||||
$shade-color: rgba(200,200,200,0.5);
|
$shade-color: rgba(160,160,160,0.5);
|
||||||
|
@ -501,6 +501,11 @@ textarea.span1,
|
|||||||
padding-top: 5px;
|
padding-top: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label.disabled {
|
||||||
|
color: #bbb !important;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
input[disabled],
|
input[disabled],
|
||||||
select[disabled],
|
select[disabled],
|
||||||
textarea[disabled],
|
textarea[disabled],
|
||||||
|
@ -143,3 +143,10 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.ui-widget-overlay {
|
||||||
|
@include shade;
|
||||||
|
z-index: 100;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
@ -107,6 +107,33 @@
|
|||||||
|
|
||||||
}
|
}
|
||||||
.projects-dialog-screen-start {
|
.projects-dialog-screen-start {
|
||||||
|
.projects-dialog-screen-start-hero {
|
||||||
|
// background: url(https://nodered.org/images/title-wave.png) no-repeat 0% 100% #8f0000;
|
||||||
|
// background-size: contain;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 2em;
|
||||||
|
padding: 10px;
|
||||||
|
min-height: 60px;
|
||||||
|
color: #555;
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #f0f0f0;
|
||||||
|
font-size: 2em;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.projects-dialog-screen-start-body {
|
||||||
|
min-height: 400px;
|
||||||
|
line-height: 1.6em;
|
||||||
|
p {
|
||||||
|
font-size: 1.1em;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
p:first-child {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
button.editor-button {
|
button.editor-button {
|
||||||
width: calc(50% - 40px);
|
width: calc(50% - 40px);
|
||||||
margin: 20px;
|
margin: 20px;
|
||||||
|
@ -42,9 +42,11 @@ module.exports = {
|
|||||||
runtime.storage.projects.listProjects(req.user, req.user).then(function(list) {
|
runtime.storage.projects.listProjects(req.user, req.user).then(function(list) {
|
||||||
var active = runtime.storage.projects.getActiveProject(req.user);
|
var active = runtime.storage.projects.getActiveProject(req.user);
|
||||||
var response = {
|
var response = {
|
||||||
active: active.name,
|
|
||||||
projects: list
|
projects: list
|
||||||
};
|
};
|
||||||
|
if (active) {
|
||||||
|
response.active = active.name;
|
||||||
|
}
|
||||||
res.json(response);
|
res.json(response);
|
||||||
}).catch(function(err) {
|
}).catch(function(err) {
|
||||||
console.log(err.stack);
|
console.log(err.stack);
|
||||||
|
@ -49,6 +49,8 @@ module.exports = {
|
|||||||
safeSettings.editorTheme.palette.editable = false;
|
safeSettings.editorTheme.palette.editable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
safeSettings.flowEncryptionType = runtime.nodes.getCredentialKeyType();
|
||||||
|
|
||||||
settings.exportNodeSettings(safeSettings);
|
settings.exportNodeSettings(safeSettings);
|
||||||
res.json(safeSettings);
|
res.json(safeSettings);
|
||||||
},
|
},
|
||||||
|
@ -27,6 +27,7 @@ var dirty = false;
|
|||||||
|
|
||||||
var removeDefaultKey = false;
|
var removeDefaultKey = false;
|
||||||
var encryptionEnabled = null;
|
var encryptionEnabled = null;
|
||||||
|
var encryptionKeyType; // disabled, system, user, project
|
||||||
var encryptionAlgorithm = "aes-256-ctr";
|
var encryptionAlgorithm = "aes-256-ctr";
|
||||||
var encryptionKey;
|
var encryptionKey;
|
||||||
|
|
||||||
@ -38,6 +39,11 @@ function decryptCredentials(key,credentials) {
|
|||||||
var decrypted = decipher.update(creds, 'base64', 'utf8') + decipher.final('utf8');
|
var decrypted = decipher.update(creds, 'base64', 'utf8') + decipher.final('utf8');
|
||||||
return JSON.parse(decrypted);
|
return JSON.parse(decrypted);
|
||||||
}
|
}
|
||||||
|
function encryptCredentials(key,credentials) {
|
||||||
|
var initVector = crypto.randomBytes(16);
|
||||||
|
var cipher = crypto.createCipheriv(encryptionAlgorithm, key, initVector);
|
||||||
|
return {"$":initVector.toString('hex') + cipher.update(JSON.stringify(credentials), 'utf8', 'base64') + cipher.final('base64')};
|
||||||
|
}
|
||||||
|
|
||||||
var api = module.exports = {
|
var api = module.exports = {
|
||||||
init: function(_runtime) {
|
init: function(_runtime) {
|
||||||
@ -55,12 +61,46 @@ var api = module.exports = {
|
|||||||
*/
|
*/
|
||||||
load: function (credentials) {
|
load: function (credentials) {
|
||||||
dirty = false;
|
dirty = false;
|
||||||
/*
|
|
||||||
- if encryptionEnabled === null, check the current configuration
|
|
||||||
*/
|
|
||||||
var credentialsEncrypted = credentials.hasOwnProperty("$") && Object.keys(credentials).length === 1;
|
var credentialsEncrypted = credentials.hasOwnProperty("$") && Object.keys(credentials).length === 1;
|
||||||
|
|
||||||
|
// Case 1: Active Project in place
|
||||||
|
// - use whatever its config says
|
||||||
|
|
||||||
|
// Case 2: _credentialSecret unset, credentialSecret unset
|
||||||
|
// - generate _credentialSecret and encrypt
|
||||||
|
|
||||||
|
// Case 3: _credentialSecret set, credentialSecret set
|
||||||
|
// - migrate from _credentialSecret to credentialSecret
|
||||||
|
// - delete _credentialSecret
|
||||||
|
|
||||||
|
// Case 4: credentialSecret set
|
||||||
|
// - use it
|
||||||
|
|
||||||
var setupEncryptionPromise = when.resolve();
|
var setupEncryptionPromise = when.resolve();
|
||||||
// if (encryptionEnabled === null) {
|
|
||||||
|
var projectKey = false;
|
||||||
|
var activeProject;
|
||||||
|
encryptionKeyType = "";
|
||||||
|
|
||||||
|
if (runtime.storage && runtime.storage.projects) {
|
||||||
|
// projects enabled
|
||||||
|
activeProject = runtime.storage.projects.getActiveProject();
|
||||||
|
if (activeProject) {
|
||||||
|
projectKey = activeProject.credentialSecret;
|
||||||
|
if (!projectKey) {
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : using active project key - disabled");
|
||||||
|
encryptionKeyType = "disabled";
|
||||||
|
encryptionEnabled = false;
|
||||||
|
} else {
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : using active project key");
|
||||||
|
encryptionKeyType = "project";
|
||||||
|
encryptionKey = crypto.createHash('sha256').update(projectKey).digest();
|
||||||
|
encryptionEnabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encryptionKeyType === '') {
|
||||||
var defaultKey;
|
var defaultKey;
|
||||||
try {
|
try {
|
||||||
defaultKey = settings.get('_credentialSecret');
|
defaultKey = settings.get('_credentialSecret');
|
||||||
@ -68,6 +108,7 @@ var api = module.exports = {
|
|||||||
}
|
}
|
||||||
if (defaultKey) {
|
if (defaultKey) {
|
||||||
defaultKey = crypto.createHash('sha256').update(defaultKey).digest();
|
defaultKey = crypto.createHash('sha256').update(defaultKey).digest();
|
||||||
|
encryptionKeyType = "system";
|
||||||
}
|
}
|
||||||
var userKey;
|
var userKey;
|
||||||
try {
|
try {
|
||||||
@ -76,34 +117,13 @@ var api = module.exports = {
|
|||||||
userKey = false;
|
userKey = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var projectKey = false;
|
|
||||||
var activeProject;
|
|
||||||
if (runtime.storage && runtime.storage.projects) {
|
|
||||||
// projects enabled
|
|
||||||
activeProject = runtime.storage.projects.getActiveProject();
|
|
||||||
if (activeProject) {
|
|
||||||
projectKey = activeProject.credentialSecret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (projectKey) {
|
|
||||||
log.debug("red/runtime/nodes/credentials.load : using active project key - ignoring user provided key");
|
|
||||||
userKey = projectKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Need to consider the various migration scenarios from no-project to project
|
|
||||||
// - _credentialSecret exists, projectKey exists
|
|
||||||
// - _credentialSecret does not exist, projectKey exists
|
|
||||||
// - userKey exists, projectKey exists
|
|
||||||
if (userKey === false) {
|
if (userKey === false) {
|
||||||
|
encryptionKeyType = "disabled";
|
||||||
log.debug("red/runtime/nodes/credentials.load : user disabled encryption");
|
log.debug("red/runtime/nodes/credentials.load : user disabled encryption");
|
||||||
// User has disabled encryption
|
// User has disabled encryption
|
||||||
encryptionEnabled = false;
|
encryptionEnabled = false;
|
||||||
// Check if we have a generated _credSecret to decrypt with and remove
|
// Check if we have a generated _credSecret to decrypt with and remove
|
||||||
if (defaultKey) {
|
if (defaultKey) {
|
||||||
console.log("****************************************************************");
|
|
||||||
console.log("* Oh oh - default key present. We don't handle this well today *");
|
|
||||||
console.log("****************************************************************");
|
|
||||||
log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
|
log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
|
||||||
if (credentialsEncrypted) {
|
if (credentialsEncrypted) {
|
||||||
try {
|
try {
|
||||||
@ -123,11 +143,14 @@ var api = module.exports = {
|
|||||||
if (!projectKey) {
|
if (!projectKey) {
|
||||||
log.debug("red/runtime/nodes/credentials.load : user provided key");
|
log.debug("red/runtime/nodes/credentials.load : user provided key");
|
||||||
}
|
}
|
||||||
|
if (encryptionKeyType !== 'project') {
|
||||||
|
encryptionKeyType = 'user';
|
||||||
|
}
|
||||||
// User has provided own encryption key, get the 32-byte hash of it
|
// User has provided own encryption key, get the 32-byte hash of it
|
||||||
encryptionKey = crypto.createHash('sha256').update(userKey).digest();
|
encryptionKey = crypto.createHash('sha256').update(userKey).digest();
|
||||||
encryptionEnabled = true;
|
encryptionEnabled = true;
|
||||||
|
|
||||||
if (defaultKey) {
|
if (encryptionKeyType !== 'project' && defaultKey) {
|
||||||
log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
|
log.debug("red/runtime/nodes/credentials.load : default key present. Will migrate");
|
||||||
// User has provided their own key, but we already have a default key
|
// User has provided their own key, but we already have a default key
|
||||||
// Decrypt using default key
|
// Decrypt using default key
|
||||||
@ -148,6 +171,9 @@ var api = module.exports = {
|
|||||||
} else {
|
} else {
|
||||||
log.debug("red/runtime/nodes/credentials.load : no user key present");
|
log.debug("red/runtime/nodes/credentials.load : no user key present");
|
||||||
// User has not provide their own key
|
// User has not provide their own key
|
||||||
|
if (encryptionKeyType !== 'project') {
|
||||||
|
encryptionKeyType = 'system';
|
||||||
|
}
|
||||||
encryptionKey = defaultKey;
|
encryptionKey = defaultKey;
|
||||||
encryptionEnabled = true;
|
encryptionEnabled = true;
|
||||||
if (encryptionKey === undefined) {
|
if (encryptionKey === undefined) {
|
||||||
@ -169,13 +195,20 @@ var api = module.exports = {
|
|||||||
log.debug("red/runtime/nodes/credentials.load : using default key");
|
log.debug("red/runtime/nodes/credentials.load : using default key");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//}
|
}
|
||||||
|
|
||||||
if (encryptionEnabled && !dirty) {
|
if (encryptionEnabled && !dirty) {
|
||||||
encryptedCredentials = credentials;
|
encryptedCredentials = credentials;
|
||||||
}
|
}
|
||||||
|
log.debug("red/runtime/nodes/credentials.load : keyType="+encryptionKeyType);
|
||||||
return setupEncryptionPromise.then(function() {
|
return setupEncryptionPromise.then(function() {
|
||||||
var clearInvalidFlag = false;
|
var clearInvalidFlag = false;
|
||||||
if (credentials.hasOwnProperty("$")) {
|
if (credentials.hasOwnProperty("$")) {
|
||||||
|
if (encryptionEnabled === false) {
|
||||||
|
var error = new Error("Failed to decrypt credentials");
|
||||||
|
error.code = "credentials_load_failed";
|
||||||
|
return when.reject(error);
|
||||||
|
}
|
||||||
// These are encrypted credentials
|
// These are encrypted credentials
|
||||||
try {
|
try {
|
||||||
credentialCache = decryptCredentials(encryptionKey,credentials)
|
credentialCache = decryptCredentials(encryptionKey,credentials)
|
||||||
@ -343,9 +376,20 @@ var api = module.exports = {
|
|||||||
return dirty;
|
return dirty;
|
||||||
},
|
},
|
||||||
setKey: function(key) {
|
setKey: function(key) {
|
||||||
|
if (key) {
|
||||||
encryptionKey = crypto.createHash('sha256').update(key).digest();
|
encryptionKey = crypto.createHash('sha256').update(key).digest();
|
||||||
encryptionEnabled = true;
|
encryptionEnabled = true;
|
||||||
dirty = true;
|
dirty = true;
|
||||||
|
encryptionKeyType = "project";
|
||||||
|
} else {
|
||||||
|
encryptionKey = null;
|
||||||
|
encryptionEnabled = false;
|
||||||
|
dirty = true;
|
||||||
|
encryptionKeyType = "disabled";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getKeyType: function() {
|
||||||
|
return encryptionKeyType;
|
||||||
},
|
},
|
||||||
export: function() {
|
export: function() {
|
||||||
var result = credentialCache;
|
var result = credentialCache;
|
||||||
@ -354,9 +398,7 @@ var api = module.exports = {
|
|||||||
if (dirty) {
|
if (dirty) {
|
||||||
try {
|
try {
|
||||||
log.debug("red/runtime/nodes/credentials.export : encrypting");
|
log.debug("red/runtime/nodes/credentials.export : encrypting");
|
||||||
var initVector = crypto.randomBytes(16);
|
result = encryptCredentials(encryptionKey, credentialCache);
|
||||||
var cipher = crypto.createCipheriv(encryptionAlgorithm, encryptionKey, initVector);
|
|
||||||
result = {"$":initVector.toString('hex') + cipher.update(JSON.stringify(credentialCache), 'utf8', 'base64') + cipher.final('base64')};
|
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
log.warn(log._("nodes.credentials.error-saving",{message:err.toString()}))
|
log.warn(log._("nodes.credentials.error-saving",{message:err.toString()}))
|
||||||
}
|
}
|
||||||
|
@ -175,5 +175,6 @@ module.exports = {
|
|||||||
getCredentialDefinition: credentials.getDefinition,
|
getCredentialDefinition: credentials.getDefinition,
|
||||||
setCredentialSecret: credentials.setKey,
|
setCredentialSecret: credentials.setKey,
|
||||||
clearCredentials: credentials.clear,
|
clearCredentials: credentials.clear,
|
||||||
exportCredentials: credentials.export
|
exportCredentials: credentials.export,
|
||||||
|
getCredentialKeyType: credentials.getKeyType
|
||||||
};
|
};
|
||||||
|
@ -606,27 +606,53 @@ function createDefaultProject(user, project) {
|
|||||||
// Create a basic skeleton of a project
|
// Create a basic skeleton of a project
|
||||||
return gitTools.initRepo(projectPath).then(function() {
|
return gitTools.initRepo(projectPath).then(function() {
|
||||||
var promises = [];
|
var promises = [];
|
||||||
|
var files = Object.keys(defaultFileSet);
|
||||||
|
if (project.files) {
|
||||||
|
if (project.files.flow && !/\.\./.test(project.files.flow)) {
|
||||||
|
var flowFilePath;
|
||||||
|
var credsFilePath;
|
||||||
|
|
||||||
|
if (project.files.migrateFiles) {
|
||||||
|
var baseFlowFileName = fspath.basename(project.files.flow);
|
||||||
|
var baseCredentialFileName = fspath.basename(project.files.credentials);
|
||||||
|
files.push(baseFlowFileName);
|
||||||
|
files.push(baseCredentialFileName);
|
||||||
|
flowFilePath = fspath.join(projectPath,baseFlowFileName);
|
||||||
|
credsFilePath = fspath.join(projectPath,baseCredentialFileName);
|
||||||
|
log.trace("Migrating "+project.files.flow+" to "+flowFilePath);
|
||||||
|
log.trace("Migrating "+project.files.credentials+" to "+credsFilePath);
|
||||||
|
promises.push(fs.copy(project.files.flow,flowFilePath));
|
||||||
|
runtime.nodes.setCredentialSecret(project.credentialSecret);
|
||||||
|
promises.push(runtime.nodes.exportCredentials().then(function(creds) {
|
||||||
|
var credentialData;
|
||||||
|
if (settings.flowFilePretty) {
|
||||||
|
credentialData = JSON.stringify(creds,null,4);
|
||||||
|
} else {
|
||||||
|
credentialData = JSON.stringify(creds);
|
||||||
|
}
|
||||||
|
return util.writeFile(credsFilePath,credentialData);
|
||||||
|
}));
|
||||||
|
delete project.files.migrateFiles;
|
||||||
|
project.files.flow = baseFlowFileName;
|
||||||
|
project.files.credentials = baseCredentialFileName;
|
||||||
|
} else {
|
||||||
|
project.files.credentials = project.files.credentials || getCredentialsFilename(project.files.flow);
|
||||||
|
files.push(project.files.flow);
|
||||||
|
files.push(project.files.credentials);
|
||||||
|
flowFilePath = fspath.join(projectPath,project.files.flow);
|
||||||
|
credsFilePath = getCredentialsFilename(flowFilePath);
|
||||||
|
promises.push(util.writeFile(flowFilePath,"[]"));
|
||||||
|
promises.push(util.writeFile(credsFilePath,"{}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (var file in defaultFileSet) {
|
for (var file in defaultFileSet) {
|
||||||
if (defaultFileSet.hasOwnProperty(file)) {
|
if (defaultFileSet.hasOwnProperty(file)) {
|
||||||
promises.push(util.writeFile(fspath.join(projectPath,file),defaultFileSet[file](project)));
|
promises.push(util.writeFile(fspath.join(projectPath,file),defaultFileSet[file](project)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (project.files) {
|
|
||||||
if (project.files.flow && !/\.\./.test(project.files.flow)) {
|
|
||||||
var flowFilePath = fspath.join(projectPath,project.files.flow);
|
|
||||||
promises.push(util.writeFile(flowFilePath,"[]"));
|
|
||||||
var credsFilePath = getCredentialsFilename(flowFilePath);
|
|
||||||
promises.push(util.writeFile(credsFilePath,"{}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return when.all(promises).then(function() {
|
return when.all(promises).then(function() {
|
||||||
var files = Object.keys(defaultFileSet);
|
|
||||||
if (project.files) {
|
|
||||||
if (project.files.flow && !/\.\./.test(project.files.flow)) {
|
|
||||||
files.push(project.files.flow);
|
|
||||||
files.push(getCredentialsFilename(flowFilePath))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return gitTools.stageFile(projectPath,files);
|
return gitTools.stageFile(projectPath,files);
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
return gitTools.commit(projectPath,"Create project");
|
return gitTools.commit(projectPath,"Create project");
|
||||||
|
@ -14,18 +14,6 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
**/
|
**/
|
||||||
|
|
||||||
|
|
||||||
var fspath = require("path");
|
|
||||||
|
|
||||||
function getCredentialsFilename(filename) {
|
|
||||||
// TODO: DRY - ./index.js
|
|
||||||
var ffDir = fspath.dirname(filename);
|
|
||||||
var ffExt = fspath.extname(filename);
|
|
||||||
var ffBase = fspath.basename(filename,ffExt);
|
|
||||||
return fspath.join(ffDir,ffBase+"_cred"+ffExt);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
"package.json": function(project) {
|
"package.json": function(project) {
|
||||||
var package = {
|
var package = {
|
||||||
@ -41,7 +29,7 @@ module.exports = {
|
|||||||
if (project.files) {
|
if (project.files) {
|
||||||
if (project.files.flow) {
|
if (project.files.flow) {
|
||||||
package['node-red'].settings.flowFile = project.files.flow;
|
package['node-red'].settings.flowFile = project.files.flow;
|
||||||
package['node-red'].settings.credentialsFile = getCredentialsFilename(project.files.flow);
|
package['node-red'].settings.credentialsFile = project.files.credentials;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return JSON.stringify(package,"",4);
|
return JSON.stringify(package,"",4);
|
||||||
@ -50,7 +38,5 @@ module.exports = {
|
|||||||
return project.name+"\n"+("=".repeat(project.name.length))+"\n\n"+(project.summary||"A Node-RED Project")+"\n\n";
|
return project.name+"\n"+("=".repeat(project.name.length))+"\n\n"+(project.summary||"A Node-RED Project")+"\n\n";
|
||||||
},
|
},
|
||||||
"settings.json": function() { return "{}" },
|
"settings.json": function() { return "{}" },
|
||||||
// "flow.json": function() { return "[]" },
|
|
||||||
// "flow_cred.json": function() { return "{}" },
|
|
||||||
".gitignore": function() { return "*.backup" ;}
|
".gitignore": function() { return "*.backup" ;}
|
||||||
}
|
}
|
||||||
|
@ -270,6 +270,22 @@ function reloadActiveProject(action) {
|
|||||||
}
|
}
|
||||||
function createProject(user, metadata) {
|
function createProject(user, metadata) {
|
||||||
// var userSettings = getUserGitSettings(user);
|
// var userSettings = getUserGitSettings(user);
|
||||||
|
if (metadata.files && metadata.files.migrateFiles) {
|
||||||
|
// We expect there to be no active project in this scenario
|
||||||
|
if (activeProject) {
|
||||||
|
throw new Error("Cannot migrate as there is an active project");
|
||||||
|
}
|
||||||
|
var currentEncryptionKey = settings.get('credentialSecret');
|
||||||
|
if (currentEncryptionKey === undefined) {
|
||||||
|
currentEncryptionKey = settings.get('_credentialSecret');
|
||||||
|
}
|
||||||
|
if (!metadata.hasOwnProperty('credentialSecret')) {
|
||||||
|
metadata.credentialSecret = currentEncryptionKey;
|
||||||
|
}
|
||||||
|
metadata.files.flow = flowsFullPath;
|
||||||
|
metadata.files.credentials = credentialsFile;
|
||||||
|
metadata.files.credentialSecret = currentEncryptionKey;
|
||||||
|
}
|
||||||
return Projects.create(null,metadata).then(function(p) {
|
return Projects.create(null,metadata).then(function(p) {
|
||||||
return setActiveProject(user, p.name);
|
return setActiveProject(user, p.name);
|
||||||
}).then(function() {
|
}).then(function() {
|
||||||
@ -281,7 +297,7 @@ function setActiveProject(user, projectName) {
|
|||||||
var globalProjectSettings = settings.get("projects");
|
var globalProjectSettings = settings.get("projects");
|
||||||
globalProjectSettings.activeProject = project.name;
|
globalProjectSettings.activeProject = project.name;
|
||||||
return settings.set("projects",globalProjectSettings).then(function() {
|
return settings.set("projects",globalProjectSettings).then(function() {
|
||||||
log.info(log._("storage.localfilesystem.projects.changing-project",{project:activeProject||"none"}));
|
log.info(log._("storage.localfilesystem.projects.changing-project",{project:(activeProject&&activeProject.name)||"none"}));
|
||||||
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
|
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
|
||||||
// console.log("Updated file targets to");
|
// console.log("Updated file targets to");
|
||||||
// console.log(flowsFullPath)
|
// console.log(flowsFullPath)
|
||||||
|
Loading…
Reference in New Issue
Block a user