diff --git a/backend/.env.example b/backend/.env.example index c0660ea..b3d7c1f 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -63,3 +63,8 @@ AWS_BUCKET= AWS_USE_PATH_STYLE_ENDPOINT=false VITE_APP_NAME="${APP_NAME}" + +# Database Transfer Tools +# Windows example: C:/xampp/mysql/bin/mysqldump.exe +MYSQLDUMP_PATH=mysqldump +MYSQL_BINARY_PATH=mysql diff --git a/backend/app/Services/Database/MySqlDriver.php b/backend/app/Services/Database/MySqlDriver.php index d35ec20..059f422 100644 --- a/backend/app/Services/Database/MySqlDriver.php +++ b/backend/app/Services/Database/MySqlDriver.php @@ -106,6 +106,8 @@ class MySqlDriver implements DatabaseDriverInterface, SchemaDiscoveryInterface $path = $directory . DIRECTORY_SEPARATOR . $filename; $errorPath = $directory . DIRECTORY_SEPARATOR . $filename . '.err'; + $mysqldumpPath = env('MYSQLDUMP_PATH', 'mysqldump'); + // Ensure we have a username $username = $config['username'] ?? 'root'; $password = $config['password'] ?? ''; @@ -117,10 +119,9 @@ class MySqlDriver implements DatabaseDriverInterface, SchemaDiscoveryInterface $passwordPart = !empty($password) ? "-p" . escapeshellarg($password) : ""; $dbPart = !empty($database) ? escapeshellarg($database) : "--all-databases"; - // On Windows, we might need to handle double quotes in escapeshellarg - // Let's use a more robust way to execute and capture errors $command = sprintf( - 'mysqldump -u %s %s -h %s -P %s %s > %s 2> %s', + '%s -u %s %s -h %s -P %s %s > %s 2> %s', + $mysqldumpPath === 'mysqldump' ? 'mysqldump' : escapeshellarg($mysqldumpPath), escapeshellarg($username), $passwordPart, escapeshellarg($host), @@ -136,15 +137,16 @@ class MySqlDriver implements DatabaseDriverInterface, SchemaDiscoveryInterface $errors = file_get_contents($errorPath); unlink($errorPath); if (!empty(trim($errors))) { - // If the file is 0 bytes and there are errors, it definitely failed if (!file_exists($path) || filesize($path) === 0) { - throw new \Exception("Export failed: " . $errors); + $instruction = "Please check your .env file and ensure MYSQLDUMP_PATH is correct. Example: MYSQLDUMP_PATH=C:\\xampp\\mysql\\bin\\mysqldump.exe"; + throw new \Exception("Export failed: " . $errors . "\n\nInstructions: " . $instruction); } } } if (!file_exists($path) || filesize($path) === 0) { - throw new \Exception("Export failed: Resulting file is empty. Ensure mysqldump is installed and in the system PATH."); + $instruction = "The 'mysqldump' binary was not found. Please set MYSQLDUMP_PATH in your .env file to the absolute path of the mysqldump executable."; + throw new \Exception("Export failed: Binary not found.\n\nInstructions: " . $instruction); } return $path; @@ -157,6 +159,8 @@ class MySqlDriver implements DatabaseDriverInterface, SchemaDiscoveryInterface mkdir($directory, 0755, true); } $errorPath = $directory . DIRECTORY_SEPARATOR . 'import_error.err'; + + $mysqlPath = env('MYSQL_BINARY_PATH', 'mysql'); $username = $config['username'] ?? 'root'; $password = $config['password'] ?? ''; @@ -168,7 +172,8 @@ class MySqlDriver implements DatabaseDriverInterface, SchemaDiscoveryInterface $dbPart = !empty($database) ? escapeshellarg($database) : ""; $command = sprintf( - 'mysql -u %s %s -h %s -P %s %s < %s 2> %s', + '%s -u %s %s -h %s -P %s %s < %s 2> %s', + $mysqlPath === 'mysql' ? 'mysql' : escapeshellarg($mysqlPath), escapeshellarg($username), $passwordPart, escapeshellarg($host), @@ -184,7 +189,8 @@ class MySqlDriver implements DatabaseDriverInterface, SchemaDiscoveryInterface $errors = file_get_contents($errorPath); unlink($errorPath); if (!empty(trim($errors))) { - throw new \Exception("Import failed: " . $errors); + $instruction = "Please check your .env file and ensure MYSQL_BINARY_PATH is correct. Example: MYSQL_BINARY_PATH=C:\\xampp\\mysql\\bin\\mysql.exe"; + throw new \Exception("Import failed: " . $errors . "\n\nInstructions: " . $instruction); } } diff --git a/frontend/src/components/TransferContent.tsx b/frontend/src/components/TransferContent.tsx index 00b949c..275b1fb 100644 --- a/frontend/src/components/TransferContent.tsx +++ b/frontend/src/components/TransferContent.tsx @@ -98,7 +98,11 @@ const TransferContent: React.FC = ({ mode = 'both' }) => { } - sx={{ borderRadius: 2, boxShadow: '0 4px 12px rgba(0,0,0,0.05)' }} + sx={{ + borderRadius: 2, + boxShadow: '0 4px 12px rgba(0,0,0,0.05)', + '& .MuiAlert-message': { whiteSpace: 'pre-line' } + }} > {success || error}