zwave-js-ui/api/lib/jsonStore.ts

137 lines
3.0 KiB
TypeScript

import { readFile, writeFile } from 'jsonfile'
import { storeBackupsDir, storeDir } from '../config/app'
import { StoreFile, StoreKeys } from '../config/store'
import { module } from './logger'
import * as utils from './utils'
import { recursive as merge } from 'merge'
import archiver from 'archiver'
import { createWriteStream } from 'fs'
import { mkdirp, existsSync } from 'fs-extra'
import { Response } from 'express'
const logger = module('Store')
export const STORE_BACKUP_PREFIX = 'store-backup_'
/**
Constructor
**/
export class StorageHelper {
private _store: Record<StoreKeys, any>
private config: Record<StoreKeys, StoreFile>
public get store() {
return this._store
}
constructor() {
this._store = {} as Record<StoreKeys, any>
}
async init(config: Record<StoreKeys, StoreFile>) {
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?: Response): Promise<string> {
const backupFile = `${STORE_BACKUP_PREFIX}${utils.fileDate()}.zip`
await mkdirp(storeBackupsDir)
const fileStream = createWriteStream(
utils.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: StoreFile = this.config[model]
const filePath = utils.joinPath(storeDir, config.file)
if (existsSync(filePath)) {
archive.file(filePath, {
name: config.file,
})
}
}
void archive.finalize()
})
}
private async _getFile(config: StoreFile) {
let err: { code: string } | undefined
let data: any
try {
data = await readFile(utils.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: StoreFile) {
if (this._store[model.file]) {
return this._store[model.file]
} else {
throw Error('Requested file not present in store: ' + model.file)
}
}
async put(model: StoreFile, data: any) {
await writeFile(utils.joinPath(storeDir, model.file), data)
this._store[model.file] = data
return data
}
}
export default new StorageHelper()