From 8cdd06ec49a40cb8672cb4ef07f028aa41607085 Mon Sep 17 00:00:00 2001
From: Nico <47644445+nicotsx@users.noreply.github.com>
Date: Sun, 21 Dec 2025 14:25:45 +0100
Subject: [PATCH] fix(stop): always update status to warning when stop is
executed (#202)
* fix(stop): always update status to warning when stop is executed
* fix: automatically put in_progress backups to warning during startup
---
.../backups/components/schedule-summary.tsx | 3 +-
app/server/modules/backups/backups.service.ts | 34 ++++++++++---------
app/server/modules/lifecycle/startup.ts | 14 +++++++-
3 files changed, 33 insertions(+), 18 deletions(-)
diff --git a/app/client/modules/backups/components/schedule-summary.tsx b/app/client/modules/backups/components/schedule-summary.tsx
index 8bdd3b9d..87efda27 100644
--- a/app/client/modules/backups/components/schedule-summary.tsx
+++ b/app/client/modules/backups/components/schedule-summary.tsx
@@ -181,7 +181,8 @@ export const ScheduleSummary = (props: Props) => {
Warning Details
- Last backup completed with warnings. Check your container logs for more details.
+ {schedule.lastBackupError ??
+ "Last backup completed with warnings. Check your container logs for more details."}
)}
diff --git a/app/server/modules/backups/backups.service.ts b/app/server/modules/backups/backups.service.ts
index f8dc2837..120dc7cc 100644
--- a/app/server/modules/backups/backups.service.ts
+++ b/app/server/modules/backups/backups.service.ts
@@ -431,23 +431,25 @@ const stopBackup = async (scheduleId: number) => {
throw new NotFoundError("Backup schedule not found");
}
- const abortController = runningBackups.get(scheduleId);
- if (!abortController) {
- throw new ConflictError("No backup is currently running for this schedule");
+ try {
+ const abortController = runningBackups.get(scheduleId);
+ if (!abortController) {
+ throw new ConflictError("No backup is currently running for this schedule");
+ }
+
+ logger.info(`Stopping backup for schedule ${scheduleId}`);
+
+ abortController.abort();
+ } finally {
+ await db
+ .update(backupSchedulesTable)
+ .set({
+ lastBackupStatus: "warning",
+ lastBackupError: "Backup was stopped by user",
+ updatedAt: Date.now(),
+ })
+ .where(eq(backupSchedulesTable.id, scheduleId));
}
-
- logger.info(`Stopping backup for schedule ${scheduleId}`);
-
- abortController.abort();
-
- await db
- .update(backupSchedulesTable)
- .set({
- lastBackupStatus: "warning",
- lastBackupError: "Backup was stopped by user",
- updatedAt: Date.now(),
- })
- .where(eq(backupSchedulesTable.id, scheduleId));
};
const runForget = async (scheduleId: number, repositoryId?: string) => {
diff --git a/app/server/modules/lifecycle/startup.ts b/app/server/modules/lifecycle/startup.ts
index 2761bf88..1bf3265a 100644
--- a/app/server/modules/lifecycle/startup.ts
+++ b/app/server/modules/lifecycle/startup.ts
@@ -1,7 +1,7 @@
import { Scheduler } from "../../core/scheduler";
import { and, eq, or } from "drizzle-orm";
import { db } from "../../db/db";
-import { volumesTable } from "../../db/schema";
+import { backupSchedulesTable, volumesTable } from "../../db/schema";
import { logger } from "../../utils/logger";
import { restic } from "../../utils/restic";
import { volumeService } from "../volumes/volume.service";
@@ -63,6 +63,18 @@ export const startup = async () => {
});
}
+ await db
+ .update(backupSchedulesTable)
+ .set({
+ lastBackupStatus: "warning",
+ lastBackupError: "Zerobyte was restarted during the last scheduled backup",
+ updatedAt: Date.now(),
+ })
+ .where(eq(backupSchedulesTable.lastBackupStatus, "in_progress"))
+ .catch((err) => {
+ logger.error(`Failed to update stuck backup schedules on startup: ${err.message}`);
+ });
+
Scheduler.build(CleanupDanglingMountsJob).schedule("0 * * * *");
Scheduler.build(VolumeHealthCheckJob).schedule("*/30 * * * *");
Scheduler.build(RepositoryHealthCheckJob).schedule("50 12 * * *");