UNPKG

powerbackup

Version:

Multi-Database Backup & Restore Tool v2.4.2 with Beautiful Logging, REST API, Enhanced Binary Detection & Debug Tools

102 lines (87 loc) 4.64 kB
import { spawn } from 'child_process'; import { log } from '../../utils/logger.js'; import { findBinary } from '../../utils/find-binary.js'; import path from 'path'; import { URL } from 'url'; /** * Run pg_dump using the provided Postgres URL and output file path. * @param {string} url - Postgres connection string * @param {string} outputPath - Path to write the dump file * @returns {Promise<void>} */ export async function runPgDump(url, outputPath) { return new Promise(async (resolve, reject) => { try { const parsed = new URL(url); const args = []; if (parsed.hostname) args.push('-h', parsed.hostname); if (parsed.port) args.push('-p', parsed.port); if (parsed.username) args.push('-U', parsed.username); if (parsed.pathname) args.push('-d', parsed.pathname.replace(/^\//, '')); // Use SQL format instead of binary format for compatibility args.push('-F', 'p'); // 'p' for plain SQL format args.push('-f', outputPath); args.push('-v'); // Add verbose output for debugging // Set password env if present const env = { ...process.env }; if (parsed.password) env.PGPASSWORD = parsed.password; // Try to get binary path from config or auto-detect let binaryPath = process.env.PG_DUMP_PATH || null; // Check if we have a configured postgresPath directory if (!binaryPath && global.powerbackupConfig && global.powerbackupConfig.binaries && global.powerbackupConfig.binaries.postgresPath) { const postgresDir = global.powerbackupConfig.binaries.postgresPath; const isWin = process.platform === 'win32'; const pgDumpName = isWin ? 'pg_dump.exe' : 'pg_dump'; binaryPath = path.join(postgresDir, pgDumpName); log.info(`[pg_dump] Using configured postgresPath: ${binaryPath}`); } // If still no binary path, try to find it in PATH if (!binaryPath) { binaryPath = await findBinary('pg_dump'); if (binaryPath) log.info(`[pg_dump] Auto-detected binary: ${binaryPath}`); } // Last resort: use just the command name if (!binaryPath) binaryPath = 'pg_dump'; log.info(`[pg_dump] Running: ${binaryPath} ${args.join(' ')}`); // Test if binary exists and is executable try { const { execSync } = await import('child_process'); execSync(`"${binaryPath}" --version`, { stdio: 'pipe' }); log.info(`[pg_dump] Binary test successful: ${binaryPath}`); } catch (versionError) { log.error(`[pg_dump] Binary not found or not executable: ${binaryPath}`); log.error(`[pg_dump] Version test error: ${versionError.message}`); throw new Error(`pg_dump binary not found or not executable: ${binaryPath}`); } const child = spawn(binaryPath, args, { env }); let stdout = ''; let stderr = ''; child.stdout.on('data', (data) => { stdout += data.toString(); log.info(`[pg_dump] ${data.toString().trim()}`); }); child.stderr.on('data', (data) => { stderr += data.toString(); // pg_dump writes progress info to stderr, so log as info instead of error log.info(`[pg_dump] ${data.toString().trim()}`); }); child.on('close', (code) => { if (code === 0) { log.success('[pg_dump] Dump completed successfully'); resolve(); } else { log.error(`[pg_dump] Process exited with code ${code}`); log.error(`[pg_dump] stdout: ${stdout}`); log.error(`[pg_dump] stderr: ${stderr}`); reject(new Error(`[pg_dump] exited with code ${code}. stderr: ${stderr}`)); } }); child.on('error', (error) => { log.error(`[pg_dump] Spawn error: ${error.message}`); reject(new Error(`[pg_dump] spawn error: ${error.message}`)); }); } catch (err) { reject(err); } }); }