UNPKG

zwave-js-ui

Version:

Z-Wave Control Panel and MQTT Gateway

118 lines (117 loc) 3.72 kB
import jsonFile from 'jsonfile'; import { storeBackupsDir, storeDir } from "../config/app.js"; import { module } from "./logger.js"; import { recursive as merge } from 'merge'; import archiver from 'archiver'; import { createWriteStream, existsSync } from 'node:fs'; import { ensureDir, fileDate, joinPath } from "./utils.js"; const logger = module('Store'); export const STORE_BACKUP_PREFIX = 'store-backup_'; // Default dependencies for production use const defaultDeps = { readFile: jsonFile.readFile.bind(jsonFile), writeFile: jsonFile.writeFile.bind(jsonFile), }; /** Constructor **/ export class StorageHelper { _store; config; readFile; writeFile; get store() { return this._store; } constructor(deps) { this._store = {}; this.readFile = deps?.readFile || defaultDeps.readFile; this.writeFile = deps?.writeFile || defaultDeps.writeFile; } async init(config) { this.config = config; for (const model in config) { const res = await this._getFile(config[model]); this._store[res.file] = res.data; } return this._store; } async backup(res) { const backupFile = `${STORE_BACKUP_PREFIX}${fileDate()}.zip`; await ensureDir(storeBackupsDir); const fileStream = createWriteStream(joinPath(storeBackupsDir, backupFile)); return new Promise((resolve, reject) => { const archive = archiver('zip'); archive.on('error', (err) => { reject(err); }); // on stream closed we can end the request archive.on('end', () => { resolve(backupFile); }); if (res) { res.set({ 'Content-Type': 'application/json', 'Content-Disposition': `attachment; filename="${backupFile}"`, }); archive.pipe(res); } archive.pipe(fileStream); // backup zwavejs files too archive.glob('*.jsonl', { cwd: storeDir, }); for (const model in this.config) { const config = this.config[model]; const filePath = joinPath(storeDir, config.file); if (existsSync(filePath)) { archive.file(filePath, { name: config.file, }); } } void archive.finalize(); }); } async _getFile(config) { let err; let data; try { data = await this.readFile(joinPath(storeDir, config.file)); } catch (error) { err = error; } // ignore ENOENT error if (err) { if (err.code !== 'ENOENT') { logger.error('Error reading file: ' + config.file, err); } else { logger.warn(`${config.file} not found`); } } // replace data with default if (!data) { data = config.default; } else { data = Array.isArray(data) ? data : merge(config.default, data); } return { file: config.file, data: data }; } get(model) { if (this._store[model.file]) { return this._store[model.file]; } else { throw Error('Requested file not present in store: ' + model.file); } } async put(model, data) { await this.writeFile(joinPath(storeDir, model.file), data); this._store[model.file] = data; return data; } } export default new StorageHelper();