/** * 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. **/ /** * Local file-system based context storage * * Configuration options: * { * base: "contexts", // the base directory to use * // default: "contexts" * dir: "/path/to/storage", // the directory to create the base directory in * // default: settings.userDir * cache: true // whether to cache contents in memory * // default: true * } * * * $HOME/.node-red/contexts * ├── global * │ └── global_context.json * ├── * │ ├── flow_context.json * │ ├── .json * │ └── .json * └── * ├── flow_context.json * ├── .json * └── .json */ var fs = require('fs-extra'); var path = require("path"); var util = require("../../util"); var MemoryStore = require("./memory"); function getStoragePath(storageBaseDir, scope) { if(scope.indexOf(":") === -1){ if(scope === "global"){ return path.join(storageBaseDir,"global",scope); }else{ // scope:flow return path.join(storageBaseDir,scope,"flow"); } }else{ // scope:local var ids = scope.split(":") return path.join(storageBaseDir,ids[1],ids[0]); } } function getBasePath(config) { var base = config.base || "contexts"; var storageBaseDir; if (!config.dir) { if(config.settings && config.settings.userDir){ storageBaseDir = path.join(config.settings.userDir, base); }else{ try { fs.statSync(path.join(process.env.NODE_RED_HOME,".config.json")); storageBaseDir = path.join(process.env.NODE_RED_HOME, base); } catch(err) { try { // Consider compatibility for older versions if (process.env.HOMEPATH) { fs.statSync(path.join(process.env.HOMEPATH,".node-red",".config.json")); storageBaseDir = path.join(process.env.HOMEPATH, ".node-red", base); } } catch(err) { } if (!storageBaseDir) { storageBaseDir = path.join(process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH || process.env.NODE_RED_HOME,".node-red", base); } } } }else{ storageBaseDir = path.join(config.dir, base); } return storageBaseDir; } function loadFile(storagePath){ return fs.pathExists(storagePath).then(function(exists){ if(exists === true){ return fs.readFile(storagePath, "utf8"); }else{ return Promise.resolve(undefined); } }).catch(function(err){ throw Promise.reject(err); }); } function LocalFileSystem(config){ this.config = config; this.storageBaseDir = getBasePath(this.config); if (config.hasOwnProperty('cache')?config.cache:true) { this.cache = MemoryStore({}); } } LocalFileSystem.prototype.open = function(){ var self = this; if (this.cache) { var scopes = []; var promises = []; var subdirs = []; var subdirPromises = []; return fs.readdir(self.storageBaseDir).then(function(dirs){ dirs.forEach(function(fn) { var p = getStoragePath(self.storageBaseDir ,fn)+".json"; scopes.push(fn); promises.push(loadFile(p)); subdirs.push(path.join(self.storageBaseDir,fn)); subdirPromises.push(fs.readdir(path.join(self.storageBaseDir,fn))); }) return Promise.all(subdirPromises); }).then(function(dirs) { dirs.forEach(function(files,i) { files.forEach(function(fn) { if (fn !== 'flow.json' && fn !== 'global.json') { scopes.push(fn.substring(0,fn.length-5)+":"+scopes[i]); promises.push(loadFile(path.join(subdirs[i],fn))) } }); }) return Promise.all(promises); }).then(function(res) { scopes.forEach(function(scope,i) { var data = res[i]?JSON.parse(res[i]):{}; Object.keys(data).forEach(function(key) { self.cache.set(scope,key,data[key]); }) }); }).catch(function(err){ if(err.code == 'ENOENT') { return fs.ensureDir(self.storageBaseDir); }else{ return Promise.reject(err); } }); } else { return Promise.resolve(); } } LocalFileSystem.prototype.close = function(){ return Promise.resolve(); } LocalFileSystem.prototype.get = function(scope, key, callback) { if (this.cache) { return this.cache.get(scope,key,callback); } if(typeof callback !== "function"){ throw new Error("Callback must be a function"); } var storagePath = getStoragePath(this.storageBaseDir ,scope); loadFile(storagePath + ".json").then(function(data){ if(data){ data = JSON.parse(data); if (!Array.isArray(key)) { callback(null, util.getObjectProperty(data,key)); } else { var results = [undefined]; for (var i=0;i