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";