feat(core): safe file write, and also cleanup function using exit-hook
package
This commit is contained in:
parent
48d328f486
commit
63471115ae
1 changed files with 84 additions and 13 deletions
|
@ -5,14 +5,59 @@ try {
|
|||
fs = require('fs')
|
||||
} catch (e) {}
|
||||
|
||||
let exitHook: any = null
|
||||
try {
|
||||
exitHook = require('exit-hook')
|
||||
} catch (e) {}
|
||||
|
||||
export class JsonFileStorage extends JsonMemoryStorage {
|
||||
private readonly _filename: string
|
||||
private readonly _safe: boolean
|
||||
private readonly _cleanup: boolean
|
||||
|
||||
constructor(filename: string) {
|
||||
private readonly _unsubscribe: () => void
|
||||
|
||||
constructor(
|
||||
filename: string,
|
||||
params?: {
|
||||
/**
|
||||
* Whether to save file "safely", meaning that the file will first be saved
|
||||
* to `${filename}.tmp`, and then renamed to `filename`,
|
||||
* instead of writing directly to `filename`.
|
||||
*
|
||||
* This solves the issue with the storage being saved as
|
||||
* a blank file because of the app being stopped while
|
||||
* the storage is being written.
|
||||
*
|
||||
* Defaults to `true`
|
||||
*/
|
||||
safe?: boolean
|
||||
|
||||
/**
|
||||
* Whether to save file on process exit.
|
||||
* Uses [`exit-hook`](https://www.npmjs.com/package/exit-hook)
|
||||
*
|
||||
* Defaults to `true` if `exit-hook` is installed, otherwise `false`
|
||||
*/
|
||||
cleanup?: boolean
|
||||
}
|
||||
) {
|
||||
super()
|
||||
if (!fs) throw new Error('Node fs module is not available!')
|
||||
|
||||
this._filename = filename
|
||||
this._safe = params?.safe ?? true
|
||||
this._cleanup = params?.cleanup ?? !!exitHook
|
||||
|
||||
if (this._cleanup && !exitHook) {
|
||||
throw new Error(
|
||||
'Cleanup on exit is supported through `exit-hook` library, install it first!'
|
||||
)
|
||||
}
|
||||
|
||||
if (this._cleanup) {
|
||||
this._unsubscribe = exitHook(this._onProcessExit.bind(this))
|
||||
}
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
|
@ -32,19 +77,45 @@ export class JsonFileStorage extends JsonMemoryStorage {
|
|||
|
||||
save(): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// calling writeFile immediately seems to destroy session when using debugger
|
||||
setTimeout(
|
||||
() =>
|
||||
fs.writeFile(
|
||||
this._filename,
|
||||
this._safe ? this._filename + '.tmp' : this._filename,
|
||||
this._saveJson(),
|
||||
(err?: Error) => {
|
||||
if (this._safe) {
|
||||
fs.rename(
|
||||
this._filename + '.tmp',
|
||||
this._filename,
|
||||
(err?: Error) => {
|
||||
if (err) reject(err)
|
||||
else resolve()
|
||||
}
|
||||
),
|
||||
0
|
||||
)
|
||||
} else {
|
||||
if (err) reject(err)
|
||||
else resolve()
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
private _onProcessExit(): void {
|
||||
// on exit handler must be synchronous, thus we use sync methods here
|
||||
|
||||
try {
|
||||
fs.writeFileSync(this._filename, this._saveJson())
|
||||
} catch (e) {}
|
||||
|
||||
if (this._safe) {
|
||||
try {
|
||||
fs.unlinkSync(this._filename + '.tmp')
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
if (this._cleanup) {
|
||||
this._unsubscribe()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue