From 0690f0726230031d5c88dbda510202f6b2ef92f5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 3 May 2025 12:59:22 -0600 Subject: [PATCH] Enhance backup validation and improve backup display - Added validation to require a service name for compose backups in the backup handling logic, improving user feedback. - Refactored the ShowBackups component to sort deployments by creation date and enhance the display of backup information, including service names and statuses, for better user experience. - Updated logging in the compose backup utility to include command execution details, aiding in debugging and monitoring. --- .../database/backups/handle-backup.tsx | 9 + .../database/backups/show-backups.tsx | 350 +++++++++--------- packages/server/src/utils/backups/compose.ts | 4 +- 3 files changed, 196 insertions(+), 167 deletions(-) diff --git a/apps/dokploy/components/dashboard/database/backups/handle-backup.tsx b/apps/dokploy/components/dashboard/database/backups/handle-backup.tsx index 032616b1..de5f2d3a 100644 --- a/apps/dokploy/components/dashboard/database/backups/handle-backup.tsx +++ b/apps/dokploy/components/dashboard/database/backups/handle-backup.tsx @@ -108,6 +108,15 @@ const Schema = z path: ["databaseType"], }); } + + if (data.backupType === "compose" && !data.serviceName) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Service name is required for compose backups", + path: ["serviceName"], + }); + } + if (data.backupType === "compose" && data.databaseType) { if (data.databaseType === "postgres") { if (!data.metadata?.postgres?.databaseUser) { diff --git a/apps/dokploy/components/dashboard/database/backups/show-backups.tsx b/apps/dokploy/components/dashboard/database/backups/show-backups.tsx index e71bc0bb..81a2a299 100644 --- a/apps/dokploy/components/dashboard/database/backups/show-backups.tsx +++ b/apps/dokploy/components/dashboard/database/backups/show-backups.tsx @@ -178,184 +178,202 @@ export const ShowBackups = ({ )}
- {postgres?.backups.map((backup) => ( -
-
-
-
- {backup.backupType === "compose" && ( -
- {backup.databaseType === "postgres" && ( - - )} - {backup.databaseType === "mysql" && ( - - )} - {backup.databaseType === "mariadb" && ( - - )} - {backup.databaseType === "mongo" && ( - - )} -
- )} -
+ {postgres?.backups.map((backup) => { + const orderedDeployments = backup.deployments.sort( + (a, b) => + new Date(b.createdAt).getTime() - + new Date(a.createdAt).getTime(), + ); + + const serverId = + "serverId" in postgres ? postgres.serverId : undefined; + + return ( +
+
+
+
{backup.backupType === "compose" && ( -
-

- {backup.serviceName} -

- - {backup.databaseType} - +
+ {backup.databaseType === "postgres" && ( + + )} + {backup.databaseType === "mysql" && ( + + )} + {backup.databaseType === "mariadb" && ( + + )} + {backup.databaseType === "mongo" && ( + + )}
)} -
-
- - {backup.enabled ? "Active" : "Inactive"} +
+ {backup.backupType === "compose" && ( +
+

+ {backup.serviceName} +

+ + {backup.databaseType} + +
+ )} +
+
+ + {backup.enabled ? "Active" : "Inactive"} + +
+
+
+ +
+
+ + Destination +

+ {backup.destination.name} +

+
+ +
+ + Database + +

+ {backup.database} +

+
+ +
+ + Schedule + +

+ {backup.schedule} +

+
+ +
+ + Prefix Storage + +

+ {backup.prefix} +

+
+ +
+ + Keep Latest + +

+ {backup.keepLatestCount || "All"} +

-
-
- - Destination - -

- {backup.destination.name} -

-
+
+ + + + + + + + + + Run Manual Backup + + + -
- - Database - -

- {backup.database} -

-
- -
- - Schedule - -

- {backup.schedule} -

-
- -
- - Prefix Storage - -

- {backup.prefix} -

-
- -
- - Keep Latest - -

- {backup.keepLatestCount || "All"} -

-
+ + { + await deleteBackup({ + backupId: backup.backupId, + }) + .then(() => { + refetch(); + toast.success( + "Backup deleted successfully", + ); + }) + .catch(() => { + toast.error("Error deleting backup"); + }); + }} + > + +
- -
- - - - - - - - - Run Manual Backup - - - - - { - await deleteBackup({ - backupId: backup.backupId, - }) - .then(() => { - refetch(); - toast.success("Backup deleted successfully"); - }) - .catch(() => { - toast.error("Error deleting backup"); - }); - }} - > - - -
-
- ))} + ); + })}
)} diff --git a/packages/server/src/utils/backups/compose.ts b/packages/server/src/utils/backups/compose.ts index 6131f2a4..76fd43a6 100644 --- a/packages/server/src/utils/backups/compose.ts +++ b/packages/server/src/utils/backups/compose.ts @@ -33,6 +33,7 @@ export const runComposeBackup = async ( title: "Compose Backup", description: "Compose Backup", }); + try { const rcloneFlags = getS3Credentials(destination); const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`; @@ -77,7 +78,8 @@ export const runComposeBackup = async ( compose.serverId, ` set -e; - Running command. + echo "Running command." >> ${deployment.logPath}; + export RCLONE_LOG_LEVEL=DEBUG; ${backupCommand} | ${rcloneCommand} >> ${deployment.logPath} 2>> ${deployment.logPath} || { echo "❌ Command failed" >> ${deployment.logPath}; exit 1;