From af636870d44bf1436ed3e91f92af03cf1f3218ed Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 6 Oct 2020 15:42:52 +0100 Subject: [PATCH] Add better error message if context file gets corrupted --- .../lib/nodes/context/localfilesystem.js | 27 ++++++++++++------- .../runtime/locales/en-US/runtime.json | 1 + 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/node_modules/@node-red/runtime/lib/nodes/context/localfilesystem.js b/packages/node_modules/@node-red/runtime/lib/nodes/context/localfilesystem.js index 9755681d8..ef499f0f9 100644 --- a/packages/node_modules/@node-red/runtime/lib/nodes/context/localfilesystem.js +++ b/packages/node_modules/@node-red/runtime/lib/nodes/context/localfilesystem.js @@ -137,16 +137,15 @@ function stringify(value) { return { json: result, circular: hasCircular }; } - -function writeFileAtomic(storagePath, content) { +async function writeFileAtomic(storagePath, content) { // To protect against file corruption, write to a tmp file first and then // rename to the destination file let finalFile = storagePath + ".json"; let tmpFile = finalFile + "."+Date.now()+".tmp"; - return fs.outputFile(tmpFile, content, "utf8").then(function() { - return fs.rename(tmpFile,finalFile); - }) + await fs.outputFile(tmpFile, content, "utf8"); + return fs.rename(tmpFile,finalFile); } + function LocalFileSystem(config){ this.config = config; this.storageBaseDir = getBasePath(this.config); @@ -169,6 +168,7 @@ LocalFileSystem.prototype.open = function(){ if (this.cache) { var scopes = []; var promises = []; + var contextFiles = []; return listFiles(self.storageBaseDir).then(function(files) { files.forEach(function(file) { var parts = file.split(path.sep); @@ -179,15 +179,22 @@ LocalFileSystem.prototype.open = function(){ } else { scopes.push(parts[1].substring(0,parts[1].length-5)+":"+parts[0]); } - promises.push(loadFile(path.join(self.storageBaseDir,file))); + let contextFile = path.join(self.storageBaseDir,file); + contextFiles.push(contextFile) + promises.push(loadFile(contextFile)); }) 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]); - }) + try { + var data = res[i]?JSON.parse(res[i]):{}; + Object.keys(data).forEach(function(key) { + self.cache.set(scope,key,data[key]); + }) + } catch(err) { + let error = new Error(log._("context.localfilesystem.invalid-json",{file: contextFiles[i]})) + throw error; + } }); }).catch(function(err){ if(err.code == 'ENOENT') { diff --git a/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json b/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json index 3047295de..bc866e09e 100644 --- a/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json +++ b/packages/node_modules/@node-red/runtime/locales/en-US/runtime.json @@ -176,6 +176,7 @@ "error-invalid-default-module": "Default context store unknown: '__storage__'", "unknown-store": "Unknown context store '__name__' specified. Using default store.", "localfilesystem": { + "invalid-json": "Invalid JSON in context file '__file__'", "error-circular": "Context __scope__ contains a circular reference that cannot be persisted", "error-write": "Error writing context: __message__" }