This commit is contained in:
seb
2026-10-14 03:15:43 +02:00
parent 61076a9ef2
commit e7b3044903
3 changed files with 395 additions and 173 deletions

View File

@@ -1,12 +1,12 @@
/**
* @file backupAutomat.js
* @description Manages backup status and backup archives (`GET /backup`, `POST /backup/run`).
* @description Manages backup status, archive listings, and VFS file recovery from backup archives (`GET /backup`, `GET /backup/:id/vfs`, `POST /backup/:id/vfs/recover`).
*
* Upstream Triggers:
* - `authAutomat`: Subscribes to auth state changes — calls `actions.load()` on
* `'authed'` with a `selectedCompanyId`, and `actions.clear()` on `'anon'`.
* - `sseAutomat`: Subscribes to `'backup'` event (if dispatched) to reload silently.
* - UI / Settings: Calls to `actions.load()`, `actions.reload()`, or `actions.runBackup()`.
* - UI / Settings: Calls to `actions.load()`, `actions.reload()`, `actions.listBackupVfs()`, or `actions.recoverBackupVfs()`.
*/
import { Automat } from 'react-automat';
@@ -41,17 +41,16 @@ import sseAutomat from './sseAutomat';
* @typedef {Object} BackupState
* @property {'idle'|'loading'|'ready'|'failed'} status Load lifecycle state
* @property {string|null} error Last error message
* @property {boolean} runningBackup Whether a manual backup run is currently in progress
* @property {BackupStatus|null} backupStatus Service status and retention settings
* @property {{ counts: { hourly: number, daily: number, monthly: number }, hourly: BackupItem[], daily: BackupItem[], monthly: BackupItem[] }} backups Grouped backup archives
* @property {object|null} lastRunResult Manifest/result from the most recent manual backup run
* @property {Record<string, Array<{ name: string, size: number, mime: string, mtime: string }>>} vfsFilesByBackup
* @property {Record<string, boolean>} vfsLoadingByBackup
*/
/** @type {BackupState} */
const initialState = {
status: 'idle',
error: null,
runningBackup: false,
backupStatus: null,
backups: {
counts: { hourly: 0, daily: 0, monthly: 0 },
@@ -59,7 +58,8 @@ const initialState = {
daily: [],
monthly: [],
},
lastRunResult: null,
vfsFilesByBackup: {},
vfsLoadingByBackup: {},
};
let backupAutomat;
@@ -141,30 +141,50 @@ const actions = {
},
// #endregion
// #region runBackup
// MARK: calls POST /api/backup/run from ./api/index.js
// #region listBackupVfs
// MARK: calls GET /api/backup/:id/vfs from ./api/index.js
/**
* Triggers an immediate manual backup run on the backend.
* Retrieves the list of VFS files stored inside a specific backup.
*
* @returns {Promise<object>} The backup run result containing manifest and pruneResult
* @param {string} backupId Identifier of the backup
* @param {number} [companyId] Optional company ID override
* @returns {Promise<Array<{ name: string, size: number, mime: string, mtime: string }>>}
*/
async runBackup() {
backupAutomat.setState({ runningBackup: true, error: null });
async listBackupVfs(backupId, companyId) {
if (!backupId) return [];
backupAutomat.setState({
vfsLoadingByBackup: { ...backupAutomat.state.vfsLoadingByBackup, [backupId]: true },
});
try {
const result = await backupApi.run();
backupAutomat.setState({ lastRunResult: result });
await actions.reload();
return result;
} catch (err) {
const error = err?.message ?? String(err);
backupAutomat.setState({ error });
throw err;
const res = await backupApi.listVfs(backupId, companyId);
const files = Array.isArray(res?.files) ? res.files : [];
backupAutomat.setState({
vfsFilesByBackup: { ...backupAutomat.state.vfsFilesByBackup, [backupId]: files },
});
return files;
} finally {
backupAutomat.setState({ runningBackup: false });
backupAutomat.setState({
vfsLoadingByBackup: { ...backupAutomat.state.vfsLoadingByBackup, [backupId]: false },
});
}
},
// #endregion
// #region recoverBackupVfs
// MARK: calls POST /api/backup/:id/vfs/recover from ./api/index.js
/**
* Restores a single VFS file from the specified backup into the active VFS.
*
* @param {string} backupId Identifier of the backup
* @param {string} path Relative file path within the VFS
* @param {number} [companyId] Optional company ID override
* @returns {Promise<object>} Recovery result
*/
async recoverBackupVfs(backupId, path, companyId) {
return backupApi.recoverVfs(backupId, path, companyId);
},
// #endregion
/**
* Resets the automat state to initial empty values.
*/