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

Add Project object in runtime

This commit is contained in:
Nick O'Leary 2017-10-16 23:23:50 +01:00
parent 19c84eb694
commit 4569cb432d
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
9 changed files with 457 additions and 354 deletions

View File

@ -477,7 +477,7 @@ RED.projects.settings = (function() {
}); });
} }
if (activeProject.settings.credentialsInvalid) { if (activeProject.settings.credentialSecretInvalid) {
row = $('<div class="user-settings-row"></div>').appendTo(pane); 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); $('<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);
} }
@ -498,7 +498,7 @@ RED.projects.settings = (function() {
} }
if (activeProject.settings.credentialsEncrypted) { if (activeProject.settings.credentialsEncrypted) {
if (!activeProject.settings.credentialsInvalid) { if (!activeProject.settings.credentialSecretInvalid) {
row = $('<div class="user-settings-row project-settings-credentials-current-row hide"></div>').appendTo(credentialsContainer); row = $('<div class="user-settings-row project-settings-credentials-current-row hide"></div>').appendTo(credentialsContainer);
$('<label for="">Current key</label>').appendTo(row); $('<label for="">Current key</label>').appendTo(row);
currentKey = $('<input type="password">').appendTo(row); currentKey = $('<input type="password">').appendTo(row);
@ -518,7 +518,7 @@ RED.projects.settings = (function() {
// $('<label for="" style="margin-left:20px; width: auto;"><input type="radio" name="project-settings-credentials-current" value="lost"> Forgotten key?</label>').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); row = $('<div class="user-settings-row project-settings-credentials-row hide"></div>').appendTo(credentialsContainer);
$('<label for=""></label>').text((activeProject.settings.credentialsEncrypted&& !activeProject.settings.credentialsInvalid)?"New key":"Encryption key").appendTo(row); $('<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); 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); row = $('<div class="user-settings-row project-settings-credentials-row hide"></div>').appendTo(credentialsContainer);
@ -551,14 +551,14 @@ RED.projects.settings = (function() {
var payload = { var payload = {
credentialSecret: newKey.val() credentialSecret: newKey.val()
}; };
if (activeProject.settings.credentialsInvalid) { if (activeProject.settings.credentialSecretInvalid) {
RED.deploy.setDeployInflight(true); RED.deploy.setDeployInflight(true);
} }
if (activeProject.settings.credentialsEncrypted) { if (activeProject.settings.credentialsEncrypted) {
if (action === 'reset') { if (action === 'reset') {
payload.resetCredentialSecret = true; payload.resetCredentialSecret = true;
} else if (!activeProject.settings.credentialsInvalid) { } else if (!activeProject.settings.credentialSecretInvalid) {
payload.currentCredentialSecret = currentKey.val(); payload.currentCredentialSecret = currentKey.val();
} }
} }
@ -606,7 +606,7 @@ RED.projects.settings = (function() {
}, },
} }
},payload).always(function() { },payload).always(function() {
if (activeProject.settings.credentialsInvalid) { if (activeProject.settings.credentialSecretInvalid) {
RED.deploy.setDeployInflight(false); RED.deploy.setDeployInflight(false);
} }
}); });
@ -620,11 +620,11 @@ RED.projects.settings = (function() {
// $('<button id="" class="editor-button">Set key</button>').appendTo(row); // $('<button id="" class="editor-button">Set key</button>').appendTo(row);
// $('<h3></h3>').text("Repository").appendTo(pane); $('<h3></h3>').text("Repository").appendTo(pane);
// row = $('<div class="user-settings-row"></div>').appendTo(pane); row = $('<div class="user-settings-row"></div>').appendTo(pane);
// var input; var input;
// $('<label for="">'+'Remote'+'</label>').appendTo(row); $('<label for="">'+'Remote'+'</label>').appendTo(row);
// $('<input id="" type="text">').appendTo(row); $('<input id="" type="text">').appendTo(row);
return pane; return pane;

View File

@ -178,7 +178,10 @@
padding-top: 4px; padding-top: 4px;
} }
} }
.projects-dialog-screen-create-type.editor-button.toggle.selected:not(.disabled):not(:disabled) {
background: #fff !important;
color: #666 !important;
}
.sidebar-version-control { .sidebar-version-control {
height: 100%; height: 100%;

View File

@ -33,7 +33,7 @@ module.exports = {
runtime.storage.projects.listProjects().then(function(list) { runtime.storage.projects.listProjects().then(function(list) {
var active = runtime.storage.projects.getActiveProject(); var active = runtime.storage.projects.getActiveProject();
var response = { var response = {
active: active, active: active.name,
projects: list projects: list
}; };
res.json(response); res.json(response);
@ -68,7 +68,7 @@ module.exports = {
//TODO: validate the payload properly //TODO: validate the payload properly
if (req.body.active) { if (req.body.active) {
var currentProject = runtime.storage.projects.getActiveProject(); var currentProject = runtime.storage.projects.getActiveProject();
if (req.params.id !== currentProject) { if (req.params.id !== currentProject.name) {
runtime.storage.projects.setActiveProject(req.params.id).then(function() { runtime.storage.projects.setActiveProject(req.params.id).then(function() {
res.redirect(303,req.baseUrl + '/'); res.redirect(303,req.baseUrl + '/');
}).catch(function(err) { }).catch(function(err) {

View File

@ -158,9 +158,9 @@ function start() {
if (settings.httpStatic) { if (settings.httpStatic) {
log.info(log._("runtime.paths.httpStatic",{path:path.resolve(settings.httpStatic)})); log.info(log._("runtime.paths.httpStatic",{path:path.resolve(settings.httpStatic)}));
} }
redNodes.loadFlows().then(redNodes.startFlows).otherwise(function(err) {}); redNodes.loadFlows().then(redNodes.startFlows).catch(function(err) {});
started = true; started = true;
}).otherwise(function(err) { }).catch(function(err) {
console.log(err); console.log(err);
}); });
}); });

View File

@ -16,6 +16,7 @@
var when = require("when"); var when = require("when");
var crypto = require('crypto'); var crypto = require('crypto');
var runtime;
var settings; var settings;
var log; var log;
@ -38,30 +39,9 @@ function decryptCredentials(key,credentials) {
return JSON.parse(decrypted); return JSON.parse(decrypted);
} }
function markProjectSecretValid(project,valid) {
try {
var projects = settings.get('projects');
if (projects) {
if (valid) {
if (!projects.projects[project].credentialSecretInvalid) {
return when.resolve();
} else {
delete projects.projects[project].credentialSecretInvalid;
}
} else {
projects.projects[project].credentialSecretInvalid = true;
}
return settings.set('projects',projects);
} else {
return when.resolve();
}
} catch(err) {
return when.resolve();
}
}
var api = module.exports = { var api = module.exports = {
init: function(runtime) { init: function(_runtime) {
runtime = _runtime;
log = runtime.log; log = runtime.log;
settings = runtime.settings; settings = runtime.settings;
dirty = false; dirty = false;
@ -98,14 +78,14 @@ var api = module.exports = {
var projectKey = false; var projectKey = false;
var activeProject; var activeProject;
try { if (runtime.storage.projects) {
var projects = settings.get('projects'); // projects enabled
if (projects && projects.activeProject) { activeProject = runtime.storage.projects.getActiveProject();
activeProject = projects.activeProject; if (activeProject) {
projectKey = projects.projects[projects.activeProject].credentialSecret; projectKey = activeProject.credentialSecret;
} }
} catch(err) {
} }
if (projectKey) { if (projectKey) {
log.debug("red/runtime/nodes/credentials.load : using active project key - ignoring user provided key"); log.debug("red/runtime/nodes/credentials.load : using active project key - ignoring user provided key");
userKey = projectKey; userKey = projectKey;
@ -208,9 +188,9 @@ var api = module.exports = {
error.code = "credentials_load_failed"; error.code = "credentials_load_failed";
if (projectKey) { if (projectKey) {
// This is a project with a bad key. Mark it as invalid // This is a project with a bad key. Mark it as invalid
return markProjectSecretValid(activeProject,false).then(function() { // TODO: this delves too deep into Project structure
return when.reject(error); activeProject.info.settings.credentialSecretInvalid = true;
}) return when.reject(error);
} }
return when.reject(error); return when.reject(error);
} }
@ -218,7 +198,7 @@ var api = module.exports = {
credentialCache = credentials; credentialCache = credentials;
} }
if (clearInvalidFlag) { if (clearInvalidFlag) {
return markProjectSecretValid(activeProject,true) delete activeProject.info.settings.credentialSecretInvalid;
} }
}); });
}, },

View File

@ -76,7 +76,7 @@ function loadFlows() {
events.emit("runtime-event",{id:"runtime-state",retain:true}); events.emit("runtime-event",{id:"runtime-state",retain:true});
return config; return config;
}); });
}).otherwise(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:"credentials_load_failed",text:"notification.warnings.invalid-credentials-secret"},retain:true});
log.warn(log._("nodes.flows.error",{message:err.toString()})); log.warn(log._("nodes.flows.error",{message:err.toString()}));
@ -141,7 +141,7 @@ function setFlows(_config,type,muteLog,forceStart) {
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true}); events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});
}); });
return flowRevision; return flowRevision;
}).otherwise(function(err) { }).catch(function(err) {
}) })
} else { } else {
events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true}); events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true});

View File

@ -0,0 +1,338 @@
/**
* Copyright JS Foundation and other contributors, http://js.foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
var fs = require('fs-extra');
var when = require('when');
var fspath = require("path");
var gitTools = require("./git");
var util = require("../util");
var defaultFileSet = require("./defaultFileSet");
var settings;
var runtime;
var log;
var projectsDir;
function Project(name) {
this.name = name;
this.path = fspath.join(projectsDir,name);
this.paths = {};
this.info = {
name: name,
settings: {}
};
this.credentialSecret = null;
}
Project.prototype.load = function () {
var project = this;
var globalProjectSettings = settings.get("projects");
// console.log(globalProjectSettings)
var projectSettings = {};
if (globalProjectSettings) {
projectSettings = globalProjectSettings.projects[this.name]||{};
}
this.credentialSecret = projectSettings.credentialSecret;
// console.log(this.name,"credSec",this.credentialSecret )
if (typeof projectSettings.credentialSecret === "string") {
this.info.settings.credentialsEncrypted = true;
} else {
this.info.settings.credentialsEncrypted = false;
}
var promises = [];
return checkProjectFiles(project.name).then(function(missingFiles) {
if (missingFiles.length > 0) {
project.info.missingFiles = missingFiles;
}
if (missingFiles.indexOf('package.json') === -1) {
project.paths['package.json'] = fspath.join(project.path,"package.json");
promises.push(fs.readFile(project.paths['package.json'],"utf8").then(function(content) {
project.package = util.parseJSON(content);
project.info.summary = project.package.description||"";
project.info.dependencies = project.package.dependencies||{};
}));
}
if (missingFiles.indexOf('README.md') === -1) {
project.paths['README.md'] = fspath.join(project.path,"README.md");
promises.push(fs.readFile(project.paths['README.md'],"utf8").then(function(content) {
project.info.description = content;
}));
} else {
project.info.description = "";
}
return when.settle(promises).then(function() {
return project;
})
});
};
Project.prototype.update = function (data) {
var filesToUpdate = {};
var promises = [];
var project = this;
if (data.credentialSecret) {
var existingSecret = data.currentCredentialSecret;
var isReset = data.resetCredentialSecret;
var secret = data.credentialSecret;
// console.log("updating credentialSecret");
// console.log("request:");
// console.log(JSON.stringify(data,"",4));
// console.log(" this.credentialSecret",this.credentialSecret);
// console.log(" this.info", this.info);
if (!isReset && // not a reset
this.credentialSecret && // key already set
!this.info.settings.credentialSecretInvalid && // key not invalid
this.credentialSecret !== existingSecret) { // key doesn't match provided existing key
var e = new Error("Cannot change credentialSecret without current key");
e.code = "missing_current_credential_key";
throw e;
}
this.credentialSecret = secret;
var globalProjectSettings = settings.get("projects");
globalProjectSettings.projects[this.name] = globalProjectSettings.projects[this.name]||{}
globalProjectSettings.projects[this.name].credentialSecret = project.credentialSecret;
delete this.info.settings.credentialSecretInvalid;
return settings.set("projects",globalProjectSettings);
}
if (data.hasOwnProperty('description')) {
filesToUpdate[this.paths['README.md']] = function() {
return data.description;
};
this.info.description = data.description;
}
if (data.hasOwnProperty('dependencies')) {
filesToUpdate[this.paths['package.json']] = function() {
return JSON.stringify(project.package,"",4)
};
this.package.dependencies = data.dependencies;
this.info.dependencies = data.dependencies;
}
if (data.hasOwnProperty('summary')) {
filesToUpdate[this.paths['package.json']] = function() {
return JSON.stringify(project.package,"",4)
};
this.package.description = data.summary;
this.info.summary = data.summary;
}
var files = Object.keys(filesToUpdate);
files.forEach(function(f) {
promises.push(util.writeFile(f,filesToUpdate[f]()));
});
return when.settle(promises);
};
Project.prototype.getFiles = function () {
return gitTools.getFiles(this.path);
};
Project.prototype.stageFile = function(file) {
return gitTools.stageFile(this.path,file);
};
Project.prototype.unstageFile = function(file) {
return gitTools.unstageFile(this.path,file);
}
Project.prototype.commit = function(options) {
return gitTools.commit(this.path,options.message);
}
Project.prototype.getFileDiff = function(file,type) {
return gitTools.getFileDiff(this.path,file,type);
}
Project.prototype.getCommits = function(options) {
return gitTools.getCommits(this.path,options);
}
Project.prototype.getCommit = function(sha) {
return gitTools.getCommit(this.path,sha);
}
Project.prototype.getFlowFile = function() {
return fspath.join(this.path,"flow.json");
}
Project.prototype.getFlowFileBackup = function() {
return getBackupFilename(this.getFlowFile());
}
Project.prototype.getCredentialsFile = function() {
return fspath.join(this.path,"flow_cred.json");
}
Project.prototype.getCredentialsFileBackup = function() {
return getBackupFilename(this.getCredentialsFile());
}
function getBackupFilename(filename) {
var ffName = fspath.basename(filename);
var ffDir = fspath.dirname(filename);
return fspath.join(ffDir,"."+ffName+".backup");
}
function checkProjectExists(project) {
var projectPath = fspath.join(projectsDir,project);
return fs.pathExists(projectPath).then(function(exists) {
if (!exists) {
var e = new Error("NLD: project not found");
e.code = "project_not_found";
throw e;
}
});
}
function createProjectDirectory(project) {
var projectPath = fspath.join(projectsDir,project);
return fs.ensureDir(projectPath);
}
function createDefaultProject(project) {
var projectPath = fspath.join(projectsDir,project.name);
// Create a basic skeleton of a project
return gitTools.initRepo(projectPath).then(function() {
var promises = [];
for (var file in defaultFileSet) {
if (defaultFileSet.hasOwnProperty(file)) {
promises.push(util.writeFile(fspath.join(projectPath,file),defaultFileSet[file](project)));
}
}
return when.all(promises);
});
}
function checkProjectFiles(project) {
var projectPath = fspath.join(projectsDir,project);
var promises = [];
var paths = [];
for (var file in defaultFileSet) {
if (defaultFileSet.hasOwnProperty(file)) {
paths.push(file);
promises.push(fs.stat(fspath.join(projectPath,file)));
}
}
return when.settle(promises).then(function(results) {
var missing = [];
results.forEach(function(result,i) {
if (result.state === 'rejected') {
missing.push(paths[i]);
}
});
return missing;
}).then(function(missing) {
// if (createMissing) {
// var promises = [];
// missing.forEach(function(file) {
// promises.push(util.writeFile(fspath.join(projectPath,file),defaultFileSet[file](project)));
// });
// return promises;
// } else {
return missing;
// }
});
}
function createProject(metadata) {
var project = metadata.name;
return when.promise(function(resolve,reject) {
var projectPath = fspath.join(projectsDir,project);
fs.stat(projectPath, function(err,stat) {
if (!err) {
var e = new Error("NLS: Project already exists");
e.code = "project_exists";
return reject(e);
}
createProjectDirectory(project).then(function() {
var projects = settings.get('projects');
projects.projects[project] = {};
if (metadata.credentialSecret) {
projects.projects[project].credentialSecret = metadata.credentialSecret;
}
return settings.set('projects',projects);
}).then(function() {
if (metadata.remote) {
return gitTools.clone(metadata.remote,projectPath).then(function(result) {
// Check this is a valid project
// If it is empty
// - if 'populate' flag is set, call populateProject
// - otherwise reject with suitable error to allow UI to confirm population
// If it is missing package.json/flow.json/flow_cred.json
// - reject as invalid project
// checkProjectFiles(project).then(function(results) {
// console.log("checkProjectFiles");
// console.log(results);
// });
resolve(getProject(project));
}).catch(function(error) {
fs.remove(projectPath,function() {
reject(error);
});
})
} else {
createDefaultProject(metadata).then(function() { resolve(getProject(project))}).catch(reject);
}
}).catch(reject);
})
})
}
var currentProject;
function getProject(name) {
return checkProjectExists(name).then(function() {
if (currentProject && currentProject.name === name) {
return currentProject;
}
currentProject = new Project(name);
return currentProject.load();
})
}
function listProjects() {
return fs.readdir(projectsDir).then(function(fns) {
var dirs = [];
fns.sort().filter(function(fn) {
var fullPath = fspath.join(projectsDir,fn);
if (fn[0] != ".") {
var stats = fs.lstatSync(fullPath);
if (stats.isDirectory()) {
dirs.push(fn);
}
}
});
return dirs;
});
}
function init(_settings, _runtime) {
settings = _settings;
runtime = _runtime;
log = runtime.log;
projectsDir = fspath.join(settings.userDir,"projects");
}
module.exports = {
init: init,
get: getProject,
create: createProject,
list: listProjects
}

View File

@ -208,6 +208,9 @@ module.exports = {
return runCommand(gitCommand,args,cwd); return runCommand(gitCommand,args,cwd);
}, },
clone: function(repo, cwd) { clone: function(repo, cwd) {
if (repo.url) {
repo = repo.url;
}
var args = ["clone",repo,"."]; var args = ["clone",repo,"."];
return runCommand(gitCommand,args,cwd); return runCommand(gitCommand,args,cwd);
}, },

View File

@ -24,18 +24,21 @@ var storageSettings = require("../settings");
var util = require("../util"); var util = require("../util");
var gitTools = require("./git"); var gitTools = require("./git");
var Projects = require("./Project");
var settings; var settings;
var runtime; var runtime;
var projectsDir; var projectsDir;
var activeProject
var defaultFileSet = require("./defaultFileSet");
function init(_settings, _runtime) { function init(_settings, _runtime) {
settings = _settings; settings = _settings;
runtime = _runtime; runtime = _runtime;
log = runtime.log; log = runtime.log;
Projects.init(settings,runtime);
projectsDir = fspath.join(settings.userDir,"projects"); projectsDir = fspath.join(settings.userDir,"projects");
if (settings.flowFile) { if (settings.flowFile) {
@ -85,11 +88,6 @@ function init(_settings, _runtime) {
return storageSettings.saveSettings(globalSettings); return storageSettings.saveSettings(globalSettings);
} else { } else {
activeProject = globalSettings.projects.activeProject; activeProject = globalSettings.projects.activeProject;
var projectPath = fspath.join(projectsDir,activeProject);
flowsFullPath = fspath.join(projectPath,"flow.json");
flowsFileBackup = getBackupFilename(flowsFullPath);
credentialsFile = fspath.join(projectPath,"flow_cred.json");
credentialsFileBackup = getBackupFilename(credentialsFile);
} }
}); });
} else { } else {
@ -103,347 +101,123 @@ function getBackupFilename(filename) {
return fspath.join(ffDir,"."+ffName+".backup"); return fspath.join(ffDir,"."+ffName+".backup");
} }
function listProjects() { function loadProject(name) {
return fs.readdir(projectsDir).then(function(fns) { return Projects.get(name).then(function(project) {
var dirs = []; activeProject = project;
fns.sort().filter(function(fn) { flowsFullPath = project.getFlowFile();
var fullPath = fspath.join(projectsDir,fn); flowsFileBackup = project.getFlowFileBackup();
if (fn[0] != ".") { credentialsFile = project.getCredentialsFile();
var stats = fs.lstatSync(fullPath); credentialsFileBackup = project.getCredentialsFileBackup();
if (stats.isDirectory()) { return project;
dirs.push(fn);
}
}
});
return dirs;
});
}
function getProject(project) {
return when.promise(function(resolve,reject) {
if (project === "") {
return reject(new Error("NLS: No active project set"));
}
var projectPath = fspath.join(projectsDir,project);
var globalProjectSettings = settings.get("projects");
var projectSettings = {};
if (globalProjectSettings.projects) {
projectSettings = globalProjectSettings.projects[project]||{};
}
var projectData = {
name: project,
settings: {}
};
if (typeof projectSettings.credentialSecret === "string") {
projectData.settings.credentialsEncrypted = true;
} else {
projectData.settings.credentialsEncrypted = false;
}
if (projectSettings.credentialSecretInvalid) {
projectData.settings.credentialsInvalid = true;
}
var promises = [];
checkProjectFiles(project).then(function(missingFiles) {
if (missingFiles.length > 0) {
projectData.missingFiles = missingFiles;
}
if (missingFiles.indexOf('package.json') === -1) {
promises.push(fs.readFile(fspath.join(projectPath,"package.json"),"utf8").then(function(content) {
var package = util.parseJSON(content);
projectData.summary = package.description||"";
projectData.dependencies = package.dependencies||{};
}));
}
if (missingFiles.indexOf('README.md') === -1) {
promises.push(fs.readFile(fspath.join(projectPath,"README.md"),"utf8").then(function(content) {
projectData.description = content;
}));
} else {
projectData.description = "";
}
when.settle(promises).then(function() {
resolve(projectData);
})
});
// fs.stat(projectPath,function(err,stat) {
// if (err) {
// return resolve(null);
// }
// resolve(fs.readFile(projectPackage,'utf8').then(util.parseJSON));
// })
}).catch(function(err) {
console.log(err);
var e = new Error("NLD: project not found");
e.code = "project_not_found";
throw e;
});
}
function setCredentialSecret(project,data) { //existingSecret,secret) {
var existingSecret = data.currentCredentialSecret;
var isReset = data.resetCredentialSecret;
var secret = data.credentialSecret;
var wasInvalid = false;
var globalProjectSettings = settings.get("projects");
if (globalProjectSettings.projects.hasOwnProperty(project)) {
if (!isReset &&
globalProjectSettings.projects[project].credentialSecret &&
!globalProjectSettings.projects[project].credentialSecretInvalid &&
globalProjectSettings.projects[project].credentialSecret !== existingSecret) {
var e = new Error("Cannot change credentialSecret without current key");
e.code = "missing_current_credential_key";
throw e;
}
} else {
globalProjectSettings.projects[project] = {};
}
globalProjectSettings.projects[project].credentialSecret = secret;
wasInvalid = globalProjectSettings.projects[project].credentialSecretInvalid;
delete globalProjectSettings.projects[project].credentialSecretInvalid;
return settings.set("projects",globalProjectSettings).then(function() {
if (isReset || !wasInvalid) {
if (isReset) {
runtime.nodes.clearCredentials();
}
runtime.nodes.setCredentialSecret(secret);
return runtime.nodes.exportCredentials()
.then(runtime.storage.saveCredentials)
.then(function() {
return wasInvalid;
});
}
return wasInvalid;
});
}
function createProject(metadata) {
var project = metadata.name;
return when.promise(function(resolve,reject) {
if (project === "") {
return reject(new Error("NLS: No project set"));
}
var projectPath = fspath.join(projectsDir,project);
fs.stat(projectPath, function(err,stat) {
if (!err) {
var e = new Error("NLS: Project already exists");
e.code = "project_exists";
return reject(e);
}
createProjectDirectory(project).then(function() {
var projects = settings.get('projects');
projects[project] = {};
if (metadata.credentialSecret) {
projects[project].credentialSecret = metadata.credentialSecret;
}
return settings.set('projects',projects);
}).then(function() {
if (metadata.remote) {
return gitTools.pull(metadata.remote,projectPath).then(function(result) {
// Check this is a valid project
// If it is empty
// - if 'populate' flag is set, call populateProject
// - otherwise reject with suitable error to allow UI to confirm population
// If it is missing package.json/flow.json/flow_cred.json
// - reject as invalid project
checkProjectFiles(project).then(function(results) {
console.log("checkProjectFiles");
console.log(results);
});
resolve(project);
}).catch(function(error) {
fs.remove(projectPath,function() {
reject(error);
});
})
} else {
createDefaultProject(metadata).then(function() { resolve(project)}).catch(reject);
}
}).catch(reject);
})
}) })
} }
function createProjectDirectory(project) { function getProject(name) {
var projectPath = fspath.join(projectsDir,project); if (!activeProject || activeProject.name !== name) {
return fs.ensureDir(projectPath).then(function() { //TODO: throw better err
return gitTools.initRepo(projectPath) throw new Error("Cannot get inactive project");
});
}
function createDefaultProject(project) {
var projectPath = fspath.join(projectsDir,project.name);
// Create a basic skeleton of a project
var promises = [];
for (var file in defaultFileSet) {
if (defaultFileSet.hasOwnProperty(file)) {
promises.push(util.writeFile(fspath.join(projectPath,file),defaultFileSet[file](project)));
}
} }
return when.all(promises); //return when.resolve(activeProject.info);
return Projects.get(name).then(function(p) {
return p.info
})
} }
function checkProjectExists(project) {
var projectPath = fspath.join(projectsDir,project);
return fs.pathExists(projectPath).then(function(exists) {
console.log(projectPath,exists);
if (!exists) {
var e = new Error("NLD: project not found");
e.code = "project_not_found";
throw e;
}
});
}
function checkProjectFiles(project) {
var projectPath = fspath.join(projectsDir,project);
var promises = [];
var paths = [];
for (var file in defaultFileSet) {
if (defaultFileSet.hasOwnProperty(file)) {
paths.push(file);
promises.push(fs.stat(fspath.join(projectPath,file)));
}
}
return when.settle(promises).then(function(results) {
var missing = [];
results.forEach(function(result,i) {
if (result.state === 'rejected') {
missing.push(paths[i]);
}
});
return missing;
}).then(function(missing) {
// if (createMissing) {
// var promises = [];
// missing.forEach(function(file) {
// promises.push(util.writeFile(fspath.join(projectPath,file),defaultFileSet[file](project)));
// });
// return promises;
// } else {
return missing;
// }
});
}
function getFiles(project) { function getFiles(project) {
var projectPath = fspath.join(projectsDir,project); return activeProject.getFiles();
return gitTools.getFiles(projectPath);
} }
function stageFile(project,file) { function stageFile(project,file) {
var projectPath = fspath.join(projectsDir,project); return activeProject.stageFile(file);
return gitTools.stageFile(projectPath,file);
} }
function unstageFile(project,file) { function unstageFile(project,file) {
var projectPath = fspath.join(projectsDir,project); return activeProject.unstageFile(file);
return gitTools.unstageFile(projectPath,file);
} }
function commit(project,options) { function commit(project,options) {
var projectPath = fspath.join(projectsDir,project); return activeProject.commit(options);
return gitTools.commit(projectPath,options.message);
} }
function getFileDiff(project,file,type) { function getFileDiff(project,file,type) {
var projectPath = fspath.join(projectsDir,project); return activeProject.getFileDiff(file,type);
return gitTools.getFileDiff(projectPath,file,type);
} }
function getCommits(project,options) { function getCommits(project,options) {
var projectPath = fspath.join(projectsDir,project); return activeProject.getCommits(options);
return gitTools.getCommits(projectPath,options);
} }
function getCommit(project,sha) { function getCommit(project,sha) {
var projectPath = fspath.join(projectsDir,project); return activeProject.getCommit(sha);
return gitTools.getCommit(projectPath,sha);
} }
function getFile(project,path) { function getFile(project,path) {
} }
var activeProject
function getActiveProject() { function getActiveProject() {
return activeProject; return activeProject;
} }
function reloadActiveProject(project) { function reloadActiveProject() {
return runtime.nodes.stopFlows().then(function() { return runtime.nodes.stopFlows().then(function() {
return runtime.nodes.loadFlows(true).then(function() { return runtime.nodes.loadFlows(true).then(function() {
runtime.events.emit("runtime-event",{id:"project-change",payload:{ project: project}}); runtime.events.emit("runtime-event",{id:"project-change",payload:{ project: activeProject.name}});
}).catch(function(err) { }).catch(function(err) {
// We're committed to the project change now, so notify editors // We're committed to the project change now, so notify editors
// that it has changed. // that it has changed.
runtime.events.emit("runtime-event",{id:"project-change",payload:{ project: project}}); runtime.events.emit("runtime-event",{id:"project-change",payload:{ project: activeProject.name}});
throw err; throw err;
}); });
}); });
} }
function createProject(metadata) {
function setActiveProject(project) { return Projects.create(metadata).then(function(p) {
return checkProjectExists(project).then(function() { return p.name;
activeProject = project; })
}
function setActiveProject(projectName) {
return loadProject(projectName).then(function(project) {
var globalProjectSettings = settings.get("projects"); var globalProjectSettings = settings.get("projects");
globalProjectSettings.activeProject = project; globalProjectSettings.activeProject = project.name;
return settings.set("projects",globalProjectSettings).then(function() { return settings.set("projects",globalProjectSettings).then(function() {
var projectPath = fspath.join(projectsDir,project);
flowsFullPath = fspath.join(projectPath,"flow.json");
flowsFileBackup = getBackupFilename(flowsFullPath);
credentialsFile = fspath.join(projectPath,"flow_cred.json");
credentialsFileBackup = getBackupFilename(credentialsFile);
log.info(log._("storage.localfilesystem.changing-project",{project:activeProject||"none"})); log.info(log._("storage.localfilesystem.changing-project",{project:activeProject||"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)
console.log(credentialsFile) // console.log(credentialsFile)
return reloadActiveProject();
return reloadActiveProject(project);
}) })
// return when.promise(function(resolve,reject) {
// console.log("Activating project");
// resolve();
// });
}); });
} }
function updateProject(project,data) { function updateProject(project,data) {
return checkProjectExists(project).then(function() { if (!activeProject || activeProject.name !== project) {
if (data.credentialSecret) { // TODO standardise
// TODO: this path assumes we aren't trying to migrate the secret throw new Error("Cannot update inactive project");
return setCredentialSecret(project,data).then(function(wasInvalid) { }
if (wasInvalid) { if (data.hasOwnProperty('credentialSecret')) {
return reloadActiveProject(project); return setCredentialSecret(data);
} } else {
}) return activeProject.update(data);
} else if (data.hasOwnProperty('description')) { }
var projectPath = fspath.join(projectsDir,project);
var readmeFile = fspath.join(projectPath,"README.md");
return util.writeFile(readmeFile, data.description);
} else if (data.hasOwnProperty('dependencies') || data.hasOwnProperty('summary')) {
var projectPath = fspath.join(projectsDir,project);
var packageJSON = fspath.join(projectPath,"package.json");
return fs.readFile(packageJSON,"utf8").then(function(content) {
var package = util.parseJSON(content);
if (data.dependencies) {
package.dependencies = data.dependencies;
}
if (data.summary) {
package.description = data.summary;
}
return util.writeFile(packageJSON,JSON.stringify(package,"",4));
});
}
});
} }
function setCredentialSecret(data) { //existingSecret,secret) {
var isReset = data.resetCredentialSecret;
var wasInvalid = activeProject.info.settings.credentialSecretInvalid;
return activeProject.update(data).then(function() {
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();
}
})
}
var initialFlowLoadComplete = false; var initialFlowLoadComplete = false;
@ -458,9 +232,14 @@ function getFlows() {
initialFlowLoadComplete = true; initialFlowLoadComplete = true;
log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir})); log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir}));
if (activeProject) { if (activeProject) {
log.info(log._("storage.localfilesystem.active-project",{project:activeProject||"none"})); return loadProject(activeProject).then(function() {
log.info(log._("storage.localfilesystem.active-project",{project:activeProject.name||"none"}));
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
return getFlows();
});
} else {
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
} }
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
} }
return util.readFile(flowsFullPath,flowsFileBackup,[],'flow'); return util.readFile(flowsFullPath,flowsFileBackup,[],'flow');
} }
@ -510,7 +289,7 @@ function saveCredentials(credentials) {
module.exports = { module.exports = {
init: init, init: init,
listProjects: listProjects, listProjects: Projects.list,
getActiveProject: getActiveProject, getActiveProject: getActiveProject,
setActiveProject: setActiveProject, setActiveProject: setActiveProject,
getProject: getProject, getProject: getProject,