Files
ontime/apps/server/src/services/restore-service/restore.service.ts
T
Carlos Valente 1849b4d39f Deps migration (#1988)
* chore: migrate eslint to oxlint

* chore: migrate prettier to oxfmt

* chore: migrate typescript

* chore: toThrow should have a expected value

* chore: cast test value as Day

* chore: small title fix

* chore: mocks should be hoisted

* chore: incorrect async useage

* chore: test should be inside description

* chore: test sohuld include an expeced

* chore: oxfmt

---------

Co-authored-by: alex-Arc <omnivox@LAPTOP-RC5SNBVV.localdomain>
2026-03-08 16:22:12 +01:00

106 lines
2.3 KiB
TypeScript

import { deepEqual } from 'fast-equals';
import { JSONFile } from 'lowdb/node';
import { publicFiles } from '../../setup/index.js';
import { isRestorePoint } from './restore.parser.js';
import type { RestorePoint } from './restore.type.js';
let failedCreateAttempts = 0;
let savedState: RestorePoint | null = null;
let fileRef: JSONFile<RestorePoint | null> | null = null;
/**
* Service manages saving snapshot of application state
* that can then be restored when reopening
*/
export const restoreService = {
save,
load,
clear,
};
/**
* Saves a restore point
* @param [writeFn=write] - allows overriding the write function for testing
* @public
*/
async function save(data: RestorePoint, writeFn = write) {
// after three failed attempts, mark the service as unavailable
if (failedCreateAttempts > 3) {
return;
}
if (deepEqual(data, savedState)) {
return;
}
try {
await writeFn(data);
savedState = { ...data };
failedCreateAttempts = 0;
} catch (_error) {
failedCreateAttempts += 1;
}
}
/**
* Attempts reading a restore point from a given file path
* Returns null if none found, restore point otherwise
* @param [readFn=read] - allows overriding the read function for testing
* @public
*/
async function load(readFn = read): Promise<RestorePoint | null> {
try {
const maybeRestorePoint = await readFn();
if (isRestorePoint(maybeRestorePoint)) {
return maybeRestorePoint;
}
} catch (_error) {
// no need to notify the user
}
return null;
}
/**
* Clears the restore file
* @param [writeFn=write] - allows overriding the write function for testing
* @public
*/
async function clear(writeFn = write) {
try {
await writeFn(null);
} catch (_error) {
// nothing to do
}
}
/**
* Initialised file reference
* @private
*/
async function init(): Promise<JSONFile<RestorePoint | null>> {
if (fileRef) return fileRef;
fileRef = new JSONFile<RestorePoint | null>(publicFiles.restoreFile);
return fileRef;
}
/**
* Reads from an intialized file reference
* @private
*/
async function read(): Promise<RestorePoint | null> {
const file = await init();
return file.read();
}
/**
* Writes to an intialized file reference
* @throws - if writing fails
* @private
*/
async function write(data: RestorePoint | null) {
const file = await init();
return file.write(data);
}