Initial projects implementation

This commit is contained in:
Nick O'Leary
2017-09-20 10:30:07 +01:00
parent 9a8b404054
commit b1cd13d629
49 changed files with 2711 additions and 420 deletions

View File

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

View File

@@ -133,6 +133,8 @@
"localfilesystem": {
"user-dir": "User directory : __path__",
"flows-file": "Flows file : __path__",
"changing-project": "Setting active project : __project__",
"active-project": "Active project : __project__",
"create": "Creating new __type__ file",
"empty": "Existing __type__ file is empty",
"invalid": "Existing __type__ file is not valid json",

View File

@@ -58,7 +58,7 @@ var api = module.exports = {
*/
var credentialsEncrypted = credentials.hasOwnProperty("$") && Object.keys(credentials).length === 1;
var setupEncryptionPromise = when.resolve();
if (encryptionEnabled === null) {
// if (encryptionEnabled === null) {
var defaultKey;
try {
defaultKey = settings.get('_credentialSecret');
@@ -73,12 +73,34 @@ var api = module.exports = {
} catch(err) {
userKey = false;
}
var projectKey = false;
try {
var projects = settings.get('projects');
if (projects && projects.activeProject) {
projectKey = projects.projects[projects.activeProject].credentialSecret;
}
} catch(err) {
}
if (projectKey) {
log.debug("red/runtime/nodes/credentials.load : using active project key - ignoring user provided key");
console.log(projectKey);
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) {
log.debug("red/runtime/nodes/credentials.load : user disabled encryption");
// User has disabled encryption
encryptionEnabled = false;
// Check if we have a generated _credSecret to decrypt with and remove
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");
if (credentialsEncrypted) {
try {
@@ -86,13 +108,18 @@ var api = module.exports = {
} catch(err) {
credentials = {};
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
var error = new Error("Failed to decrypt credentials");
error.code = "credentials_load_failed";
return when.reject(error);
}
}
dirty = true;
removeDefaultKey = true;
}
} else if (typeof userKey === 'string') {
log.debug("red/runtime/nodes/credentials.load : user provided key");
if (!projectKey) {
log.debug("red/runtime/nodes/credentials.load : user provided key");
}
// User has provided own encryption key, get the 32-byte hash of it
encryptionKey = crypto.createHash('sha256').update(userKey).digest();
encryptionEnabled = true;
@@ -107,6 +134,9 @@ var api = module.exports = {
} catch(err) {
credentials = {};
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
var error = new Error("Failed to decrypt credentials");
error.code = "credentials_load_failed";
return when.reject(error);
}
}
dirty = true;
@@ -136,7 +166,7 @@ var api = module.exports = {
log.debug("red/runtime/nodes/credentials.load : using default key");
}
}
}
//}
if (encryptionEnabled && !dirty) {
encryptedCredentials = credentials;
}
@@ -149,6 +179,9 @@ var api = module.exports = {
credentialCache = {};
dirty = true;
log.warn(log._("nodes.credentials.error",{message:err.toString()}))
var error = new Error("Failed to decrypt credentials");
error.code = "credentials_load_failed";
return when.reject(error);
}
} else {
credentialCache = credentials;

View File

@@ -73,15 +73,18 @@ function loadFlows() {
return storage.getFlows().then(function(config) {
log.debug("loaded flow revision: "+config.rev);
return credentials.load(config.credentials).then(function() {
events.emit("runtime-event",{id:"runtime-state",retain:true});
return config;
});
}).otherwise(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()}));
console.log(err.stack);
throw err;
});
}
function load() {
return setFlows(null,"load",false);
function load(forceStart) {
return setFlows(null,"load",false,forceStart);
}
/*
@@ -89,7 +92,7 @@ function load() {
* type - full/nodes/flows/load (default full)
* muteLog - don't emit the standard log messages (used for individual flow api)
*/
function setFlows(_config,type,muteLog) {
function setFlows(_config,type,muteLog,forceStart) {
type = type||"full";
var configSavePromise = null;
@@ -131,7 +134,7 @@ function setFlows(_config,type,muteLog) {
rev:flowRevision
};
activeFlowConfig = newFlowConfig;
if (started) {
if (forceStart || started) {
return stop(type,diff,muteLog).then(function() {
context.clean(activeFlowConfig);
start(type,diff,muteLog).then(function() {
@@ -228,6 +231,7 @@ function handleStatus(node,statusMessage) {
function start(type,diff,muteLog) {
console.log("START----")
//dumpActiveNodes();
type = type||"full";
started = true;
@@ -323,6 +327,9 @@ function start(type,diff,muteLog) {
}
function stop(type,diff,muteLog) {
if (!started) {
return when.resolve();
}
type = type||"full";
diff = diff||{
added:[],

View File

@@ -54,7 +54,10 @@ var storageModuleInterface = {
} catch (e) {
return when.reject(e);
}
return storageModule.init(runtime.settings);
if (storageModule.projects) {
storageModuleInterface.projects = storageModule.projects;
}
return storageModule.init(runtime.settings,runtime);
},
getFlows: function() {
return storageModule.getFlows().then(function(flows) {

View File

@@ -16,7 +16,6 @@
var fs = require('fs-extra');
var when = require('when');
var nodeFn = require('when/node/function');
var fspath = require("path");
var log = require("../../log");
@@ -24,6 +23,7 @@ var util = require("./util");
var library = require("./library");
var sessions = require("./sessions");
var runtimeSettings = require("./settings");
var projects = require("./projects");
var initialFlowLoadComplete = false;
var settings;
@@ -32,10 +32,9 @@ var flowsFullPath;
var flowsFileBackup;
var credentialsFile;
var credentialsFileBackup;
var oldCredentialsFile;
var localfilesystem = {
init: function(_settings) {
init: function(_settings, runtime) {
settings = _settings;
var promises = [];
@@ -62,50 +61,14 @@ var localfilesystem = {
}
}
if (settings.flowFile) {
flowsFile = settings.flowFile;
// handle Unix and Windows "C:\"
if ((flowsFile[0] == "/") || (flowsFile[1] == ":")) {
// Absolute path
flowsFullPath = flowsFile;
} else if (flowsFile.substring(0,2) === "./") {
// Relative to cwd
flowsFullPath = fspath.join(process.cwd(),flowsFile);
} else {
try {
fs.statSync(fspath.join(process.cwd(),flowsFile));
// Found in cwd
flowsFullPath = fspath.join(process.cwd(),flowsFile);
} catch(err) {
// Use userDir
flowsFullPath = fspath.join(settings.userDir,flowsFile);
}
}
} else {
flowsFile = 'flows_'+require('os').hostname()+'.json';
flowsFullPath = fspath.join(settings.userDir,flowsFile);
}
var ffExt = fspath.extname(flowsFullPath);
var ffName = fspath.basename(flowsFullPath);
var ffBase = fspath.basename(flowsFullPath,ffExt);
var ffDir = fspath.dirname(flowsFullPath);
credentialsFile = fspath.join(settings.userDir,ffBase+"_cred"+ffExt);
credentialsFileBackup = fspath.join(settings.userDir,"."+ffBase+"_cred"+ffExt+".backup");
oldCredentialsFile = fspath.join(settings.userDir,"credentials.json");
flowsFileBackup = fspath.join(ffDir,"."+ffName+".backup");
sessions.init(settings);
runtimeSettings.init(settings);
promises.push(library.init(settings));
promises.push(projects.init(settings, runtime));
var packageFile = fspath.join(settings.userDir,"package.json");
var packagePromise = when.resolve();
promises.push(library.init(settings));
if (!settings.readOnly) {
packagePromise = function() {
try {
@@ -124,64 +87,19 @@ var localfilesystem = {
return when.all(promises).then(packagePromise);
},
getFlows: function() {
if (!initialFlowLoadComplete) {
initialFlowLoadComplete = true;
log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir}));
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
}
return util.readFile(flowsFullPath,flowsFileBackup,[],'flow');
},
saveFlows: function(flows) {
if (settings.readOnly) {
return when.resolve();
}
getFlows: projects.getFlows,
saveFlows: projects.saveFlows,
getCredentials: projects.getCredentials,
saveCredentials: projects.saveCredentials,
try {
fs.renameSync(flowsFullPath,flowsFileBackup);
} catch(err) {
}
var flowData;
if (settings.flowFilePretty) {
flowData = JSON.stringify(flows,null,4);
} else {
flowData = JSON.stringify(flows);
}
return util.writeFile(flowsFullPath, flowData);
},
getCredentials: function() {
return util.readFile(credentialsFile,credentialsFileBackup,{},'credentials');
},
saveCredentials: function(credentials) {
if (settings.readOnly) {
return when.resolve();
}
try {
fs.renameSync(credentialsFile,credentialsFileBackup);
} catch(err) {
}
var credentialData;
if (settings.flowFilePretty) {
credentialData = JSON.stringify(credentials,null,4);
} else {
credentialData = JSON.stringify(credentials);
}
return util.writeFile(credentialsFile, credentialData);
},
getSettings: runtimeSettings.getSettings,
saveSettings: runtimeSettings.saveSettings,
getSessions: sessions.getSessions,
saveSessions: sessions.saveSessions,
getLibraryEntry: library.getLibraryEntry,
saveLibraryEntry: library.saveLibraryEntry
saveLibraryEntry: library.saveLibraryEntry,
projects: projects
};
module.exports = localfilesystem;

View File

@@ -0,0 +1,86 @@
/**
* 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 when = require('when');
var exec = require('child_process').exec;
var spawn = require('child_process').spawn;
function execCommand(command,args,cwd) {
return when.promise(function(resolve,reject) {
var fullCommand = command+" "+args.join(" ");
child = exec(fullCommand, {cwd: cwd, timeout:3000, killSignal: 'SIGTERM'}, function (error, stdout, stderr) {
if (error) {
reject(error);
} else {
resolve(stdout);
}
});
});
}
function runCommand(command,args,cwd) {
console.log(cwd,command,args);
return when.promise(function(resolve,reject) {
var child = spawn(command, args, {cwd:cwd, detached:true});
var stdout = "";
var stderr = "";
child.stdout.on('data', function(data) {
stdout += data;
});
child.stderr.on('data', function(data) {
stderr += data;
});
child.on('close', function(code) {
if (code !== 0) {
var err = new Error(stderr);
if (/fatal: could not read Username/.test(stderr)) {
err.code = "git_auth_failed";
} else if(/HTTP Basic: Access denied/.test(stderr)) {
err.code = "git_auth_failed";
} else {
err.code = "git_error";
}
return reject(err);
}
resolve(stdout);
});
});
}
function isAuthError(err) {
// var lines = err.toString().split("\n");
// lines.forEach(console.log);
}
var gitCommand = "git";
module.exports = {
initRepo: function(cwd) {
return runCommand(gitCommand,["init"],cwd);
},
pull: function(repo, cwd) {
if (repo.url) {
repo = repo.url;
}
var args = ["pull",repo,"master"];
return runCommand(gitCommand,args,cwd);
},
clone: function(repo, cwd) {
var args = ["clone",repo,"."];
return runCommand(gitCommand,args,cwd);
}
}

View File

@@ -0,0 +1,519 @@
/**
* 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 nodeFn = require('when/node/function');
var crypto = require('crypto');
var storageSettings = require("../settings");
var util = require("../util");
var gitTools = require("./git");
var settings;
var runtime;
var projectsDir;
function init(_settings, _runtime) {
settings = _settings;
runtime = _runtime;
log = runtime.log;
projectsDir = fspath.join(settings.userDir,"projects");
if (settings.flowFile) {
flowsFile = settings.flowFile;
// handle Unix and Windows "C:\"
if ((flowsFile[0] == "/") || (flowsFile[1] == ":")) {
// Absolute path
flowsFullPath = flowsFile;
} else if (flowsFile.substring(0,2) === "./") {
// Relative to cwd
flowsFullPath = fspath.join(process.cwd(),flowsFile);
} else {
try {
fs.statSync(fspath.join(process.cwd(),flowsFile));
// Found in cwd
flowsFullPath = fspath.join(process.cwd(),flowsFile);
} catch(err) {
// Use userDir
flowsFullPath = fspath.join(settings.userDir,flowsFile);
}
}
} else {
flowsFile = 'flows_'+require('os').hostname()+'.json';
flowsFullPath = fspath.join(settings.userDir,flowsFile);
}
var ffExt = fspath.extname(flowsFullPath);
var ffBase = fspath.basename(flowsFullPath,ffExt);
flowsFileBackup = getBackupFilename(flowsFullPath);
credentialsFile = fspath.join(settings.userDir,ffBase+"_cred"+ffExt);
credentialsFileBackup = getBackupFilename(credentialsFile)
if (!settings.readOnly) {
return util.promiseDir(projectsDir)
//TODO: this is accessing settings from storage directly as settings
// has not yet been initialised. That isn't ideal - can this be deferred?
.then(storageSettings.getSettings)
.then(function(globalSettings) {
if (!globalSettings.projects) {
// TODO: Migration Case
console.log("TODO: Migration from single file to project");
globalSettings.projects = {
activeProject: "",
projects: {}
}
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 {
return when.resolve();
}
}
function getBackupFilename(filename) {
var ffName = fspath.basename(filename);
var ffDir = fspath.dirname(filename);
return fspath.join(ffDir,"."+ffName+".backup");
}
function listProjects() {
return nodeFn.call(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]||{};
}
// console.log(projectSettings);
var projectData = {
name: project
};
var promises = [];
checkProjectFiles(project).then(function(missingFiles) {
if (missingFiles.length > 0) {
projectData.missingFiles = missingFiles;
}
if (missingFiles.indexOf('package.json') === -1) {
promises.push(nodeFn.call(fs.readFile,fspath.join(projectPath,"package.json"),"utf8").then(function(content) {
var package = util.parseJSON(content);
projectData.dependencies = package.dependencies||{};
}));
}
if (missingFiles.indexOf('README.md') === -1) {
promises.push(nodeFn.call(fs.readFile,fspath.join(projectPath,"README.md"),"utf8").then(function(content) {
projectData.description = content;
}));
} else {
projectData.description = "";
}
when.settle(promises).then(function() {
resolve(projectData);
})
// if (missingFiles.indexOf('flow_cred.json') === -1) {
// promises.push(nodeFn.call(fs.readFile,fspath.join(projectPath,"flow_cred.json"),"utf8").then(function(creds) {
// var credentials = util.parseJSON(creds);
// if (credentials.hasOwnProperty('$')) {
// // try {
// // decryptCredentials
// // }
// }
// }));
// }
});
// fs.stat(projectPath,function(err,stat) {
// if (err) {
// return resolve(null);
// }
// resolve(nodeFn.call(fs.readFile,projectPackage,'utf8').then(util.parseJSON));
// })
}).otherwise(function(err) {
console.log(err);
var e = new Error("NLD: project not found");
e.code = "project_not_found";
throw e;
});
}
var encryptionAlgorithm = "aes-256-ctr";
function decryptCredentials(key,credentials) {
var creds = credentials["$"];
var initVector = new Buffer(creds.substring(0, 32),'hex');
creds = creds.substring(32);
var decipher = crypto.createDecipheriv(encryptionAlgorithm, key, initVector);
var decrypted = decipher.update(creds, 'base64', 'utf8') + decipher.final('utf8');
return JSON.parse(decrypted);
}
function setCredentialSecret(project,secret) {
var globalProjectSettings = settings.get("projects");
globalProjectSettings.projects = globalProjectSettings.projects || {};
globalProjectSettings.projects[project] = globalProjectSettings.projects[project] || {};
globalProjectSettings.projects[project].credentialSecret = secret;
return settings.set("projects",globalProjectSettings);
}
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() {
if (metadata.credentialSecret) {
return setCredentialSecret(project,metadata.credentialSecret);
}
return when.resolve();
}).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);
}).otherwise(function(error) {
fs.remove(projectPath,function() {
reject(error);
});
})
} else {
createDefaultProject(metadata).then(function() { resolve(project)}).otherwise(reject);
}
}).otherwise(reject);
})
})
}
function createProjectDirectory(project) {
var projectPath = fspath.join(projectsDir,project);
return util.promiseDir(projectPath).then(function() {
return gitTools.initRepo(projectPath)
});
}
var defaultFileSet = {
"package.json": function(project) {
return JSON.stringify({
"name": project.name,
"description": project.summary||"A Node-RED Project",
"version": "0.0.1",
"dependencies": {}
},"",4);
},
"README.md": function(project) {
return project.name+"\n"+("=".repeat(project.name.length))+"\n\n"+(project.summary||"A Node-RED Project")+"\n\n";
},
"settings.json": function() { return "{}" },
"flow.json": function() { return "[]" },
"flow_cred.json": function() { return "{}" }
}
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);
}
function checkProjectExists(project) {
var projectPath = fspath.join(projectsDir,project);
return nodeFn.call(fs.stat,projectPath).otherwise(function(err) {
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(nodeFn.call(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 nodeFn.call(listFiles,projectPath,"/");
}
function getFile(project,path) {
}
function listFiles(root,path,done) {
var entries = [];
var fullPath = fspath.join(root,path);
fs.readdir(fullPath, function(err,fns) {
var childCount = fns.length;
fns.sort().forEach(function(fn) {
if (fn === ".git") {
childCount--;
return;
}
var child = {
path: fspath.join(path,fn),
name: fn
};
entries.push(child);
var childFullPath = fspath.join(fullPath,fn);
fs.lstat(childFullPath, function(err, stats) {
if (stats.isDirectory()) {
child.type = 'd';
listFiles(root,child.path,function(err,children) {
child.children = children;
childCount--;
if (childCount === 0) {
done(null,entries);
}
})
} else {
child.type = 'f';
childCount--;
console.log(child,childCount)
if (childCount === 0) {
done(null,entries);
}
}
});
});
});
}
var activeProject
function getActiveProject() {
return activeProject;
}
function reloadActiveProject(project) {
return runtime.nodes.stopFlows().then(function() {
return runtime.nodes.loadFlows(true).then(function() {
runtime.events.emit("runtime-event",{id:"project-change",payload:{ project: project}});
}).otherwise(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}});
throw err;
});
});
}
function setActiveProject(project) {
return checkProjectExists(project).then(function() {
activeProject = project;
var globalProjectSettings = settings.get("projects");
globalProjectSettings.activeProject = project;
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);
})
// 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.credentialSecret).then(function() {
return reloadActiveProject(project);
})
} else if (data.description) {
var projectPath = fspath.join(projectsDir,project);
var readmeFile = fspath.join(projectPath,"README.md");
return util.writeFile(readmeFile, data.description);
} else if (data.dependencies) {
var projectPath = fspath.join(projectsDir,project);
var packageJSON = fspath.join(projectPath,"package.json");
return nodeFn.call(fs.readFile,packageJSON,"utf8").then(function(content) {
var package = util.parseJSON(content);
package.dependencies = data.dependencies;
return util.writeFile(packageJSON,JSON.stringify(package,"",4));
});
}
});
}
var initialFlowLoadComplete = false;
var flowsFile;
var flowsFullPath;
var flowsFileBackup;
var credentialsFile;
var credentialsFileBackup;
function getFlows() {
if (!initialFlowLoadComplete) {
initialFlowLoadComplete = true;
log.info(log._("storage.localfilesystem.user-dir",{path:settings.userDir}));
log.info(log._("storage.localfilesystem.flows-file",{path:flowsFullPath}));
log.info(log._("storage.localfilesystem.active-project",{project:activeProject||"none"}));
}
return util.readFile(flowsFullPath,flowsFileBackup,[],'flow');
}
function saveFlows(flows) {
if (settings.readOnly) {
return when.resolve();
}
try {
fs.renameSync(flowsFullPath,flowsFileBackup);
} catch(err) {
}
var flowData;
if (settings.flowFilePretty) {
flowData = JSON.stringify(flows,null,4);
} else {
flowData = JSON.stringify(flows);
}
return util.writeFile(flowsFullPath, flowData);
}
function getCredentials() {
return util.readFile(credentialsFile,credentialsFileBackup,{},'credentials');
}
function saveCredentials(credentials) {
if (settings.readOnly) {
return when.resolve();
}
try {
fs.renameSync(credentialsFile,credentialsFileBackup);
} catch(err) {
}
var credentialData;
if (settings.flowFilePretty) {
credentialData = JSON.stringify(credentials,null,4);
} else {
credentialData = JSON.stringify(credentials);
}
return util.writeFile(credentialsFile, credentialData);
}
module.exports = {
init: init,
listProjects: listProjects,
getActiveProject: getActiveProject,
setActiveProject: setActiveProject,
getProject: getProject,
createProject: createProject,
updateProject: updateProject,
getFiles: getFiles,
getFlows: getFlows,
saveFlows: saveFlows,
getCredentials: getCredentials,
saveCredentials: saveCredentials
};