mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
- Updated the backup command in runWebServerBackup to suppress output by redirecting it to /dev/null. - Modified the restore command in restoreWebServerBackup to also suppress output during the extraction of backups.
144 lines
4.6 KiB
TypeScript
144 lines
4.6 KiB
TypeScript
import type { Destination } from "@dokploy/server/services/destination";
|
|
import { getS3Credentials } from "../backups/utils";
|
|
import { execAsync } from "../process/execAsync";
|
|
import { paths, IS_CLOUD } from "@dokploy/server/constants";
|
|
import { mkdtemp } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { tmpdir } from "node:os";
|
|
|
|
export const restoreWebServerBackup = async (
|
|
destination: Destination,
|
|
backupFile: string,
|
|
emit: (log: string) => void,
|
|
) => {
|
|
if (IS_CLOUD) {
|
|
return;
|
|
}
|
|
try {
|
|
const rcloneFlags = getS3Credentials(destination);
|
|
const bucketPath = `:s3:${destination.bucket}`;
|
|
const backupPath = `${bucketPath}/${backupFile}`;
|
|
const { BASE_PATH } = paths();
|
|
|
|
// Create a temporary directory outside of BASE_PATH
|
|
const tempDir = await mkdtemp(join(tmpdir(), "dokploy-restore-"));
|
|
|
|
try {
|
|
emit("Starting restore...");
|
|
emit(`Backup path: ${backupPath}`);
|
|
emit(`Temp directory: ${tempDir}`);
|
|
|
|
// Create temp directory
|
|
emit("Creating temporary directory...");
|
|
await execAsync(`mkdir -p ${tempDir}`);
|
|
|
|
// Download backup from S3
|
|
emit("Downloading backup from S3...");
|
|
await execAsync(
|
|
`rclone copyto ${rcloneFlags.join(" ")} "${backupPath}" "${tempDir}/${backupFile}"`,
|
|
);
|
|
|
|
// List files before extraction
|
|
emit("Listing files before extraction...");
|
|
const { stdout: beforeFiles } = await execAsync(`ls -la ${tempDir}`);
|
|
emit(`Files before extraction: ${beforeFiles}`);
|
|
|
|
// Extract backup
|
|
emit("Extracting backup...");
|
|
await execAsync(`cd ${tempDir} && unzip ${backupFile} > /dev/null 2>&1`);
|
|
|
|
// Restore filesystem first
|
|
emit("Restoring filesystem...");
|
|
emit(`Copying from ${tempDir}/filesystem/* to ${BASE_PATH}/`);
|
|
|
|
// First clean the target directory
|
|
emit("Cleaning target directory...");
|
|
await execAsync(`rm -rf "${BASE_PATH}/"*`);
|
|
|
|
// Ensure the target directory exists
|
|
emit("Setting up target directory...");
|
|
await execAsync(`mkdir -p "${BASE_PATH}"`);
|
|
|
|
// Copy files preserving permissions
|
|
emit("Copying files...");
|
|
await execAsync(`cp -rp "${tempDir}/filesystem/"* "${BASE_PATH}/"`);
|
|
|
|
// Now handle database restore
|
|
emit("Starting database restore...");
|
|
|
|
// Check if database.sql.gz exists and decompress it
|
|
const { stdout: hasGzFile } = await execAsync(
|
|
`ls ${tempDir}/database.sql.gz || true`,
|
|
);
|
|
if (hasGzFile.includes("database.sql.gz")) {
|
|
emit("Found compressed database file, decompressing...");
|
|
await execAsync(`cd ${tempDir} && gunzip database.sql.gz`);
|
|
}
|
|
|
|
// Verify database file exists
|
|
const { stdout: hasSqlFile } = await execAsync(
|
|
`ls ${tempDir}/database.sql || true`,
|
|
);
|
|
if (!hasSqlFile.includes("database.sql")) {
|
|
throw new Error("Database file not found after extraction");
|
|
}
|
|
|
|
// Drop and recreate database
|
|
emit("Disconnecting all users from database...");
|
|
await execAsync(
|
|
`docker exec $(docker ps --filter "name=dokploy-postgres" -q) psql -U dokploy postgres -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = 'dokploy' AND pid <> pg_backend_pid();"`,
|
|
);
|
|
|
|
emit("Dropping existing database...");
|
|
await execAsync(
|
|
`docker exec $(docker ps --filter "name=dokploy-postgres" -q) psql -U dokploy postgres -c "DROP DATABASE IF EXISTS dokploy;"`,
|
|
);
|
|
|
|
emit("Creating fresh database...");
|
|
await execAsync(
|
|
`docker exec $(docker ps --filter "name=dokploy-postgres" -q) psql -U dokploy postgres -c "CREATE DATABASE dokploy;"`,
|
|
);
|
|
|
|
// Copy the backup file into the container
|
|
emit("Copying backup file into container...");
|
|
await execAsync(
|
|
`docker cp ${tempDir}/database.sql $(docker ps --filter "name=dokploy-postgres" -q):/tmp/database.sql`,
|
|
);
|
|
|
|
// Verify file in container
|
|
emit("Verifying file in container...");
|
|
await execAsync(
|
|
`docker exec $(docker ps --filter "name=dokploy-postgres" -q) ls -l /tmp/database.sql`,
|
|
);
|
|
|
|
// Restore from the copied file
|
|
emit("Running database restore...");
|
|
await execAsync(
|
|
`docker exec $(docker ps --filter "name=dokploy-postgres" -q) pg_restore -v -U dokploy -d dokploy /tmp/database.sql`,
|
|
);
|
|
|
|
// Cleanup the temporary file in the container
|
|
emit("Cleaning up container temp file...");
|
|
await execAsync(
|
|
`docker exec $(docker ps --filter "name=dokploy-postgres" -q) rm /tmp/database.sql`,
|
|
);
|
|
|
|
emit("Restore completed successfully!");
|
|
} finally {
|
|
// Cleanup
|
|
emit("Cleaning up temporary files...");
|
|
await execAsync(`rm -rf ${tempDir}`);
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
emit(
|
|
`Error: ${
|
|
error instanceof Error
|
|
? error.message
|
|
: "Error restoring web server backup"
|
|
}`,
|
|
);
|
|
throw error;
|
|
}
|
|
};
|