mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-15 20:25:23 +02:00
feat: enhance volume backup scheduling and management
- Added initVolumeBackupsCronJobs function to initialize scheduled volume backups on server startup. - Updated volumeBackupsRouter to handle scheduling and removal of volume backup jobs based on user input. - Improved create and update volume backup logic to include scheduling functionality for both cloud and local environments. - Introduced utility functions for scheduling and removing volume backup jobs, enhancing overall backup management.
This commit is contained in:
@@ -6,6 +6,8 @@ import {
|
||||
runVolumeBackup,
|
||||
findVolumeBackupById,
|
||||
restoreVolume,
|
||||
scheduleVolumeBackup,
|
||||
removeVolumeBackupJob,
|
||||
} from "@dokploy/server";
|
||||
import {
|
||||
createVolumeBackupSchema,
|
||||
@@ -21,6 +23,8 @@ import {
|
||||
execAsyncRemote,
|
||||
execAsyncStream,
|
||||
} from "@dokploy/server/utils/process/execAsync";
|
||||
import { removeJob, schedule, updateJob } from "@/server/utils/backup";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
export const volumeBackupsRouter = createTRPCRouter({
|
||||
list: protectedProcedure
|
||||
@@ -55,7 +59,20 @@ export const volumeBackupsRouter = createTRPCRouter({
|
||||
create: protectedProcedure
|
||||
.input(createVolumeBackupSchema)
|
||||
.mutation(async ({ input }) => {
|
||||
return await createVolumeBackup(input);
|
||||
const newVolumeBackup = await createVolumeBackup(input);
|
||||
|
||||
if (newVolumeBackup?.enabled) {
|
||||
if (IS_CLOUD) {
|
||||
await schedule({
|
||||
cronSchedule: newVolumeBackup.cronExpression,
|
||||
volumeBackupId: newVolumeBackup.volumeBackupId,
|
||||
type: "volume-backup",
|
||||
});
|
||||
} else {
|
||||
await scheduleVolumeBackup(newVolumeBackup.volumeBackupId);
|
||||
}
|
||||
}
|
||||
return newVolumeBackup;
|
||||
}),
|
||||
one: protectedProcedure
|
||||
.input(
|
||||
@@ -73,15 +90,46 @@ export const volumeBackupsRouter = createTRPCRouter({
|
||||
}),
|
||||
)
|
||||
.mutation(async ({ input }) => {
|
||||
if (IS_CLOUD) {
|
||||
return true;
|
||||
}
|
||||
return await removeVolumeBackup(input.volumeBackupId);
|
||||
}),
|
||||
update: protectedProcedure
|
||||
.input(updateVolumeBackupSchema)
|
||||
.mutation(async ({ input }) => {
|
||||
return await updateVolumeBackup(input.volumeBackupId, input);
|
||||
const updatedVolumeBackup = await updateVolumeBackup(
|
||||
input.volumeBackupId,
|
||||
input,
|
||||
);
|
||||
|
||||
if (!updatedVolumeBackup) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Volume backup not found",
|
||||
});
|
||||
}
|
||||
|
||||
if (IS_CLOUD) {
|
||||
if (updatedVolumeBackup.enabled) {
|
||||
await updateJob({
|
||||
cronSchedule: updatedVolumeBackup.cronExpression,
|
||||
volumeBackupId: updatedVolumeBackup.volumeBackupId,
|
||||
type: "volume-backup",
|
||||
});
|
||||
} else {
|
||||
await removeJob({
|
||||
cronSchedule: updatedVolumeBackup.cronExpression,
|
||||
volumeBackupId: updatedVolumeBackup.volumeBackupId,
|
||||
type: "volume-backup",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (updatedVolumeBackup?.enabled) {
|
||||
removeVolumeBackupJob(updatedVolumeBackup.volumeBackupId);
|
||||
scheduleVolumeBackup(updatedVolumeBackup.volumeBackupId);
|
||||
} else {
|
||||
removeVolumeBackupJob(updatedVolumeBackup.volumeBackupId);
|
||||
}
|
||||
}
|
||||
return updatedVolumeBackup;
|
||||
}),
|
||||
|
||||
runManually: protectedProcedure
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
createDefaultTraefikConfig,
|
||||
initCronJobs,
|
||||
initSchedules,
|
||||
initVolumeBackupsCronJobs,
|
||||
initializeNetwork,
|
||||
sendDokployRestartNotifications,
|
||||
setupDirectories,
|
||||
@@ -51,6 +52,7 @@ void app.prepare().then(async () => {
|
||||
await migration();
|
||||
await initCronJobs();
|
||||
await initSchedules();
|
||||
await initVolumeBackupsCronJobs();
|
||||
await sendDokployRestartNotifications();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ type QueueJob =
|
||||
type: "schedule";
|
||||
cronSchedule: string;
|
||||
scheduleId: string;
|
||||
}
|
||||
| {
|
||||
type: "volume-backup";
|
||||
cronSchedule: string;
|
||||
volumeBackupId: string;
|
||||
};
|
||||
export const schedule = async (job: QueueJob) => {
|
||||
try {
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
import { db } from "../db";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import type { z } from "zod";
|
||||
import { scheduleBackup } from "../utils/backups/utils";
|
||||
|
||||
export const findVolumeBackupById = async (volumeBackupId: string) => {
|
||||
const volumeBackup = await db.query.volumeBackups.findFirst({
|
||||
@@ -43,14 +42,6 @@ export const createVolumeBackup = async (
|
||||
.returning()
|
||||
.then((e) => e[0]);
|
||||
|
||||
await schedule({
|
||||
cronSchedule: backup.schedule,
|
||||
backupId: backup.backupId,
|
||||
type: "backup",
|
||||
});
|
||||
|
||||
scheduleBackup(backup);
|
||||
|
||||
return newVolumeBackup;
|
||||
};
|
||||
|
||||
@@ -64,8 +55,10 @@ export const updateVolumeBackup = async (
|
||||
volumeBackupId: string,
|
||||
volumeBackup: z.infer<typeof updateVolumeBackupSchema>,
|
||||
) => {
|
||||
await db
|
||||
return await db
|
||||
.update(volumeBackups)
|
||||
.set(volumeBackup)
|
||||
.where(eq(volumeBackups.volumeBackupId, volumeBackupId));
|
||||
.where(eq(volumeBackups.volumeBackupId, volumeBackupId))
|
||||
.returning()
|
||||
.then((e) => e[0]);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,30 @@
|
||||
export * from "./backup";
|
||||
export * from "./restore";
|
||||
export * from "./utils";
|
||||
import { volumeBackups } from "@dokploy/server/db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { db } from "../../db/index";
|
||||
import { scheduleVolumeBackup } from "./utils";
|
||||
|
||||
export const initVolumeBackupsCronJobs = async () => {
|
||||
console.log("Setting up volume backups cron jobs....");
|
||||
try {
|
||||
const volumeBackupsResult = await db.query.volumeBackups.findMany({
|
||||
where: eq(volumeBackups.enabled, true),
|
||||
with: {
|
||||
application: true,
|
||||
compose: true,
|
||||
},
|
||||
});
|
||||
|
||||
console.log(`Initializing ${volumeBackupsResult.length} volume backups`);
|
||||
for (const volumeBackup of volumeBackupsResult) {
|
||||
scheduleVolumeBackup(volumeBackup.volumeBackupId);
|
||||
console.log(
|
||||
`Initialized volume backup: ${volumeBackup.name} ${volumeBackup.serviceType} ✅`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`Error initializing volume backups: ${error}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,6 +6,19 @@ import {
|
||||
updateDeploymentStatus,
|
||||
} from "../..";
|
||||
import { backupVolume } from "./backup";
|
||||
import { scheduleJob, scheduledJobs } from "node-schedule";
|
||||
|
||||
export const scheduleVolumeBackup = async (volumeBackupId: string) => {
|
||||
const volumeBackup = await findVolumeBackupById(volumeBackupId);
|
||||
scheduleJob(volumeBackupId, volumeBackup.cronExpression, async () => {
|
||||
await runVolumeBackup(volumeBackupId);
|
||||
});
|
||||
};
|
||||
|
||||
export const removeVolumeBackupJob = async (volumeBackupId: string) => {
|
||||
const currentJob = scheduledJobs[volumeBackupId];
|
||||
currentJob?.cancel();
|
||||
};
|
||||
|
||||
export const runVolumeBackup = async (volumeBackupId: string) => {
|
||||
const volumeBackup = await findVolumeBackupById(volumeBackupId);
|
||||
|
||||
Reference in New Issue
Block a user