mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
feat(backups): implement normalizeS3Path utility and integrate into backup processes
- Added normalizeS3Path function to standardize S3 path formatting by trimming whitespace and removing leading/trailing slashes. - Updated backup-related modules (MySQL, MongoDB, PostgreSQL, MariaDB, and web server) to utilize normalizeS3Path for consistent S3 path handling. - Introduced unit tests for normalizeS3Path to ensure correct functionality across various input scenarios.
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import path from "node:path";
|
||||
import type { BackupSchedule } from "@dokploy/server/services/backup";
|
||||
import type { Mariadb } from "@dokploy/server/services/mariadb";
|
||||
import { findProjectById } from "@dokploy/server/services/project";
|
||||
@@ -8,7 +7,7 @@ import {
|
||||
} from "../docker/utils";
|
||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
import { getS3Credentials } from "./utils";
|
||||
import { getS3Credentials, normalizeS3Path } from "./utils";
|
||||
|
||||
export const runMariadbBackup = async (
|
||||
mariadb: Mariadb,
|
||||
@@ -19,7 +18,7 @@ export const runMariadbBackup = async (
|
||||
const { prefix, database } = backup;
|
||||
const destination = backup.destination;
|
||||
const backupFileName = `${new Date().toISOString()}.sql.gz`;
|
||||
const bucketDestination = path.join(prefix, backupFileName);
|
||||
const bucketDestination = `${normalizeS3Path(prefix)}${backupFileName}`;
|
||||
|
||||
try {
|
||||
const rcloneFlags = getS3Credentials(destination);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import path from "node:path";
|
||||
import type { BackupSchedule } from "@dokploy/server/services/backup";
|
||||
import type { Mongo } from "@dokploy/server/services/mongo";
|
||||
import { findProjectById } from "@dokploy/server/services/project";
|
||||
@@ -8,7 +7,7 @@ import {
|
||||
} from "../docker/utils";
|
||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
import { getS3Credentials } from "./utils";
|
||||
import { getS3Credentials, normalizeS3Path } from "./utils";
|
||||
|
||||
// mongodb://mongo:Bqh7AQl-PRbnBu@localhost:27017/?tls=false&directConnection=true
|
||||
export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
|
||||
@@ -17,7 +16,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
|
||||
const { prefix, database } = backup;
|
||||
const destination = backup.destination;
|
||||
const backupFileName = `${new Date().toISOString()}.dump.gz`;
|
||||
const bucketDestination = path.join(prefix, backupFileName);
|
||||
const bucketDestination = `${normalizeS3Path(prefix)}${backupFileName}`;
|
||||
|
||||
try {
|
||||
const rcloneFlags = getS3Credentials(destination);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import path from "node:path";
|
||||
import type { BackupSchedule } from "@dokploy/server/services/backup";
|
||||
import type { MySql } from "@dokploy/server/services/mysql";
|
||||
import { findProjectById } from "@dokploy/server/services/project";
|
||||
@@ -8,7 +7,7 @@ import {
|
||||
} from "../docker/utils";
|
||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
import { getS3Credentials } from "./utils";
|
||||
import { getS3Credentials, normalizeS3Path } from "./utils";
|
||||
|
||||
export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
|
||||
const { appName, databaseRootPassword, projectId, name } = mysql;
|
||||
@@ -16,7 +15,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
|
||||
const { prefix, database } = backup;
|
||||
const destination = backup.destination;
|
||||
const backupFileName = `${new Date().toISOString()}.sql.gz`;
|
||||
const bucketDestination = path.join(prefix, backupFileName);
|
||||
const bucketDestination = `${normalizeS3Path(prefix)}${backupFileName}`;
|
||||
|
||||
try {
|
||||
const rcloneFlags = getS3Credentials(destination);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import path from "node:path";
|
||||
import type { BackupSchedule } from "@dokploy/server/services/backup";
|
||||
import type { Postgres } from "@dokploy/server/services/postgres";
|
||||
import { findProjectById } from "@dokploy/server/services/project";
|
||||
@@ -8,7 +7,7 @@ import {
|
||||
} from "../docker/utils";
|
||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
import { getS3Credentials } from "./utils";
|
||||
import { getS3Credentials, normalizeS3Path } from "./utils";
|
||||
|
||||
export const runPostgresBackup = async (
|
||||
postgres: Postgres,
|
||||
@@ -20,7 +19,7 @@ export const runPostgresBackup = async (
|
||||
const { prefix, database } = backup;
|
||||
const destination = backup.destination;
|
||||
const backupFileName = `${new Date().toISOString()}.sql.gz`;
|
||||
const bucketDestination = path.join(prefix, backupFileName);
|
||||
const bucketDestination = `${normalizeS3Path(prefix)}${backupFileName}`;
|
||||
try {
|
||||
const rcloneFlags = getS3Credentials(destination);
|
||||
const rcloneDestination = `:s3:${destination.bucket}/${bucketDestination}`;
|
||||
|
||||
@@ -36,6 +36,13 @@ export const removeScheduleBackup = (backupId: string) => {
|
||||
currentJob?.cancel();
|
||||
};
|
||||
|
||||
export const normalizeS3Path = (prefix: string) => {
|
||||
// Trim whitespace and remove leading/trailing slashes
|
||||
const normalizedPrefix = prefix.trim().replace(/^\/+|\/+$/g, "");
|
||||
// Return empty string if prefix is empty, otherwise append trailing slash
|
||||
return normalizedPrefix ? `${normalizedPrefix}/` : "";
|
||||
};
|
||||
|
||||
export const getS3Credentials = (destination: Destination) => {
|
||||
const { accessKey, secretAccessKey, region, endpoint, provider } =
|
||||
destination;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { BackupSchedule } from "@dokploy/server/services/backup";
|
||||
import { execAsync } from "../process/execAsync";
|
||||
import { getS3Credentials } from "./utils";
|
||||
import { getS3Credentials, normalizeS3Path } from "./utils";
|
||||
import { findDestinationById } from "@dokploy/server/services/destination";
|
||||
import { IS_CLOUD, paths } from "@dokploy/server/constants";
|
||||
import { mkdtemp } from "node:fs/promises";
|
||||
@@ -18,7 +18,7 @@ export const runWebServerBackup = async (backup: BackupSchedule) => {
|
||||
const { BASE_PATH } = paths();
|
||||
const tempDir = await mkdtemp(join(tmpdir(), "dokploy-backup-"));
|
||||
const backupFileName = `webserver-backup-${timestamp}.zip`;
|
||||
const s3Path = `:s3:${destination.bucket}/${backup.prefix}${backupFileName}`;
|
||||
const s3Path = `:s3:${destination.bucket}/${normalizeS3Path(backup.prefix)}${backupFileName}`;
|
||||
|
||||
try {
|
||||
await execAsync(`mkdir -p ${tempDir}/filesystem`);
|
||||
|
||||
Reference in New Issue
Block a user