1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Add clone project to welcome screen

This commit is contained in:
Nick O'Leary 2018-05-02 13:38:50 +01:00
parent ae4b1b17a9
commit e3520309fc
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
3 changed files with 393 additions and 17 deletions

View File

@ -80,6 +80,26 @@ RED.projects = (function() {
$('<p>').text("To get started you can create your first project or clone an existing project from a git repository.").appendTo(body); $('<p>').text("To get started you can create your first project or clone an existing project from a git repository.").appendTo(body);
$('<p>').text("If you are not sure, you can skip this for now. You will still be able to create your first project from the 'Projects' menu at any time.").appendTo(body); $('<p>').text("If you are not sure, you can skip this for now. You will still be able to create your first project from the 'Projects' menu at any time.").appendTo(body);
var row = $('<div style="text-align: center"></div>').appendTo(body);
var createAsEmpty = $('<button data-type="empty" class="editor-button projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-asterisk"></i><br/>Create Project</button>').appendTo(row);
var createAsClone = $('<button data-type="clone" class="editor-button projects-dialog-screen-create-type"><i class="fa fa-archive fa-2x"></i><i style="position: absolute;" class="fa fa-git"></i><br/>Clone Repository</button>').appendTo(row);
createAsEmpty.click(function(e) {
e.preventDefault();
createProjectOptions = {
action: "create"
}
show('git-config');
})
createAsClone.click(function(e) {
e.preventDefault();
createProjectOptions = {
action: "clone"
}
show('git-config');
})
return container; return container;
}, },
buttons: [ buttons: [
@ -90,13 +110,6 @@ RED.projects = (function() {
createProjectOptions = {}; createProjectOptions = {};
$( this ).dialog( "close" ); $( this ).dialog( "close" );
} }
},
{
text: "Create your first project", // TODO: nls
class: "primary",
click: function() {
show('git-config');
}
} }
] ]
}, },
@ -170,7 +183,11 @@ RED.projects = (function() {
currentGitSettings.user.name = gitUsernameInput.val(); currentGitSettings.user.name = gitUsernameInput.val();
currentGitSettings.user.email = gitEmailInput.val(); currentGitSettings.user.email = gitEmailInput.val();
RED.settings.set('git', currentGitSettings); RED.settings.set('git', currentGitSettings);
if (createProjectOptions.action === "create") {
show('project-details'); show('project-details');
} else if (createProjectOptions.action === "clone") {
show('clone-project');
}
} }
} }
] ]
@ -303,6 +320,365 @@ RED.projects = (function() {
} }
}; };
})(), })(),
'clone-project': (function() {
var projectNameInput;
var projectSummaryInput;
var projectFlowFileInput;
var projectSecretInput;
var projectSecretSelect;
var copyProject;
var projectRepoInput;
var projectCloneSecret;
var emptyProjectCredentialInput;
var projectRepoUserInput;
var projectRepoPasswordInput;
var projectNameSublabel;
var projectRepoSSHKeySelect;
var projectRepoPassphrase;
var projectRepoRemoteName
var projectRepoBranch;
var selectedProject;
return {
content: function(options) {
var container = $('<div class="projects-dialog-screen-start"></div>');
migrateProjectHeader.appendTo(container);
var body = $('<div class="projects-dialog-screen-start-body"></div>').appendTo(container);
$('<p>').text("Clone a project").appendTo(body);
$('<p>').text("If you already have a git repository containing a project, you can clone it to get started.").appendTo(body);
var projectList = null;
var pendingFormValidation = false;
$.getJSON("projects", function(data) {
projectList = {};
data.projects.forEach(function(p) {
projectList[p] = true;
if (pendingFormValidation) {
pendingFormValidation = false;
validateForm();
}
})
});
var validateForm = function() {
var projectName = projectNameInput.val();
var valid = true;
if (projectNameInputChanged) {
if (projectList === null) {
pendingFormValidation = true;
return;
}
projectNameStatus.empty();
if (!/^[a-zA-Z0-9\-_]+$/.test(projectName) || projectList[projectName]) {
projectNameInput.addClass("input-error");
$('<i style="margin-top: 8px;" class="fa fa-exclamation-triangle"></i>').appendTo(projectNameStatus);
projectNameValid = false;
valid = false;
if (projectList[projectName]) {
projectNameSublabel.text("Project already exists");
} else {
projectNameSublabel.text("Must contain only A-Z 0-9 _ -");
}
} else {
projectNameInput.removeClass("input-error");
$('<i style="margin-top: 8px;" class="fa fa-check"></i>').appendTo(projectNameStatus);
projectNameSublabel.text("Must contain only A-Z 0-9 _ -");
projectNameValid = true;
}
projectNameLastChecked = projectName;
}
valid = projectNameValid;
var repo = projectRepoInput.val();
// var validRepo = /^(?:file|git|ssh|https?|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?[\w\.@:\/~_-]+(?:\/?|\#[\d\w\.\-_]+?)$/.test(repo);
var validRepo = repo.length > 0 && !/\s/.test(repo);
if (/^https?:\/\/[^/]+@/i.test(repo)) {
$("#projects-dialog-screen-create-project-repo-label small").text("Do not include the username/password in the url");
validRepo = false;
}
if (!validRepo) {
if (projectRepoChanged) {
projectRepoInput.addClass("input-error");
}
valid = false;
} else {
projectRepoInput.removeClass("input-error");
}
if (/^https?:\/\//.test(repo)) {
$(".projects-dialog-screen-create-row-creds").show();
$(".projects-dialog-screen-create-row-sshkey").hide();
} else if (/^(?:ssh|[\S]+?@[\S]+?):(?:\/\/)?/.test(repo)) {
$(".projects-dialog-screen-create-row-creds").hide();
$(".projects-dialog-screen-create-row-sshkey").show();
// if ( !getSelectedSSHKey(projectRepoSSHKeySelect) ) {
// valid = false;
// }
} else {
$(".projects-dialog-screen-create-row-creds").hide();
$(".projects-dialog-screen-create-row-sshkey").hide();
}
$("#projects-dialog-clone-project").prop('disabled',!valid).toggleClass('disabled ui-button-disabled ui-state-disabled',!valid);
}
var row;
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-empty projects-dialog-screen-create-row-clone"></div>').appendTo(body);
$('<label for="projects-dialog-screen-create-project-name">Project name</label>').appendTo(row);
var subrow = $('<div style="position:relative;"></div>').appendTo(row);
projectNameInput = $('<input id="projects-dialog-screen-create-project-name" type="text"></input>').appendTo(subrow);
var projectNameStatus = $('<div class="projects-dialog-screen-input-status"></div>').appendTo(subrow);
var projectNameInputChanged = false;
var projectNameLastChecked = "";
var projectNameValid;
var checkProjectName;
var autoInsertedName = "";
projectNameInput.on("change keyup paste",function() {
projectNameInputChanged = (projectNameInput.val() !== projectNameLastChecked);
if (checkProjectName) {
clearTimeout(checkProjectName);
} else if (projectNameInputChanged) {
projectNameStatus.empty();
$('<img src="red/images/spin.svg"/>').appendTo(projectNameStatus);
if (projectNameInput.val() === '') {
validateForm();
return;
}
}
checkProjectName = setTimeout(function() {
validateForm();
checkProjectName = null;
},300)
});
projectNameSublabel = $('<label class="projects-edit-form-sublabel"><small>Must contain only A-Z 0-9 _ -</small></label>').appendTo(row).find("small");
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-clone"></div>').appendTo(body);
$('<label for="projects-dialog-screen-create-project-repo">Git repository URL</label>').appendTo(row);
projectRepoInput = $('<input id="projects-dialog-screen-create-project-repo" type="text" placeholder="https://git.example.com/path/my-project.git"></input>').appendTo(row);
$('<label id="projects-dialog-screen-create-project-repo-label" class="projects-edit-form-sublabel"><small>https://, ssh:// or file://</small></label>').appendTo(row);
var projectRepoChanged = false;
var lastProjectRepo = "";
projectRepoInput.on("change keyup paste",function() {
projectRepoChanged = true;
var repo = $(this).val();
if (lastProjectRepo !== repo) {
$("#projects-dialog-screen-create-project-repo-label small").text("https://, ssh:// or file://");
}
lastProjectRepo = repo;
var m = /\/([^/]+?)(?:\.git)?$/.exec(repo);
if (m) {
var projectName = projectNameInput.val();
if (projectName === "" || projectName === autoInsertedName) {
autoInsertedName = m[1];
projectNameInput.val(autoInsertedName);
projectNameInput.change();
}
}
validateForm();
});
var cloneAuthRows = $('<div class="projects-dialog-screen-create-row"></div>').appendTo(body);
row = $('<div class="form-row projects-dialog-screen-create-row-auth-error"></div>').hide().appendTo(cloneAuthRows);
$('<div><i class="fa fa-warning"></i> Authentication failed</div>').appendTo(row);
// Repo credentials - username/password ----------------
row = $('<div class="hide form-row projects-dialog-screen-create-row-creds"></div>').hide().appendTo(cloneAuthRows);
var subrow = $('<div style="width: calc(50% - 10px); display:inline-block;"></div>').appendTo(row);
$('<label for="projects-dialog-screen-create-project-repo-user">Username</label>').appendTo(subrow);
projectRepoUserInput = $('<input id="projects-dialog-screen-create-project-repo-user" type="text"></input>').appendTo(subrow);
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="projects-dialog-screen-create-project-repo-pass">Password</label>').appendTo(subrow);
projectRepoPasswordInput = $('<input id="projects-dialog-screen-create-project-repo-pass" type="password"></input>').appendTo(subrow);
// -----------------------------------------------------
// Repo credentials - key/passphrase -------------------
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-sshkey"></div>').hide().appendTo(cloneAuthRows);
subrow = $('<div style="width: calc(50% - 10px); display:inline-block;"></div>').appendTo(row);
$('<label for="projects-dialog-screen-create-project-repo-passphrase">SSH Key</label>').appendTo(subrow);
projectRepoSSHKeySelect = $("<select>",{style:"width: 100%"}).appendTo(subrow);
$.getJSON("settings/user/keys", function(data) {
var count = 0;
data.keys.forEach(function(key) {
projectRepoSSHKeySelect.append($("<option></option>").val(key.name).text(key.name));
count++;
});
if (count === 0) {
projectRepoSSHKeySelect.addClass("input-error");
projectRepoSSHKeySelect.attr("disabled",true);
sshwarningRow.show();
} else {
projectRepoSSHKeySelect.removeClass("input-error");
projectRepoSSHKeySelect.attr("disabled",false);
sshwarningRow.hide();
}
});
subrow = $('<div style="width: calc(50% - 10px); margin-left: 20px; display:inline-block;"></div>').appendTo(row);
$('<label for="projects-dialog-screen-create-project-repo-passphrase">Passphrase</label>').appendTo(subrow);
projectRepoPassphrase = $('<input id="projects-dialog-screen-create-project-repo-passphrase" type="password"></input>').appendTo(subrow);
subrow = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-sshkey"></div>').appendTo(cloneAuthRows);
var sshwarningRow = $('<div class="projects-dialog-screen-create-row-auth-error-no-keys"></div>').hide().appendTo(subrow);
$('<div class="form-row"><i class="fa fa-warning"></i> Before you can clone a repository over ssh you must add an SSH key to access it.</div>').appendTo(sshwarningRow);
subrow = $('<div style="text-align: center">').appendTo(sshwarningRow);
$('<button class="editor-button">Add an ssh key</button>').appendTo(subrow).click(function(e) {
e.preventDefault();
$('#projects-dialog-cancel').click();
RED.userSettings.show('gitconfig');
setTimeout(function() {
$("#user-settings-gitconfig-add-key").click();
},500);
});
// -----------------------------------------------------
// Secret - clone
row = $('<div class="form-row projects-dialog-screen-create-row projects-dialog-screen-create-row-clone"></div>').appendTo(body);
$('<label>Credentials encryption key</label>').appendTo(row);
projectSecretInput = $('<input type="password"></input>').appendTo(row);
return container;
},
buttons: function(options) {
return [
{
text: "Back",
click: function() {
show('git-config');
}
},
{
id: "projects-dialog-clone-project",
disabled: true,
text: "Clone project", // TODO: nls
class: "primary disabled",
click: function() {
var projectType = $(".projects-dialog-screen-create-type.selected").data('type');
var projectData = {
name: projectNameInput.val(),
}
projectData.credentialSecret = projectSecretInput.val();
var repoUrl = projectRepoInput.val();
var metaData = {};
if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(repoUrl)) {
var selected = projectRepoSSHKeySelect.val();//false;//getSelectedSSHKey(projectRepoSSHKeySelect);
if ( selected ) {
projectData.git = {
remotes: {
'origin': {
url: repoUrl,
keyFile: selected,
passphrase: projectRepoPassphrase.val()
}
}
};
}
else {
console.log("Error! Can't get selected SSH key path.");
return;
}
}
else {
projectData.git = {
remotes: {
'origin': {
url: repoUrl,
username: projectRepoUserInput.val(),
password: projectRepoPasswordInput.val()
}
}
};
}
$(".projects-dialog-screen-create-row-auth-error").hide();
projectRepoUserInput.removeClass("input-error");
projectRepoPasswordInput.removeClass("input-error");
projectRepoSSHKeySelect.removeClass("input-error");
projectRepoPassphrase.removeClass("input-error");
RED.deploy.setDeployInflight(true);
RED.projects.settings.switchProject(projectData.name);
sendRequest({
url: "projects",
type: "POST",
handleAuthFail: false,
responses: {
200: function(data) {
dialog.dialog( "close" );
},
400: {
'project_exists': function(error) {
console.log("already exists");
},
'git_error': function(error) {
console.log("git error",error);
},
'git_connection_failed': function(error) {
projectRepoInput.addClass("input-error");
$("#projects-dialog-screen-create-project-repo-label small").text("Connection failed");
},
'git_not_a_repository': function(error) {
projectRepoInput.addClass("input-error");
$("#projects-dialog-screen-create-project-repo-label small").text("Not a git repository");
},
'git_repository_not_found': function(error) {
projectRepoInput.addClass("input-error");
$("#projects-dialog-screen-create-project-repo-label small").text("Repository not found");
},
'git_auth_failed': function(error) {
$(".projects-dialog-screen-create-row-auth-error").show();
projectRepoUserInput.addClass("input-error");
projectRepoPasswordInput.addClass("input-error");
// getRepoAuthDetails(req);
projectRepoSSHKeySelect.addClass("input-error");
projectRepoPassphrase.addClass("input-error");
},
'missing_flow_file': function(error) {
// This is handled via a runtime notification.
dialog.dialog("close");
},
'project_empty': function(error) {
// This is handled via a runtime notification.
dialog.dialog("close");
},
'credentials_load_failed': function(error) {
// This is handled via a runtime notification.
dialog.dialog("close");
},
'*': function(error) {
reportUnexpectedError(error);
$( dialog ).dialog( "close" );
}
}
}
},projectData).then(function() {
RED.events.emit("project:change", {name:name});
}).always(function() {
setTimeout(function() {
RED.deploy.setDeployInflight(false);
},500);
})
}
}
]
}
}
})(),
'default-files': (function() { 'default-files': (function() {
var projectFlowFileInput; var projectFlowFileInput;
var projectCredentialFileInput; var projectCredentialFileInput;

View File

@ -139,16 +139,17 @@
} }
} }
button.editor-button { button.editor-button {
width: calc(50% - 40px); width: calc(50% - 80px);
margin: 20px; margin: 20px;
height: 175px; height: auto;
line-height: 2em; line-height: 2em;
font-size: 1.5em !important; padding: 10px;
border-color: #aaa;
i { i {
color: #ccc; color: #aaa;
} }
&:hover i { &:hover i {
color: #aaa; color: #999;
} }
} }
.button-group { .button-group {
@ -160,7 +161,6 @@
button.projects-dialog-screen-create-type { button.projects-dialog-screen-create-type {
height: auto; height: auto;
padding: 10px; padding: 10px;
} }
.button-group { .button-group {
text-align: center; text-align: center;

View File

@ -48,7 +48,9 @@ function runGitCommand(args,cwd,env) {
var err = new Error(stderr); var err = new Error(stderr);
err.stdout = stdout; err.stdout = stdout;
err.stderr = stderr; err.stderr = stderr;
if (/fatal: could not read/i.test(stderr)) { if(/Connection refused/i.test(stderr)) {
err.code = "git_connection_failed";
} else if (/fatal: could not read/i.test(stderr)) {
// Username/Password // Username/Password
err.code = "git_auth_failed"; err.code = "git_auth_failed";
} else if(/HTTP Basic: Access denied/i.test(stderr)) { } else if(/HTTP Basic: Access denied/i.test(stderr)) {
@ -58,8 +60,6 @@ function runGitCommand(args,cwd,env) {
} else if(/Host key verification failed/i.test(stderr)) { } else if(/Host key verification failed/i.test(stderr)) {
// TODO: handle host key verification errors separately // TODO: handle host key verification errors separately
err.code = "git_auth_failed"; err.code = "git_auth_failed";
} else if(/Connection refused/i.test(stderr)) {
err.code = "git_connection_failed";
} else if (/commit your changes or stash/i.test(stderr)) { } else if (/commit your changes or stash/i.test(stderr)) {
err.code = "git_local_overwrite"; err.code = "git_local_overwrite";
} else if (/CONFLICT/.test(err.stdout)) { } else if (/CONFLICT/.test(err.stdout)) {