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')
|
fs = require('fs')
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
|
let exitHook: any = null
|
||||||
|
try {
|
||||||
|
exitHook = require('exit-hook')
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
export class JsonFileStorage extends JsonMemoryStorage {
|
export class JsonFileStorage extends JsonMemoryStorage {
|
||||||
private readonly _filename: string
|
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()
|
super()
|
||||||
if (!fs) throw new Error('Node fs module is not available!')
|
if (!fs) throw new Error('Node fs module is not available!')
|
||||||
|
|
||||||
this._filename = filename
|
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> {
|
async load(): Promise<void> {
|
||||||
|
@ -32,19 +77,45 @@ export class JsonFileStorage extends JsonMemoryStorage {
|
||||||
|
|
||||||
save(): Promise<void> {
|
save(): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// calling writeFile immediately seems to destroy session when using debugger
|
fs.writeFile(
|
||||||
setTimeout(
|
this._safe ? this._filename + '.tmp' : this._filename,
|
||||||
() =>
|
this._saveJson(),
|
||||||
fs.writeFile(
|
(err?: Error) => {
|
||||||
this._filename,
|
if (this._safe) {
|
||||||
this._saveJson(),
|
fs.rename(
|
||||||
(err?: Error) => {
|
this._filename + '.tmp',
|
||||||
if (err) reject(err)
|
this._filename,
|
||||||
else resolve()
|
(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