diff --git a/editor/js/ui/projectSettings.js b/editor/js/ui/projectSettings.js index 82470306f..8dd0f770f 100644 --- a/editor/js/ui/projectSettings.js +++ b/editor/js/ui/projectSettings.js @@ -477,7 +477,7 @@ RED.projects.settings = (function() { }); } - if (activeProject.settings.credentialsInvalid) { + if (activeProject.settings.credentialSecretInvalid) { row = $('
').appendTo(pane); $('
The current key is not valid. Set the correct key or reset credentials.
').appendTo(row); } @@ -498,7 +498,7 @@ RED.projects.settings = (function() { } if (activeProject.settings.credentialsEncrypted) { - if (!activeProject.settings.credentialsInvalid) { + if (!activeProject.settings.credentialSecretInvalid) { row = $('
').appendTo(credentialsContainer); $('').appendTo(row); currentKey = $('').appendTo(row); @@ -518,7 +518,7 @@ RED.projects.settings = (function() { // $('').appendTo(row); row = $('
').appendTo(credentialsContainer); - $('').text((activeProject.settings.credentialsEncrypted&& !activeProject.settings.credentialsInvalid)?"New key":"Encryption key").appendTo(row); + $('').text((activeProject.settings.credentialsEncrypted&& !activeProject.settings.credentialSecretInvalid)?"New key":"Encryption key").appendTo(row); newKey = $('').appendTo(row).on("change keyup paste",checkInputs); row = $('
').appendTo(credentialsContainer); @@ -551,14 +551,14 @@ RED.projects.settings = (function() { var payload = { credentialSecret: newKey.val() }; - if (activeProject.settings.credentialsInvalid) { + if (activeProject.settings.credentialSecretInvalid) { RED.deploy.setDeployInflight(true); } if (activeProject.settings.credentialsEncrypted) { if (action === 'reset') { payload.resetCredentialSecret = true; - } else if (!activeProject.settings.credentialsInvalid) { + } else if (!activeProject.settings.credentialSecretInvalid) { payload.currentCredentialSecret = currentKey.val(); } } @@ -606,7 +606,7 @@ RED.projects.settings = (function() { }, } },payload).always(function() { - if (activeProject.settings.credentialsInvalid) { + if (activeProject.settings.credentialSecretInvalid) { RED.deploy.setDeployInflight(false); } }); @@ -620,11 +620,11 @@ RED.projects.settings = (function() { // $('').appendTo(row); - // $('

').text("Repository").appendTo(pane); - // row = $('
').appendTo(pane); - // var input; - // $('').appendTo(row); - // $('').appendTo(row); + $('

').text("Repository").appendTo(pane); + row = $('
').appendTo(pane); + var input; + $('').appendTo(row); + $('').appendTo(row); return pane; diff --git a/editor/sass/projects.scss b/editor/sass/projects.scss index d53d26d1e..ef76f3333 100644 --- a/editor/sass/projects.scss +++ b/editor/sass/projects.scss @@ -178,7 +178,10 @@ 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 { height: 100%; diff --git a/red/api/editor/projects/index.js b/red/api/editor/projects/index.js index 5a45d46e1..d403c57f9 100644 --- a/red/api/editor/projects/index.js +++ b/red/api/editor/projects/index.js @@ -33,7 +33,7 @@ module.exports = { runtime.storage.projects.listProjects().then(function(list) { var active = runtime.storage.projects.getActiveProject(); var response = { - active: active, + active: active.name, projects: list }; res.json(response); @@ -68,7 +68,7 @@ module.exports = { //TODO: validate the payload properly if (req.body.active) { 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() { res.redirect(303,req.baseUrl + '/'); }).catch(function(err) { diff --git a/red/runtime/index.js b/red/runtime/index.js index c8f269d37..73f09317a 100644 --- a/red/runtime/index.js +++ b/red/runtime/index.js @@ -158,9 +158,9 @@ function start() { if (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; - }).otherwise(function(err) { + }).catch(function(err) { console.log(err); }); }); diff --git a/red/runtime/nodes/credentials.js b/red/runtime/nodes/credentials.js index b4d6c1841..57103ff4c 100644 --- a/red/runtime/nodes/credentials.js +++ b/red/runtime/nodes/credentials.js @@ -16,6 +16,7 @@ var when = require("when"); var crypto = require('crypto'); +var runtime; var settings; var log; @@ -38,30 +39,9 @@ function decryptCredentials(key,credentials) { 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 = { - init: function(runtime) { + init: function(_runtime) { + runtime = _runtime; log = runtime.log; settings = runtime.settings; dirty = false; @@ -98,14 +78,14 @@ var api = module.exports = { var projectKey = false; var activeProject; - try { - var projects = settings.get('projects'); - if (projects && projects.activeProject) { - activeProject = projects.activeProject; - projectKey = projects.projects[projects.activeProject].credentialSecret; + if (runtime.storage.projects) { + // projects enabled + activeProject = runtime.storage.projects.getActiveProject(); + if (activeProject) { + projectKey = activeProject.credentialSecret; } - } catch(err) { } + if (projectKey) { log.debug("red/runtime/nodes/credentials.load : using active project key - ignoring user provided key"); userKey = projectKey; @@ -208,9 +188,9 @@ var api = module.exports = { error.code = "credentials_load_failed"; if (projectKey) { // This is a project with a bad key. Mark it as invalid - return markProjectSecretValid(activeProject,false).then(function() { - return when.reject(error); - }) + // TODO: this delves too deep into Project structure + activeProject.info.settings.credentialSecretInvalid = true; + return when.reject(error); } return when.reject(error); } @@ -218,7 +198,7 @@ var api = module.exports = { credentialCache = credentials; } if (clearInvalidFlag) { - return markProjectSecretValid(activeProject,true) + delete activeProject.info.settings.credentialSecretInvalid; } }); }, diff --git a/red/runtime/nodes/flows/index.js b/red/runtime/nodes/flows/index.js index 6131e18e4..e05ae56f7 100644 --- a/red/runtime/nodes/flows/index.js +++ b/red/runtime/nodes/flows/index.js @@ -76,7 +76,7 @@ function loadFlows() { events.emit("runtime-event",{id:"runtime-state",retain:true}); return config; }); - }).otherwise(function(err) { + }).catch(function(err) { activeConfig = null; 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()})); @@ -141,7 +141,7 @@ function setFlows(_config,type,muteLog,forceStart) { events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true}); }); return flowRevision; - }).otherwise(function(err) { + }).catch(function(err) { }) } else { events.emit("runtime-event",{id:"runtime-deploy",payload:{revision:flowRevision},retain: true}); diff --git a/red/runtime/storage/localfilesystem/projects/Project.js b/red/runtime/storage/localfilesystem/projects/Project.js new file mode 100644 index 000000000..7517607fe --- /dev/null +++ b/red/runtime/storage/localfilesystem/projects/Project.js @@ -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 + +} diff --git a/red/runtime/storage/localfilesystem/projects/git/index.js b/red/runtime/storage/localfilesystem/projects/git/index.js index 4cb3277b1..07d0a6d43 100644 --- a/red/runtime/storage/localfilesystem/projects/git/index.js +++ b/red/runtime/storage/localfilesystem/projects/git/index.js @@ -208,6 +208,9 @@ module.exports = { return runCommand(gitCommand,args,cwd); }, clone: function(repo, cwd) { + if (repo.url) { + repo = repo.url; + } var args = ["clone",repo,"."]; return runCommand(gitCommand,args,cwd); }, diff --git a/red/runtime/storage/localfilesystem/projects/index.js b/red/runtime/storage/localfilesystem/projects/index.js index 5ccc9acbf..a296b9744 100644 --- a/red/runtime/storage/localfilesystem/projects/index.js +++ b/red/runtime/storage/localfilesystem/projects/index.js @@ -24,18 +24,21 @@ var storageSettings = require("../settings"); var util = require("../util"); var gitTools = require("./git"); +var Projects = require("./Project"); + var settings; var runtime; var projectsDir; - -var defaultFileSet = require("./defaultFileSet"); +var activeProject function init(_settings, _runtime) { settings = _settings; runtime = _runtime; log = runtime.log; + Projects.init(settings,runtime); + projectsDir = fspath.join(settings.userDir,"projects"); if (settings.flowFile) { @@ -85,11 +88,6 @@ function init(_settings, _runtime) { return storageSettings.saveSettings(globalSettings); } else { 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 { @@ -103,347 +101,123 @@ function getBackupFilename(filename) { return fspath.join(ffDir,"."+ffName+".backup"); } -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 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 loadProject(name) { + return Projects.get(name).then(function(project) { + activeProject = project; + flowsFullPath = project.getFlowFile(); + flowsFileBackup = project.getFlowFileBackup(); + credentialsFile = project.getCredentialsFile(); + credentialsFileBackup = project.getCredentialsFileBackup(); + return project; }) } -function createProjectDirectory(project) { - var projectPath = fspath.join(projectsDir,project); - return fs.ensureDir(projectPath).then(function() { - return gitTools.initRepo(projectPath) - }); -} - -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))); - } +function getProject(name) { + if (!activeProject || activeProject.name !== name) { + //TODO: throw better err + throw new Error("Cannot get inactive 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) { - var projectPath = fspath.join(projectsDir,project); - return gitTools.getFiles(projectPath); + return activeProject.getFiles(); } function stageFile(project,file) { - var projectPath = fspath.join(projectsDir,project); - return gitTools.stageFile(projectPath,file); + return activeProject.stageFile(file); } function unstageFile(project,file) { - var projectPath = fspath.join(projectsDir,project); - return gitTools.unstageFile(projectPath,file); + return activeProject.unstageFile(file); } function commit(project,options) { - var projectPath = fspath.join(projectsDir,project); - return gitTools.commit(projectPath,options.message); + return activeProject.commit(options); } function getFileDiff(project,file,type) { - var projectPath = fspath.join(projectsDir,project); - return gitTools.getFileDiff(projectPath,file,type); + return activeProject.getFileDiff(file,type); } function getCommits(project,options) { - var projectPath = fspath.join(projectsDir,project); - return gitTools.getCommits(projectPath,options); + return activeProject.getCommits(options); } function getCommit(project,sha) { - var projectPath = fspath.join(projectsDir,project); - return gitTools.getCommit(projectPath,sha); + return activeProject.getCommit(sha); } function getFile(project,path) { } -var activeProject function getActiveProject() { return activeProject; } -function reloadActiveProject(project) { +function reloadActiveProject() { return runtime.nodes.stopFlows().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) { // We're committed to the project change now, so notify editors // 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; }); }); } - -function setActiveProject(project) { - return checkProjectExists(project).then(function() { - activeProject = project; +function createProject(metadata) { + return Projects.create(metadata).then(function(p) { + return p.name; + }) +} +function setActiveProject(projectName) { + return loadProject(projectName).then(function(project) { var globalProjectSettings = settings.get("projects"); - globalProjectSettings.activeProject = project; + globalProjectSettings.activeProject = project.name; 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.flows-file",{path:flowsFullPath})); - console.log("Updated file targets to"); - console.log(flowsFullPath) - console.log(credentialsFile) - - return reloadActiveProject(project); - + // console.log("Updated file targets to"); + // console.log(flowsFullPath) + // console.log(credentialsFile) + return reloadActiveProject(); }) - // return when.promise(function(resolve,reject) { - // console.log("Activating project"); - // resolve(); - // }); }); } function updateProject(project,data) { - return checkProjectExists(project).then(function() { - if (data.credentialSecret) { - // TODO: this path assumes we aren't trying to migrate the secret - return setCredentialSecret(project,data).then(function(wasInvalid) { - if (wasInvalid) { - return reloadActiveProject(project); - } - }) - } 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)); - }); - } - }); + if (!activeProject || activeProject.name !== project) { + // TODO standardise + throw new Error("Cannot update inactive project"); + } + if (data.hasOwnProperty('credentialSecret')) { + return setCredentialSecret(data); + } else { + return activeProject.update(data); + } } +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; @@ -458,9 +232,14 @@ function getFlows() { initialFlowLoadComplete = true; log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir})); 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'); } @@ -510,7 +289,7 @@ function saveCredentials(credentials) { module.exports = { init: init, - listProjects: listProjects, + listProjects: Projects.list, getActiveProject: getActiveProject, setActiveProject: setActiveProject, getProject: getProject,