mirror of
https://github.com/andwati/dokploy.git
synced 2026-10-27 23:42:15 +00:00
Enhance backup command generation and logging for database backups
- Refactored backup utilities for PostgreSQL, MySQL, MariaDB, and MongoDB to streamline command generation and improve logging. - Introduced a unified `getBackupCommand` function to encapsulate backup command logic, enhancing maintainability. - Improved error handling and logging during backup processes, providing clearer feedback on command execution and success/failure states. - Updated container retrieval methods to return null instead of throwing errors when no containers are found, improving robustness.
This commit is contained in:
@@ -77,35 +77,179 @@ export const getS3Credentials = (destination: Destination) => {
|
||||
};
|
||||
|
||||
export const getPostgresBackupCommand = (
|
||||
containerId: string,
|
||||
database: string,
|
||||
databaseUser: string,
|
||||
) => {
|
||||
return `docker exec ${containerId} sh -c "pg_dump -Fc --no-acl --no-owner -h localhost -U ${databaseUser} --no-password '${database}' | gzip"`;
|
||||
return `docker exec -i $CONTAINER_ID bash -c "set -o pipefail; pg_dump -Fc --no-acl --no-owner -h localhost -U ${databaseUser} --no-password '${database}' | gzip"`;
|
||||
};
|
||||
|
||||
export const getMariadbBackupCommand = (
|
||||
containerId: string,
|
||||
database: string,
|
||||
databaseUser: string,
|
||||
databasePassword: string,
|
||||
) => {
|
||||
return `docker exec ${containerId} sh -c "mariadb-dump --user='${databaseUser}' --password='${databasePassword}' --databases ${database} | gzip"`;
|
||||
return `docker exec -i $CONTAINER_ID bash -c "set -o pipefail; mariadb-dump --user='${databaseUser}' --password='${databasePassword}' --databases ${database} | gzip"`;
|
||||
};
|
||||
|
||||
export const getMysqlBackupCommand = (
|
||||
containerId: string,
|
||||
database: string,
|
||||
databasePassword: string,
|
||||
) => {
|
||||
return `docker exec ${containerId} sh -c "mysqldump --default-character-set=utf8mb4 -u 'root' --password='${databasePassword}' --single-transaction --no-tablespaces --quick '${database}' | gzip"`;
|
||||
return `docker exec -i $CONTAINER_ID bash -c "set -o pipefail; mysqldump --default-character-set=utf8mb4 -u 'root' --password='${databasePassword}' --single-transaction --no-tablespaces --quick '${database}' | gzip"`;
|
||||
};
|
||||
|
||||
export const getMongoBackupCommand = (
|
||||
containerId: string,
|
||||
database: string,
|
||||
databaseUser: string,
|
||||
databasePassword: string,
|
||||
) => {
|
||||
return `docker exec ${containerId} sh -c "mongodump -d '${database}' -u '${databaseUser}' -p '${databasePassword}' --archive --authenticationDatabase admin --gzip"`;
|
||||
return `docker exec -i $CONTAINER_ID bash -c "set -o pipefail; mongodump -d '${database}' -u '${databaseUser}' -p '${databasePassword}' --archive --authenticationDatabase admin --gzip"`;
|
||||
};
|
||||
|
||||
const getServiceContainerCommand = (appName: string) => {
|
||||
return `docker ps -q --filter "status=running" --filter "label=com.docker.swarm.service.name=${appName}" | head -n 1`;
|
||||
};
|
||||
|
||||
const getComposeContainerCommand = (
|
||||
appName: string,
|
||||
serviceName: string,
|
||||
composeType: "stack" | "docker-compose" | undefined,
|
||||
) => {
|
||||
if (composeType === "stack") {
|
||||
return `docker ps -q --filter "status=running" --filter "label=com.docker.stack.namespace=${appName}" --filter "label=com.docker.swarm.service.name=${serviceName}" | head -n 1`;
|
||||
}
|
||||
return `docker ps -q --filter "status=running" --filter "label=com.docker.compose.project=${appName}" --filter "label=com.docker.compose.service=${serviceName}" | head -n 1`;
|
||||
};
|
||||
|
||||
const getContainerSearchCommand = (backup: BackupSchedule) => {
|
||||
const { backupType, postgres, mysql, mariadb, mongo, compose, serviceName } =
|
||||
backup;
|
||||
|
||||
if (backupType === "database") {
|
||||
const appName =
|
||||
postgres?.appName || mysql?.appName || mariadb?.appName || mongo?.appName;
|
||||
return getServiceContainerCommand(appName || "");
|
||||
}
|
||||
if (backupType === "compose") {
|
||||
const { appName, composeType } = compose || {};
|
||||
return getComposeContainerCommand(
|
||||
appName || "",
|
||||
serviceName || "",
|
||||
composeType,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const generateBackupCommand = (backup: BackupSchedule) => {
|
||||
const { backupType, databaseType } = backup;
|
||||
switch (databaseType) {
|
||||
case "postgres": {
|
||||
const postgres = backup.postgres;
|
||||
if (backupType === "database" && postgres) {
|
||||
return getPostgresBackupCommand(backup.database, postgres.databaseUser);
|
||||
}
|
||||
if (backupType === "compose" && backup.metadata?.postgres) {
|
||||
return getPostgresBackupCommand(
|
||||
backup.database,
|
||||
backup.metadata.postgres.databaseUser,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "mysql": {
|
||||
const mysql = backup.mysql;
|
||||
if (backupType === "database" && mysql) {
|
||||
return getMysqlBackupCommand(backup.database, mysql.databasePassword);
|
||||
}
|
||||
if (backupType === "compose" && backup.metadata?.mysql) {
|
||||
return getMysqlBackupCommand(
|
||||
backup.database,
|
||||
backup.metadata?.mysql?.databaseRootPassword || "",
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "mariadb": {
|
||||
const mariadb = backup.mariadb;
|
||||
if (backupType === "database" && mariadb) {
|
||||
return getMariadbBackupCommand(
|
||||
backup.database,
|
||||
mariadb.databaseUser,
|
||||
mariadb.databasePassword,
|
||||
);
|
||||
}
|
||||
if (backupType === "compose" && backup.metadata?.mariadb) {
|
||||
return getMariadbBackupCommand(
|
||||
backup.database,
|
||||
backup.metadata.mariadb.databaseUser,
|
||||
backup.metadata.mariadb.databasePassword,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "mongo": {
|
||||
const mongo = backup.mongo;
|
||||
if (backupType === "database" && mongo) {
|
||||
return getMongoBackupCommand(
|
||||
backup.database,
|
||||
mongo.databaseUser,
|
||||
mongo.databasePassword,
|
||||
);
|
||||
}
|
||||
if (backupType === "compose" && backup.metadata?.mongo) {
|
||||
return getMongoBackupCommand(
|
||||
backup.database,
|
||||
backup.metadata.mongo.databaseUser,
|
||||
backup.metadata.mongo.databasePassword,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Database type not supported: ${databaseType}`);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export const getBackupCommand = (
|
||||
backup: BackupSchedule,
|
||||
rcloneCommand: string,
|
||||
logPath: string,
|
||||
) => {
|
||||
const containerSearch = getContainerSearchCommand(backup);
|
||||
const backupCommand = generateBackupCommand(backup);
|
||||
return `
|
||||
set -eo pipefail;
|
||||
echo "[$(date)] Starting backup process..." >> ${logPath};
|
||||
echo "[$(date)] Executing backup command..." >> ${logPath};
|
||||
CONTAINER_ID=$(${containerSearch})
|
||||
|
||||
if [ -z "$CONTAINER_ID" ]; then
|
||||
echo "[$(date)] ❌ Container not found" >> ${logPath};
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
echo "[$(date)] Container Up: $CONTAINER_ID" >> ${logPath};
|
||||
|
||||
# Run the backup command and capture the exit status
|
||||
BACKUP_OUTPUT=$(${backupCommand} 2>&1 >/dev/null) || {
|
||||
echo "[$(date)] ❌ backup failed" >> ${logPath};
|
||||
echo "Error: $BACKUP_OUTPUT" >> ${logPath};
|
||||
exit 1;
|
||||
}
|
||||
|
||||
echo "[$(date)] ✅ backup completed successfully" >> ${logPath};
|
||||
echo "[$(date)] Starting upload to S3..." >> ${logPath};
|
||||
|
||||
# Run the upload command and capture the exit status
|
||||
UPLOAD_OUTPUT=$(${backupCommand} | ${rcloneCommand} 2>&1 >/dev/null) || {
|
||||
echo "[$(date)] ❌ Upload to S3 failed" >> ${logPath};
|
||||
echo "Error: $UPLOAD_OUTPUT" >> ${logPath};
|
||||
exit 1;
|
||||
}
|
||||
|
||||
echo "[$(date)] ✅ Upload to S3 completed successfully" >> ${logPath};
|
||||
echo "Backup done ✅" >> ${logPath};
|
||||
`;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user