2024-01-06 05:24:57 +00:00
< ? php
namespace App\Livewire\Project\Database ;
2025-11-02 14:19:13 +00:00
use App\Models\S3Storage ;
2024-01-06 05:24:57 +00:00
use App\Models\Server ;
2025-08-23 16:50:35 +00:00
use Illuminate\Foundation\Auth\Access\AuthorizesRequests ;
2024-11-04 13:18:16 +00:00
use Illuminate\Support\Facades\Auth ;
2024-01-10 14:42:54 +00:00
use Illuminate\Support\Facades\Storage ;
2024-06-10 20:43:34 +00:00
use Livewire\Component ;
2024-01-06 05:24:57 +00:00
class Import extends Component
{
2025-08-23 16:50:35 +00:00
use AuthorizesRequests ;
2024-04-11 11:20:46 +00:00
public bool $unsupported = false ;
2024-06-10 20:43:34 +00:00
2024-01-06 05:24:57 +00:00
public $resource ;
2024-06-10 20:43:34 +00:00
2024-01-06 05:24:57 +00:00
public $parameters ;
2024-06-10 20:43:34 +00:00
2024-01-10 14:42:54 +00:00
public $containers ;
2024-06-10 20:43:34 +00:00
2024-01-06 05:24:57 +00:00
public bool $scpInProgress = false ;
2024-06-10 20:43:34 +00:00
2024-01-06 05:24:57 +00:00
public bool $importRunning = false ;
2024-04-11 10:13:11 +00:00
public ? string $filename = null ;
2024-06-10 20:43:34 +00:00
2024-04-11 10:13:11 +00:00
public ? string $filesize = null ;
2024-06-10 20:43:34 +00:00
2024-04-11 10:13:11 +00:00
public bool $isUploading = false ;
2024-06-10 20:43:34 +00:00
2024-04-11 10:13:11 +00:00
public int $progress = 0 ;
2024-06-10 20:43:34 +00:00
2024-04-11 10:13:11 +00:00
public bool $error = false ;
2024-01-06 05:24:57 +00:00
public Server $server ;
2024-06-10 20:43:34 +00:00
2024-01-06 05:24:57 +00:00
public string $container ;
2024-06-10 20:43:34 +00:00
2024-01-06 05:24:57 +00:00
public array $importCommands = [];
2024-06-10 20:43:34 +00:00
2025-01-07 12:00:41 +00:00
public bool $dumpAll = false ;
public string $restoreCommandText = '' ;
2025-01-07 13:02:19 +00:00
public string $customLocation = '' ;
2024-01-10 14:42:54 +00:00
public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB' ;
2024-06-10 20:43:34 +00:00
2024-02-24 20:12:34 +00:00
public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE' ;
2024-06-10 20:43:34 +00:00
2024-02-24 20:12:34 +00:00
public string $mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p$MARIADB_PASSWORD $MARIADB_DATABASE' ;
2024-06-10 20:43:34 +00:00
2024-04-25 21:44:55 +00:00
public string $mongodbRestoreCommand = 'mongorestore --authenticationDatabase=admin --username $MONGO_INITDB_ROOT_USERNAME --password $MONGO_INITDB_ROOT_PASSWORD --uri mongodb://localhost:27017 --gzip --archive=' ;
2024-01-06 05:24:57 +00:00
2025-11-02 14:19:13 +00:00
// S3 Restore properties
public $availableS3Storages = [];
public ? int $s3StorageId = null ;
public string $s3Path = '' ;
public ? int $s3FileSize = null ;
2024-01-10 14:42:54 +00:00
public function getListeners ()
{
2024-11-04 13:18:16 +00:00
$userId = Auth :: id ();
2024-06-10 20:43:34 +00:00
2024-01-10 14:42:54 +00:00
return [
" echo-private:user. { $userId } ,DatabaseStatusChanged " => '$refresh' ,
];
}
2024-06-10 20:43:34 +00:00
2024-01-06 05:24:57 +00:00
public function mount ()
{
$this -> parameters = get_route_parameters ();
$this -> getContainers ();
2025-11-02 14:19:13 +00:00
$this -> loadAvailableS3Storages ();
2024-01-06 05:24:57 +00:00
}
2025-01-07 12:00:41 +00:00
public function updatedDumpAll ( $value )
{
switch ( $this -> resource -> getMorphClass ()) {
2025-01-07 14:31:43 +00:00
case \App\Models\StandaloneMariadb :: class :
2025-01-07 12:00:41 +00:00
if ( $value === true ) {
$this -> mariadbRestoreCommand = <<< 'EOD'
for pid in $ ( mariadb - u root - p $MARIADB_ROOT_PASSWORD - N - e " SELECT id FROM information_schema.processlist WHERE user != 'root'; " ); do
mariadb - u root - p $MARIADB_ROOT_PASSWORD - e " KILL $pid " 2 >/ dev / null || true
done && \
mariadb - u root - p $MARIADB_ROOT_PASSWORD - N - e " SELECT CONCAT('DROP DATABASE IF EXISTS \ `',schema_name,' \ `;') FROM information_schema.schemata WHERE schema_name NOT IN ('information_schema','mysql','performance_schema','sys'); " | mariadb - u root - p $MARIADB_ROOT_PASSWORD && \
mariadb - u root - p $MARIADB_ROOT_PASSWORD - e " CREATE DATABASE IF NOT EXISTS \ `default \ `; " && \
( gunzip - cf $tmpPath 2 >/ dev / null || cat $tmpPath ) | sed - e '/^CREATE DATABASE/d' - e '/^USE \`mysql\`/d' | mariadb - u root - p $MARIADB_ROOT_PASSWORD default
EOD ;
$this -> restoreCommandText = $this -> mariadbRestoreCommand . ' && (gunzip -cf <temp_backup_file> 2>/dev/null || cat <temp_backup_file>) | mariadb -u root -p$MARIADB_ROOT_PASSWORD default' ;
} else {
$this -> mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p$MARIADB_PASSWORD $MARIADB_DATABASE' ;
}
break ;
2025-01-07 14:31:43 +00:00
case \App\Models\StandaloneMysql :: class :
2025-01-07 12:00:41 +00:00
if ( $value === true ) {
$this -> mysqlRestoreCommand = <<< 'EOD'
for pid in $ ( mysql - u root - p $MYSQL_ROOT_PASSWORD - N - e " SELECT id FROM information_schema.processlist WHERE user != 'root'; " ); do
mysql - u root - p $MYSQL_ROOT_PASSWORD - e " KILL $pid " 2 >/ dev / null || true
done && \
mysql - u root - p $MYSQL_ROOT_PASSWORD - N - e " SELECT CONCAT('DROP DATABASE IF EXISTS \ `',schema_name,' \ `;') FROM information_schema.schemata WHERE schema_name NOT IN ('information_schema','mysql','performance_schema','sys'); " | mysql - u root - p $MYSQL_ROOT_PASSWORD && \
mysql - u root - p $MYSQL_ROOT_PASSWORD - e " CREATE DATABASE IF NOT EXISTS \ `default \ `; " && \
( gunzip - cf $tmpPath 2 >/ dev / null || cat $tmpPath ) | sed - e '/^CREATE DATABASE/d' - e '/^USE \`mysql\`/d' | mysql - u root - p $MYSQL_ROOT_PASSWORD default
EOD ;
$this -> restoreCommandText = $this -> mysqlRestoreCommand . ' && (gunzip -cf <temp_backup_file> 2>/dev/null || cat <temp_backup_file>) | mysql -u root -p$MYSQL_ROOT_PASSWORD default' ;
} else {
$this -> mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE' ;
}
break ;
2025-01-07 14:31:43 +00:00
case \App\Models\StandalonePostgresql :: class :
2025-01-07 12:00:41 +00:00
if ( $value === true ) {
$this -> postgresqlRestoreCommand = <<< 'EOD'
psql - U $POSTGRES_USER - c " SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname IS NOT NULL AND pid <> pg_backend_pid() " && \
psql - U $POSTGRES_USER - t - c " SELECT datname FROM pg_database WHERE NOT datistemplate " | xargs - I {} dropdb - U $POSTGRES_USER -- if - exists {} && \
createdb - U $POSTGRES_USER postgres
EOD ;
$this -> restoreCommandText = $this -> postgresqlRestoreCommand . ' && (gunzip -cf <temp_backup_file> 2>/dev/null || cat <temp_backup_file>) | psql -U $POSTGRES_USER postgres' ;
} else {
$this -> postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB' ;
}
break ;
}
}
2024-01-06 05:24:57 +00:00
public function getContainers ()
{
$this -> containers = collect ();
2024-06-10 20:43:34 +00:00
if ( ! data_get ( $this -> parameters , 'database_uuid' )) {
2024-01-06 05:24:57 +00:00
abort ( 404 );
}
2024-04-11 10:13:11 +00:00
$resource = getResourceByUuid ( $this -> parameters [ 'database_uuid' ], data_get ( auth () -> user () -> currentTeam (), 'id' ));
2024-01-06 05:24:57 +00:00
if ( is_null ( $resource )) {
2024-04-10 13:00:46 +00:00
abort ( 404 );
2024-01-06 05:24:57 +00:00
}
2025-10-14 15:33:42 +00:00
$this -> authorize ( 'view' , $resource );
2024-01-06 05:24:57 +00:00
$this -> resource = $resource ;
$this -> server = $this -> resource -> destination -> server ;
$this -> container = $this -> resource -> uuid ;
2024-01-10 14:42:54 +00:00
if ( str ( data_get ( $this , 'resource.status' )) -> startsWith ( 'running' )) {
2024-01-06 05:24:57 +00:00
$this -> containers -> push ( $this -> container );
}
2024-01-10 14:42:54 +00:00
if (
2025-01-07 14:31:43 +00:00
$this -> resource -> getMorphClass () === \App\Models\StandaloneRedis :: class ||
$this -> resource -> getMorphClass () === \App\Models\StandaloneKeydb :: class ||
$this -> resource -> getMorphClass () === \App\Models\StandaloneDragonfly :: class ||
$this -> resource -> getMorphClass () === \App\Models\StandaloneClickhouse :: class
2024-01-10 14:42:54 +00:00
) {
2024-04-11 11:20:46 +00:00
$this -> unsupported = true ;
2024-01-06 05:24:57 +00:00
}
}
2024-01-10 14:42:54 +00:00
2025-01-07 13:02:19 +00:00
public function checkFile ()
{
if ( filled ( $this -> customLocation )) {
try {
$result = instant_remote_process ([ " ls -l { $this -> customLocation } " ], $this -> server , throwError : false );
if ( blank ( $result )) {
$this -> dispatch ( 'error' , 'The file does not exist or has been deleted.' );
2025-01-07 14:31:43 +00:00
return ;
2025-01-07 13:02:19 +00:00
}
$this -> filename = $this -> customLocation ;
$this -> dispatch ( 'success' , 'The file exists.' );
2025-01-07 14:31:43 +00:00
} catch ( \Throwable $e ) {
2025-01-07 13:02:19 +00:00
return handleError ( $e , $this );
}
}
}
2024-01-06 05:24:57 +00:00
public function runImport ()
{
2025-08-23 16:50:35 +00:00
$this -> authorize ( 'update' , $this -> resource );
2024-10-31 14:23:19 +00:00
if ( $this -> filename === '' ) {
2024-04-11 10:13:11 +00:00
$this -> dispatch ( 'error' , 'Please select a file to import.' );
2024-06-10 20:43:34 +00:00
2025-01-07 14:31:43 +00:00
return ;
2024-04-11 10:13:11 +00:00
}
2024-01-06 05:24:57 +00:00
try {
2025-08-26 08:27:31 +00:00
$this -> importRunning = true ;
2025-01-07 13:02:19 +00:00
$this -> importCommands = [];
2025-11-17 13:13:10 +00:00
$backupFileName = " upload/ { $this -> resource -> uuid } /restore " ;
2025-01-07 13:02:19 +00:00
2025-11-17 13:13:10 +00:00
// Check if an uploaded file exists first (takes priority over custom location)
if ( Storage :: exists ( $backupFileName )) {
$path = Storage :: path ( $backupFileName );
2025-01-07 13:02:19 +00:00
$tmpPath = '/tmp/' . basename ( $backupFileName ) . '_' . $this -> resource -> uuid ;
instant_scp ( $path , $tmpPath , $this -> server );
Storage :: delete ( $backupFileName );
$this -> importCommands [] = " docker cp { $tmpPath } { $this -> container } : { $tmpPath } " ;
2025-11-17 13:13:10 +00:00
} elseif ( filled ( $this -> customLocation )) {
$tmpPath = '/tmp/restore_' . $this -> resource -> uuid ;
$this -> importCommands [] = " docker cp { $this -> customLocation } { $this -> container } : { $tmpPath } " ;
} else {
$this -> dispatch ( 'error' , 'The file does not exist or has been deleted.' );
return ;
2024-04-11 10:13:11 +00:00
}
2024-01-06 05:24:57 +00:00
2025-01-07 12:00:41 +00:00
// Copy the restore command to a script file
$scriptPath = " /tmp/restore_ { $this -> resource -> uuid } .sh " ;
2024-01-06 05:24:57 +00:00
switch ( $this -> resource -> getMorphClass ()) {
2025-01-07 14:31:43 +00:00
case \App\Models\StandaloneMariadb :: class :
2025-01-07 12:00:41 +00:00
$restoreCommand = $this -> mariadbRestoreCommand ;
if ( $this -> dumpAll ) {
$restoreCommand .= " && (gunzip -cf { $tmpPath } 2>/dev/null || cat { $tmpPath } ) | mariadb -u root -p \$ MARIADB_ROOT_PASSWORD " ;
} else {
2025-01-07 13:08:38 +00:00
$restoreCommand .= " < { $tmpPath } " ;
2025-01-07 12:00:41 +00:00
}
2024-01-10 14:42:54 +00:00
break ;
2025-01-07 14:31:43 +00:00
case \App\Models\StandaloneMysql :: class :
2025-01-07 12:00:41 +00:00
$restoreCommand = $this -> mysqlRestoreCommand ;
if ( $this -> dumpAll ) {
$restoreCommand .= " && (gunzip -cf { $tmpPath } 2>/dev/null || cat { $tmpPath } ) | mysql -u root -p \$ MYSQL_ROOT_PASSWORD " ;
} else {
2025-01-07 13:08:38 +00:00
$restoreCommand .= " < { $tmpPath } " ;
2025-01-07 12:00:41 +00:00
}
2024-01-10 14:42:54 +00:00
break ;
2025-01-07 14:31:43 +00:00
case \App\Models\StandalonePostgresql :: class :
2025-01-07 12:00:41 +00:00
$restoreCommand = $this -> postgresqlRestoreCommand ;
if ( $this -> dumpAll ) {
$restoreCommand .= " && (gunzip -cf { $tmpPath } 2>/dev/null || cat { $tmpPath } ) | psql -U \$ POSTGRES_USER postgres " ;
} else {
$restoreCommand .= " { $tmpPath } " ;
}
2024-01-10 14:42:54 +00:00
break ;
2025-01-07 14:31:43 +00:00
case \App\Models\StandaloneMongodb :: class :
2025-01-07 12:00:41 +00:00
$restoreCommand = $this -> mongodbRestoreCommand ;
if ( $this -> dumpAll === false ) {
2025-01-28 11:41:22 +00:00
$restoreCommand .= " { $tmpPath } " ;
2025-01-07 12:00:41 +00:00
}
2024-04-14 20:31:55 +00:00
break ;
2024-01-06 05:24:57 +00:00
}
2025-09-15 15:56:48 +00:00
$restoreCommandBase64 = base64_encode ( $restoreCommand );
$this -> importCommands [] = " echo \" { $restoreCommandBase64 } \" | base64 -d > { $scriptPath } " ;
2025-01-07 12:00:41 +00:00
$this -> importCommands [] = " chmod +x { $scriptPath } " ;
$this -> importCommands [] = " docker cp { $scriptPath } { $this -> container } : { $scriptPath } " ;
$this -> importCommands [] = " docker exec { $this -> container } sh -c ' { $scriptPath } ' " ;
2024-01-06 05:24:57 +00:00
$this -> importCommands [] = " docker exec { $this -> container } sh -c 'echo \" Import finished with exit code $ ? \" ' " ;
2025-01-07 14:31:43 +00:00
if ( ! empty ( $this -> importCommands )) {
2025-01-07 13:02:19 +00:00
$activity = remote_process ( $this -> importCommands , $this -> server , ignore_errors : true , callEventOnFinish : 'RestoreJobFinished' , callEventData : [
'scriptPath' => $scriptPath ,
'tmpPath' => $tmpPath ,
'container' => $this -> container ,
'serverId' => $this -> server -> id ,
]);
2025-11-17 09:05:18 +00:00
// Dispatch activity to the monitor and open slide-over
2025-11-02 16:10:34 +00:00
$this -> dispatch ( 'activityMonitor' , $activity -> id );
2025-11-17 09:05:18 +00:00
$this -> dispatch ( 'databaserestore' );
2024-01-06 05:24:57 +00:00
}
2025-01-07 14:31:43 +00:00
} catch ( \Throwable $e ) {
2024-04-11 10:13:11 +00:00
return handleError ( $e , $this );
2025-01-07 12:00:41 +00:00
} finally {
$this -> filename = null ;
2025-01-07 13:02:19 +00:00
$this -> importCommands = [];
2024-01-06 05:24:57 +00:00
}
}
2025-11-02 14:19:13 +00:00
public function loadAvailableS3Storages ()
{
try {
$this -> availableS3Storages = S3Storage :: ownedByCurrentTeam ([ 'id' , 'name' , 'description' ])
-> where ( 'is_usable' , true )
-> get ();
} catch ( \Throwable $e ) {
$this -> availableS3Storages = collect ();
}
}
2025-11-25 09:18:30 +00:00
public function updatedS3Path ( $value )
{
// Reset validation state when path changes
$this -> s3FileSize = null ;
// Ensure path starts with a slash
if ( $value !== null && $value !== '' ) {
$this -> s3Path = str ( $value ) -> trim () -> start ( '/' ) -> value ();
}
}
public function updatedS3StorageId ()
{
// Reset validation state when storage changes
$this -> s3FileSize = null ;
}
2025-11-02 14:19:13 +00:00
public function checkS3File ()
{
if ( ! $this -> s3StorageId ) {
$this -> dispatch ( 'error' , 'Please select an S3 storage.' );
return ;
}
if ( blank ( $this -> s3Path )) {
$this -> dispatch ( 'error' , 'Please provide an S3 path.' );
return ;
}
try {
2025-11-02 15:33:34 +00:00
$s3Storage = S3Storage :: ownedByCurrentTeam () -> findOrFail ( $this -> s3StorageId );
2025-11-02 14:19:13 +00:00
// Test connection
$s3Storage -> testConnection ();
// Build S3 disk configuration
$disk = Storage :: build ([
'driver' => 's3' ,
'region' => $s3Storage -> region ,
'key' => $s3Storage -> key ,
'secret' => $s3Storage -> secret ,
'bucket' => $s3Storage -> bucket ,
'endpoint' => $s3Storage -> endpoint ,
'use_path_style_endpoint' => true ,
]);
// Clean the path (remove leading slash if present)
$cleanPath = ltrim ( $this -> s3Path , '/' );
// Check if file exists
if ( ! $disk -> exists ( $cleanPath )) {
$this -> dispatch ( 'error' , 'File not found in S3. Please check the path.' );
return ;
}
// Get file size
$this -> s3FileSize = $disk -> size ( $cleanPath );
$this -> dispatch ( 'success' , 'File found in S3. Size: ' . formatBytes ( $this -> s3FileSize ));
} catch ( \Throwable $e ) {
$this -> s3FileSize = null ;
return handleError ( $e , $this );
}
}
2025-11-17 09:05:18 +00:00
public function restoreFromS3 ()
2025-11-02 14:19:13 +00:00
{
$this -> authorize ( 'update' , $this -> resource );
if ( ! $this -> s3StorageId || blank ( $this -> s3Path )) {
$this -> dispatch ( 'error' , 'Please select S3 storage and provide a path first.' );
return ;
}
if ( is_null ( $this -> s3FileSize )) {
$this -> dispatch ( 'error' , 'Please check the file first by clicking "Check File".' );
return ;
}
try {
2025-11-17 09:05:18 +00:00
$this -> importRunning = true ;
2025-11-02 14:19:13 +00:00
2025-11-02 15:33:34 +00:00
$s3Storage = S3Storage :: ownedByCurrentTeam () -> findOrFail ( $this -> s3StorageId );
2025-11-02 14:19:13 +00:00
$key = $s3Storage -> key ;
$secret = $s3Storage -> secret ;
$bucket = $s3Storage -> bucket ;
$endpoint = $s3Storage -> endpoint ;
2025-11-17 09:05:18 +00:00
// Clean the S3 path
2025-11-02 14:19:13 +00:00
$cleanPath = ltrim ( $this -> s3Path , '/' );
// Get helper image
$helperImage = config ( 'constants.coolify.helper_image' );
2025-11-17 09:05:18 +00:00
$latestVersion = getHelperVersion ();
2025-11-02 14:19:13 +00:00
$fullImageName = " { $helperImage } : { $latestVersion } " ;
2025-11-17 09:05:18 +00:00
// Get the database destination network
$destinationNetwork = $this -> resource -> destination -> network ? ? 'coolify' ;
2025-11-02 14:19:13 +00:00
2025-11-17 09:05:18 +00:00
// Generate unique names for this operation
2025-11-02 14:19:13 +00:00
$containerName = " s3-restore- { $this -> resource -> uuid } " ;
2025-11-17 09:05:18 +00:00
$helperTmpPath = '/tmp/' . basename ( $cleanPath );
$serverTmpPath = " /tmp/s3-restore- { $this -> resource -> uuid } - " . basename ( $cleanPath );
$containerTmpPath = " /tmp/restore_ { $this -> resource -> uuid } - " . basename ( $cleanPath );
$scriptPath = " /tmp/restore_ { $this -> resource -> uuid } .sh " ;
2025-11-02 14:19:13 +00:00
2025-11-17 09:05:18 +00:00
// Prepare all commands in sequence
$commands = [];
2025-11-02 14:19:13 +00:00
2025-11-17 13:23:50 +00:00
// 1. Clean up any existing helper container and temp files from previous runs
2025-11-17 09:05:18 +00:00
$commands [] = " docker rm -f { $containerName } 2>/dev/null || true " ;
2025-11-17 13:23:50 +00:00
$commands [] = " rm -f { $serverTmpPath } 2>/dev/null || true " ;
$commands [] = " docker exec { $this -> container } rm -f { $containerTmpPath } { $scriptPath } 2>/dev/null || true " ;
2025-11-02 14:19:13 +00:00
2025-11-17 09:05:18 +00:00
// 2. Start helper container on the database network
2025-11-17 13:13:10 +00:00
$commands [] = " docker run -d --network { $destinationNetwork } --name { $containerName } { $fullImageName } sleep 3600 " ;
2025-11-02 14:19:13 +00:00
2025-11-17 09:05:18 +00:00
// 3. Configure S3 access in helper container
$escapedEndpoint = escapeshellarg ( $endpoint );
$escapedKey = escapeshellarg ( $key );
$escapedSecret = escapeshellarg ( $secret );
$commands [] = " docker exec { $containerName } mc alias set s3temp { $escapedEndpoint } { $escapedKey } { $escapedSecret } " ;
2025-11-02 14:19:13 +00:00
2025-11-17 09:05:18 +00:00
// 4. Check file exists in S3
$commands [] = " docker exec { $containerName } mc stat s3temp/ { $bucket } / { $cleanPath } " ;
2025-11-02 14:19:13 +00:00
2025-11-17 13:23:50 +00:00
// 5. Download from S3 to helper container (progress shown by default)
2025-11-17 09:05:18 +00:00
$commands [] = " docker exec { $containerName } mc cp s3temp/ { $bucket } / { $cleanPath } { $helperTmpPath } " ;
2025-11-02 14:19:13 +00:00
2025-11-17 13:23:50 +00:00
// 6. Copy from helper to server, then immediately to database container
2025-11-17 09:05:18 +00:00
$commands [] = " docker cp { $containerName } : { $helperTmpPath } { $serverTmpPath } " ;
$commands [] = " docker cp { $serverTmpPath } { $this -> container } : { $containerTmpPath } " ;
2025-11-02 14:19:13 +00:00
2025-11-17 13:23:50 +00:00
// 7. Cleanup helper container and server temp file immediately (no longer needed)
$commands [] = " docker rm -f { $containerName } 2>/dev/null || true " ;
$commands [] = " rm -f { $serverTmpPath } 2>/dev/null || true " ;
2025-11-17 09:05:18 +00:00
// 8. Build and execute restore command inside database container
$restoreCommand = $this -> buildRestoreCommand ( $containerTmpPath );
2025-11-02 14:19:13 +00:00
$restoreCommandBase64 = base64_encode ( $restoreCommand );
2025-11-17 09:05:18 +00:00
$commands [] = " echo \" { $restoreCommandBase64 } \" | base64 -d > { $scriptPath } " ;
$commands [] = " chmod +x { $scriptPath } " ;
$commands [] = " docker cp { $scriptPath } { $this -> container } : { $scriptPath } " ;
2025-11-17 13:23:50 +00:00
// 9. Execute restore and cleanup temp files immediately after completion
$commands [] = " docker exec { $this -> container } sh -c ' { $scriptPath } && rm -f { $containerTmpPath } { $scriptPath } ' " ;
2025-11-17 09:05:18 +00:00
$commands [] = " docker exec { $this -> container } sh -c 'echo \" Import finished with exit code $ ? \" ' " ;
2025-11-17 13:23:50 +00:00
// Execute all commands with cleanup event (as safety net for edge cases)
2025-11-17 09:05:18 +00:00
$activity = remote_process ( $commands , $this -> server , ignore_errors : true , callEventOnFinish : 'S3RestoreJobFinished' , callEventData : [
'containerName' => $containerName ,
'serverTmpPath' => $serverTmpPath ,
'scriptPath' => $scriptPath ,
'containerTmpPath' => $containerTmpPath ,
'container' => $this -> container ,
'serverId' => $this -> server -> id ,
]);
2025-11-02 14:19:13 +00:00
2025-11-17 09:05:18 +00:00
// Dispatch activity to the monitor and open slide-over
$this -> dispatch ( 'activityMonitor' , $activity -> id );
$this -> dispatch ( 'databaserestore' );
2025-11-17 13:23:50 +00:00
$this -> dispatch ( 'info' , 'Restoring database from S3. Progress will be shown in the activity monitor...' );
2025-11-02 14:19:13 +00:00
} catch ( \Throwable $e ) {
2025-11-17 09:05:18 +00:00
$this -> importRunning = false ;
2025-11-02 14:19:13 +00:00
return handleError ( $e , $this );
}
}
2025-11-17 09:05:18 +00:00
public function buildRestoreCommand ( string $tmpPath ) : string
2025-11-02 14:19:13 +00:00
{
2025-11-17 09:05:18 +00:00
switch ( $this -> resource -> getMorphClass ()) {
case \App\Models\StandaloneMariadb :: class :
$restoreCommand = $this -> mariadbRestoreCommand ;
if ( $this -> dumpAll ) {
$restoreCommand .= " && (gunzip -cf { $tmpPath } 2>/dev/null || cat { $tmpPath } ) | mariadb -u root -p \$ MARIADB_ROOT_PASSWORD " ;
} else {
$restoreCommand .= " < { $tmpPath } " ;
}
break ;
case \App\Models\StandaloneMysql :: class :
$restoreCommand = $this -> mysqlRestoreCommand ;
if ( $this -> dumpAll ) {
$restoreCommand .= " && (gunzip -cf { $tmpPath } 2>/dev/null || cat { $tmpPath } ) | mysql -u root -p \$ MYSQL_ROOT_PASSWORD " ;
} else {
$restoreCommand .= " < { $tmpPath } " ;
}
break ;
case \App\Models\StandalonePostgresql :: class :
$restoreCommand = $this -> postgresqlRestoreCommand ;
if ( $this -> dumpAll ) {
$restoreCommand .= " && (gunzip -cf { $tmpPath } 2>/dev/null || cat { $tmpPath } ) | psql -U \$ POSTGRES_USER postgres " ;
} else {
$restoreCommand .= " { $tmpPath } " ;
}
break ;
case \App\Models\StandaloneMongodb :: class :
$restoreCommand = $this -> mongodbRestoreCommand ;
if ( $this -> dumpAll === false ) {
$restoreCommand .= " { $tmpPath } " ;
}
break ;
default :
$restoreCommand = '' ;
2025-11-02 14:19:13 +00:00
}
2025-11-17 09:05:18 +00:00
return $restoreCommand ;
2025-11-02 14:19:13 +00:00
}
2024-01-06 05:24:57 +00:00
}