diff --git a/app/Livewire/Project/Database/Import.php b/app/Livewire/Project/Database/Import.php index 062f454b1..b76e7ec2c 100644 --- a/app/Livewire/Project/Database/Import.php +++ b/app/Livewire/Project/Database/Import.php @@ -37,6 +37,10 @@ class Import extends Component public array $importCommands = []; + public bool $dumpAll = false; + + public string $restoreCommandText = ''; + public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB'; public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE'; @@ -60,6 +64,55 @@ public function mount() $this->getContainers(); } + public function updatedDumpAll($value) + { + switch ($this->resource->getMorphClass()) { + case \App\Models\StandaloneMariadb::class: + 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 2>/dev/null || cat ) | mariadb -u root -p$MARIADB_ROOT_PASSWORD default'; + } else { + $this->mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p$MARIADB_PASSWORD $MARIADB_DATABASE'; + } + break; + case \App\Models\StandaloneMysql::class: + 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 2>/dev/null || cat ) | mysql -u root -p$MYSQL_ROOT_PASSWORD default'; + } else { + $this->mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE'; + } + break; + case \App\Models\StandalonePostgresql::class: + 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 2>/dev/null || cat ) | psql -U $POSTGRES_USER postgres'; + } else { + $this->postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB'; + } + break; + } + + } + public function getContainers() { $this->containers = collect(); @@ -107,26 +160,50 @@ public function runImport() Storage::delete($uploadedFilename); $this->importCommands[] = "docker cp {$tmpPath} {$this->container}:{$tmpPath}"; + // Copy the restore command to a script file + $scriptPath = "/tmp/restore_{$this->resource->uuid}.sh"; + switch ($this->resource->getMorphClass()) { case \App\Models\StandaloneMariadb::class: - $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->mariadbRestoreCommand} < {$tmpPath}'"; - $this->importCommands[] = "rm {$tmpPath}"; + $restoreCommand = $this->mariadbRestoreCommand; + if ($this->dumpAll) { + $restoreCommand .= " && (gunzip -cf {$tmpPath} 2>/dev/null || cat {$tmpPath}) | mariadb -u root -p\$MARIADB_ROOT_PASSWORD"; + } else { + $restoreCommand = "mariadb -u \$MARIADB_USER -p\$MARIADB_PASSWORD \$MARIADB_DATABASE < {$tmpPath}"; + } break; case \App\Models\StandaloneMysql::class: - $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->mysqlRestoreCommand} < {$tmpPath}'"; - $this->importCommands[] = "rm {$tmpPath}"; + $restoreCommand = $this->mysqlRestoreCommand; + if ($this->dumpAll) { + $restoreCommand .= " && (gunzip -cf {$tmpPath} 2>/dev/null || cat {$tmpPath}) | mysql -u root -p\$MYSQL_ROOT_PASSWORD"; + } else { + $restoreCommand = "mysql -u \$MYSQL_USER -p\$MYSQL_PASSWORD \$MYSQL_DATABASE < {$tmpPath}"; + } break; case \App\Models\StandalonePostgresql::class: - $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->postgresqlRestoreCommand} {$tmpPath}'"; - $this->importCommands[] = "rm {$tmpPath}"; + $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: - $this->importCommands[] = "docker exec {$this->container} sh -c '{$this->mongodbRestoreCommand}{$tmpPath}'"; - $this->importCommands[] = "rm {$tmpPath}"; + $restoreCommand = $this->mongodbRestoreCommand; + if ($this->dumpAll === false) { + $restoreCommand .= " {$tmpPath}"; + } break; } - $this->importCommands[] = "docker exec {$this->container} sh -c 'rm {$tmpPath}'"; + $restoreCommandBase64 = base64_encode($restoreCommand); + $this->importCommands[] = "echo \"{$restoreCommandBase64}\" | base64 -d > {$scriptPath}"; + $this->importCommands[] = "chmod +x {$scriptPath}"; + $this->importCommands[] = "docker cp {$scriptPath} {$this->container}:{$scriptPath}"; + + $this->importCommands[] = "docker exec {$this->container} sh -c '{$scriptPath}'"; + $this->importCommands[] = "docker exec {$this->container} sh -c 'rm {$scriptPath} && rm {$tmpPath}'"; + $this->importCommands[] = "docker exec {$this->container} sh -c 'echo \"Import finished with exit code $?\"'"; if (! empty($this->importCommands)) { @@ -135,6 +212,8 @@ public function runImport() } } catch (\Throwable $e) { return handleError($e, $this); + } finally { + $this->filename = null; } } } diff --git a/resources/views/livewire/project/database/import.blade.php b/resources/views/livewire/project/database/import.blade.php index 6210fe50b..47afe611d 100644 --- a/resources/views/livewire/project/database/import.blade.php +++ b/resources/views/livewire/project/database/import.blade.php @@ -1,7 +1,7 @@
@script -