@iobroker/db-base
Advanced tools
Comparing version 1.0.10 to 1.1.0
@@ -74,8 +74,6 @@ /** | ||
// Create data directory | ||
if (!fs.existsSync(this.dataDir)) { | ||
fs.mkdirSync(this.dataDir); | ||
} | ||
this.datasetName = path.join(this.dataDir, this.settings.fileDB.fileName); | ||
const parts = path.dirname(this.datasetName); | ||
fs.ensureDirSync(parts); | ||
this.datasetName = path.join(this.dataDir, this.settings.fileDB.fileName); | ||
this.stateTimer = null; | ||
@@ -86,23 +84,3 @@ | ||
if (!this.settings.backup.disabled) { | ||
this.zlib = this.zlib || require('zlib'); | ||
// Interval in minutes => to milliseconds | ||
this.settings.backup.period = this.settings.backup.period === undefined ? 120 : parseInt(this.settings.backup.period); | ||
if (isNaN(this.settings.backup.period)) { | ||
this.settings.backup.period = 120; | ||
} | ||
this.settings.backup.period *= 60000; | ||
this.settings.backup.files = this.settings.backup.files === undefined ? 24 : parseInt(this.settings.backup.files); | ||
if (isNaN(this.settings.backup.files)) { | ||
this.settings.backup.files = 24; | ||
} | ||
this.settings.backup.hours = this.settings.backup.hours === undefined ? 48 : parseInt(this.settings.backup.hours); | ||
if (isNaN(this.settings.backup.hours)) { | ||
this.settings.backup.hours = 48; | ||
} | ||
// Create backup directory | ||
if (!fs.existsSync(this.backupDir)) { | ||
fs.mkdirSync(this.backupDir); | ||
} | ||
this.initBackupDir(); | ||
} | ||
@@ -113,37 +91,65 @@ | ||
this.log.debug(this.namespace + ' Data File: ' + this.datasetName); | ||
} | ||
/** @returns {Promise<void>} */ | ||
async open() { | ||
// load values from file | ||
if (fs.existsSync(this.datasetName)) { | ||
this.dataset = await this.loadDataset(this.datasetName); | ||
} | ||
/** | ||
* Loads a dataset file | ||
* | ||
* @param datasetName {string} Filename of the file to load | ||
* @returns {Promise<Record<string, any>>} read data, normally as object | ||
*/ | ||
async loadDatasetFile(datasetName) { | ||
if (!(await fs.pathExists(datasetName))) { | ||
throw new Error(`Database file ${datasetName} does not exists.`); | ||
} | ||
return fs.readJSON(datasetName); | ||
} | ||
/** | ||
* Loads the dataset including pot. Fallback handling | ||
* | ||
* @param datasetName {string} Filename of the file to load | ||
* @returns {Promise<Record<string, any>>} dataset read as object | ||
*/ | ||
async loadDataset(datasetName) { | ||
let ret = {}; | ||
try { | ||
ret = await this.loadDatasetFile(datasetName); | ||
} catch (err) { | ||
this.log.error(`${this.namespace} Cannot load ${datasetName}: ${err.message}. Try last Backup!`); | ||
try { | ||
this.dataset = fs.readJSONSync(this.datasetName); | ||
} catch (e) { | ||
this.log.error(this.namespace + ' Cannot parse ' + this.datasetName + ': ' + e.message); | ||
if (fs.existsSync(this.datasetName + '.bak')) { | ||
try { | ||
this.dataset = fs.readJSONSync(this.datasetName + '.bak'); | ||
} catch (e) { | ||
this.log.error(this.namespace + ' Cannot parse ' + this.datasetName + '.bak: ' + e.message); | ||
this.dataset = {}; | ||
} | ||
} else { | ||
this.dataset = {}; | ||
} | ||
ret = await this.loadDatasetFile(datasetName + '.bak'); | ||
} catch (err) { | ||
this.log.error(`${this.namespace} Cannot load ${datasetName}.bak: ${err.message}. Continue with empty dataset!`); | ||
} | ||
} else if (fs.existsSync(this.datasetName + '.bak')) { | ||
try { | ||
this.dataset = fs.readJSONSync(this.datasetName + '.bak'); | ||
} catch (e) { | ||
this.log.error(this.namespace + ' Cannot parse ' + this.datasetName + '.bak: ' + e.message); | ||
this.dataset = {}; | ||
} | ||
} else { | ||
this.dataset = {}; | ||
} | ||
return ret; | ||
} | ||
// Check if directory exists | ||
this.datasetName = this.datasetName.replace(/\\/g, '/'); | ||
initBackupDir() { | ||
this.zlib = this.zlib || require('zlib'); | ||
// Interval in minutes => to milliseconds | ||
this.settings.backup.period = this.settings.backup.period === undefined ? 120 : parseInt(this.settings.backup.period); | ||
if (isNaN(this.settings.backup.period)) { | ||
this.settings.backup.period = 120; | ||
} | ||
this.settings.backup.period *= 60000; | ||
const parts = path.dirname(this.datasetName); | ||
if (!fs.existsSync(parts)) { | ||
fs.mkdirSync(parts); | ||
this.settings.backup.files = this.settings.backup.files === undefined ? 24 : parseInt(this.settings.backup.files); | ||
if (isNaN(this.settings.backup.files)) { | ||
this.settings.backup.files = 24; | ||
} | ||
this.settings.backup.hours = this.settings.backup.hours === undefined ? 48 : parseInt(this.settings.backup.hours); | ||
if (isNaN(this.settings.backup.hours)) { | ||
this.settings.backup.hours = 48; | ||
} | ||
// Create backup directory | ||
fs.ensureDirSync(this.backupDir); | ||
} | ||
@@ -270,47 +276,79 @@ | ||
saveState() { | ||
/** | ||
* Handle saving the dataset incl. backups | ||
*/ | ||
async saveState() { | ||
if (this.stateTimer) { | ||
clearTimeout(this.stateTimer); | ||
this.stateTimer = null; | ||
} | ||
const jsonString = await this.saveDataset(); | ||
if (!this.settings.backup.disabled && jsonString) { | ||
this.saveBackup(jsonString); | ||
} | ||
} | ||
/** | ||
* Saves the dataset into File incl. handling of a fallback backup file | ||
* | ||
* @returns {Promise<string>} JSON string of the complete dataset to also be stored into a compressed backup file | ||
*/ | ||
async saveDataset() { | ||
const jsonString = JSON.stringify(this.dataset); | ||
try { | ||
if (fs.existsSync(this.datasetName)) { | ||
const old = fs.readFileSync(this.datasetName); | ||
fs.writeFileSync(this.datasetName + '.bak', old); | ||
if (await fs.pathExists(this.datasetName)) { | ||
await fs.move(this.datasetName, `${this.datasetName}.bak`, { overwrite: true }); | ||
} | ||
} catch (e) { | ||
this.log.error(`${this.namespace} Cannot save backup file ${this.datasetName}.bak: ${e.message}`); | ||
} | ||
const actual = JSON.stringify(this.dataset); | ||
fs.writeFileSync(this.datasetName, actual); | ||
try { | ||
await fs.writeFile(this.datasetName, jsonString); | ||
} catch (e) { | ||
this.log.error(`${this.namespace} Cannot save ${this.datasetName}: ${e.message}`); | ||
} | ||
if (!this.settings.backup.disabled) { | ||
// save files for the last x hours | ||
const now = Date.now(); | ||
return jsonString; | ||
} | ||
// makes backups only if settings.backupInterval is not 0 | ||
if (this.settings.backup.period && (!this.lastSave || now - this.lastSave > this.settings.backup.period)) { | ||
this.lastSave = now; | ||
const backFileName = path.join(this.backupDir, this.getTimeStr(now) + '_' + this.settings.fileDB.fileName + '.gz'); | ||
/** | ||
* Stores a compressed backup of the DB in definable intervals | ||
* | ||
* @param jsonString {string} JSON string of the complete dataset to also be stored into a compressed backup file | ||
*/ | ||
saveBackup(jsonString) { | ||
// save files for the last x hours | ||
const now = Date.now(); | ||
if (!fs.existsSync(backFileName)) { | ||
this.zlib = this.zlib || require('zlib'); | ||
const output = fs.createWriteStream(backFileName); | ||
output.on('error', err => { | ||
this.log.error(this.namespace + ' Cannot save ' + this.datasetName + ': ' + err); | ||
}); | ||
const compress = this.zlib.createGzip(); | ||
/* The following line will pipe everything written into compress to the file stream */ | ||
compress.pipe(output); | ||
/* Since we're piped through the file stream, the following line will do: | ||
'Hello World!'->gzip compression->file which is the desired effect */ | ||
compress.write(actual); | ||
compress.end(); | ||
// makes backups only if settings.backupInterval is not 0 | ||
if (this.settings.backup.period && (!this.lastSave || now - this.lastSave > this.settings.backup.period)) { | ||
this.lastSave = now; | ||
const backFileName = path.join(this.backupDir, this.getTimeStr(now) + '_' + this.settings.fileDB.fileName + '.gz'); | ||
// analyse older files | ||
this.deleteOldBackupFiles(); | ||
} | ||
try { | ||
if (!fs.existsSync(backFileName)) { | ||
this.zlib = this.zlib || require('zlib'); | ||
const output = fs.createWriteStream(backFileName); | ||
output.on('error', err => { | ||
this.log.error(this.namespace + ' Cannot save ' + this.datasetName + ': ' + err); | ||
}); | ||
const compress = this.zlib.createGzip(); | ||
/* The following line will pipe everything written into compress to the file stream */ | ||
compress.pipe(output); | ||
/* Since we're piped through the file stream, the following line will do: | ||
'Hello World!'->gzip compression->file which is the desired effect */ | ||
compress.write(jsonString); | ||
compress.end(); | ||
// analyse older files | ||
this.deleteOldBackupFiles(); | ||
} | ||
} catch (e) { | ||
this.log.error(`${this.namespace} Cannot save backup ${backFileName}: ${e.message}`); | ||
} | ||
} catch (e) { | ||
this.log.error(this.namespace + ' Cannot save ' + this.datasetName + ': ' + e.message); | ||
} | ||
if (this.stateTimer) { | ||
clearTimeout(this.stateTimer); | ||
this.stateTimer = null; | ||
} | ||
} | ||
@@ -357,3 +395,5 @@ | ||
async destroy() { | ||
this.stateTimer && this.saveState(); | ||
if (this.stateTimer) { | ||
await this.saveState(); | ||
} | ||
} | ||
@@ -360,0 +400,0 @@ } |
{ | ||
"name": "@iobroker/db-base", | ||
"version": "1.0.10", | ||
"version": "1.1.0", | ||
"engines": { | ||
@@ -34,3 +34,3 @@ "node": ">=10.0.0" | ||
}, | ||
"gitHead": "758fd93aae496663dff8b9d8dbe38d06fdb739be" | ||
"gitHead": "48a8b298f8c6bd10ca3da29f404fc4a0c756ba9c" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
28455
669
0