From 107cdcee49e9d9907025c0c8f82f62ef585d61d9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 2 Jul 2025 00:45:57 -0600 Subject: [PATCH] feat: extend volume backup functionality with scheduling and management - Added support for volume backup jobs in the scheduling and removal processes. - Enhanced job management to include volume backups in the job queue. - Updated schema to accommodate volume backup data structure. - Implemented initialization of volume backup jobs based on server status, improving backup management. --- apps/schedules/src/index.ts | 6 ++++ apps/schedules/src/queue.ts | 19 ++++++++++++- apps/schedules/src/schema.ts | 5 ++++ apps/schedules/src/utils.ts | 55 +++++++++++++++++++++++++++++++++++- packages/server/src/index.ts | 3 +- 5 files changed, 84 insertions(+), 4 deletions(-) diff --git a/apps/schedules/src/index.ts b/apps/schedules/src/index.ts index 7ab2b98c5..af8ad8ff9 100644 --- a/apps/schedules/src/index.ts +++ b/apps/schedules/src/index.ts @@ -61,6 +61,12 @@ app.post("/update-backup", zValidator("json", jobQueueSchema), async (c) => { type: "schedule", cronSchedule: job.pattern, }); + } else if (data.type === "volume-backup") { + result = await removeJob({ + volumeBackupId: data.volumeBackupId, + type: "volume-backup", + cronSchedule: job.pattern, + }); } logger.info({ result }, "Job removed"); } diff --git a/apps/schedules/src/queue.ts b/apps/schedules/src/queue.ts index 5a1efc058..ebc9fa32a 100644 --- a/apps/schedules/src/queue.ts +++ b/apps/schedules/src/queue.ts @@ -42,6 +42,12 @@ export const scheduleJob = (job: QueueJob) => { pattern: job.cronSchedule, }, }); + } else if (job.type === "volume-backup") { + jobQueue.add(job.volumeBackupId, job, { + repeat: { + pattern: job.cronSchedule, + }, + }); } }; @@ -67,6 +73,13 @@ export const removeJob = async (data: QueueJob) => { }); return result; } + if (data.type === "volume-backup") { + const { volumeBackupId, cronSchedule } = data; + const result = await jobQueue.removeRepeatable(volumeBackupId, { + pattern: cronSchedule, + }); + return result; + } return false; }; @@ -89,6 +102,10 @@ export const getJobRepeatable = async ( const job = repeatableJobs.find((j) => j.name === scheduleId); return job ? job : null; } - + if (data.type === "volume-backup") { + const { volumeBackupId } = data; + const job = repeatableJobs.find((j) => j.name === volumeBackupId); + return job ? job : null; + } return null; }; diff --git a/apps/schedules/src/schema.ts b/apps/schedules/src/schema.ts index 32b2536bc..ac01e50de 100644 --- a/apps/schedules/src/schema.ts +++ b/apps/schedules/src/schema.ts @@ -16,6 +16,11 @@ export const jobQueueSchema = z.discriminatedUnion("type", [ type: z.literal("schedule"), scheduleId: z.string(), }), + z.object({ + cronSchedule: z.string(), + type: z.literal("volume-backup"), + volumeBackupId: z.string(), + }), ]); export type QueueJob = z.infer; diff --git a/apps/schedules/src/utils.ts b/apps/schedules/src/utils.ts index e65775aee..4d255a2b7 100644 --- a/apps/schedules/src/utils.ts +++ b/apps/schedules/src/utils.ts @@ -5,6 +5,7 @@ import { findBackupById, findScheduleById, findServerById, + findVolumeBackupById, keepLatestNBackups, runCommand, runComposeBackup, @@ -12,9 +13,15 @@ import { runMongoBackup, runMySqlBackup, runPostgresBackup, + runVolumeBackup, } from "@dokploy/server"; import { db } from "@dokploy/server/dist/db"; -import { backups, schedules, server } from "@dokploy/server/dist/db/schema"; +import { + backups, + schedules, + server, + volumeBackups, +} from "@dokploy/server/dist/db/schema"; import { and, eq } from "drizzle-orm"; import { logger } from "./logger.js"; import { scheduleJob } from "./queue.js"; @@ -93,6 +100,12 @@ export const runJobs = async (job: QueueJob) => { if (schedule.enabled) { await runCommand(schedule.scheduleId); } + } else if (job.type === "volume-backup") { + const { volumeBackupId } = job; + const volumeBackup = await findVolumeBackupById(volumeBackupId); + if (volumeBackup.enabled) { + await runVolumeBackup(volumeBackupId); + } } } catch (error) { logger.error(error); @@ -184,4 +197,44 @@ export const initializeJobs = async () => { { Quantity: filteredSchedulesBasedOnServerStatus.length }, "Schedules Initialized", ); + + const volumeBackupsResult = await db.query.volumeBackups.findMany({ + where: eq(volumeBackups.enabled, true), + with: { + application: { + with: { + server: true, + }, + }, + compose: { + with: { + server: true, + }, + }, + }, + }); + + const filteredVolumeBackupsBasedOnServerStatus = volumeBackupsResult.filter( + (volumeBackup) => { + if (volumeBackup.application) { + return volumeBackup.application.server?.serverStatus === "active"; + } + if (volumeBackup.compose) { + return volumeBackup.compose.server?.serverStatus === "active"; + } + }, + ); + + for (const volumeBackup of filteredVolumeBackupsBasedOnServerStatus) { + scheduleJob({ + volumeBackupId: volumeBackup.volumeBackupId, + type: "volume-backup", + cronSchedule: volumeBackup.cronExpression, + }); + } + + logger.info( + { Quantity: filteredVolumeBackupsBasedOnServerStatus.length }, + "Volume Backups Initialized", + ); }; diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 73586004d..1c0655f8b 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -63,8 +63,6 @@ export * from "./utils/notifications/utils"; export * from "./utils/notifications/docker-cleanup"; export * from "./utils/notifications/server-threshold"; -export * from "./utils/volume-backups/index"; - export * from "./utils/builders/index"; export * from "./utils/builders/compose"; export * from "./utils/builders/docker-file"; @@ -135,5 +133,6 @@ export { export * from "./utils/schedules/utils"; export * from "./utils/schedules/index"; +export * from "./utils/volume-backups/index"; export * from "./lib/logger";