startDataBackupRun already holds the backup run lock, but the archive (createDataBackup) and restoreDataBackup took it again, so every run failed a few seconds in with "Another backup or restore is already running". The writer and the restore now take no lock of their own. The run's notice (archiving, restoring, or why it failed) moves to its own line under the actions row so the buttons don't shift, and archiving reads in gray rather than the warning colour. Co-Authored-By: Claude Opus 5.5 <[email protected]>
70 lines
2.3 KiB
TypeScript
70 lines
2.3 KiB
TypeScript
import grabSuperAdminUser from "@/src/functions/backend/backups/grab-super-admin-user";
|
|
import writeDataBackupArchive from "@/src/functions/backend/backups/write-data-backup-archive";
|
|
import { startDataBackupRun } from "@/src/functions/backend/backups/data-backup-run";
|
|
import logEvent, { grabRequestIP, logError } from "@/src/functions/backend/logger";
|
|
import safeErrorMessage from "@/src/utils/safe-error-message";
|
|
import type { DataBackupRun } from "@/src/types";
|
|
import type {
|
|
APIResponseObject,
|
|
BunextAPIRouteHandler,
|
|
} from "@moduletrace/bunext/types";
|
|
|
|
/**
|
|
* Starts archiving the whole `DATA_DIR` (Settings > Backups > Data
|
|
* archives) and answers at once: the page polls `data-status` until the
|
|
* archive is written. `startDataBackupRun` holds the backup run lock, so
|
|
* the task calls the unlocked writer.
|
|
*/
|
|
export const handler: BunextAPIRouteHandler<
|
|
APIResponseObject<DataBackupRun>
|
|
> = async ({ req }) => {
|
|
const user = await grabSuperAdminUser({ req });
|
|
|
|
if (!user?.id) {
|
|
return {
|
|
success: false,
|
|
msg: `Unauthorized`,
|
|
};
|
|
}
|
|
|
|
const ip_address = grabRequestIP(req);
|
|
|
|
try {
|
|
const run = startDataBackupRun({
|
|
kind: "archive",
|
|
task: async () => {
|
|
try {
|
|
const backup = await writeDataBackupArchive();
|
|
|
|
await logEvent({
|
|
title: "Data backup created",
|
|
category: "system",
|
|
user_id: user.id,
|
|
plain_text: `User #${user.id} created a data backup (${backup.name}).`,
|
|
metadata: { backup_name: backup.name },
|
|
ip_address,
|
|
});
|
|
|
|
return backup.name;
|
|
} catch (error: any) {
|
|
await logError({
|
|
title: "Data backup failed",
|
|
error,
|
|
user_id: user.id,
|
|
ip_address,
|
|
});
|
|
|
|
throw error;
|
|
}
|
|
},
|
|
});
|
|
|
|
return {
|
|
success: true,
|
|
singleRes: run,
|
|
};
|
|
} catch (error: unknown) {
|
|
return { success: false, msg: safeErrorMessage({ error }) };
|
|
}
|
|
};
|