2024-01-22 15:08:18 +00:00
< ? php
namespace App\Livewire\Project\Shared ;
2025-01-09 13:13:09 +00:00
use App\Actions\Database\StartDatabase ;
use App\Actions\Database\StopDatabase ;
use App\Actions\Service\StartService ;
use App\Actions\Service\StopService ;
use App\Jobs\VolumeCloneJob ;
2024-01-22 15:08:18 +00:00
use App\Models\Environment ;
use App\Models\Project ;
use App\Models\StandaloneDocker ;
use App\Models\SwarmDocker ;
2025-08-22 14:47:59 +00:00
use Illuminate\Foundation\Auth\Access\AuthorizesRequests ;
2024-01-22 15:08:18 +00:00
use Livewire\Component ;
use Visus\Cuid2\Cuid2 ;
class ResourceOperations extends Component
{
2025-08-22 14:47:59 +00:00
use AuthorizesRequests ;
2024-01-22 15:08:18 +00:00
public $resource ;
2024-06-10 20:43:34 +00:00
2024-01-22 15:08:18 +00:00
public $projectUuid ;
2024-06-10 20:43:34 +00:00
2024-11-22 15:03:20 +00:00
public $environmentUuid ;
2024-06-10 20:43:34 +00:00
2024-01-22 15:08:18 +00:00
public $projects ;
2024-06-10 20:43:34 +00:00
2024-01-22 15:08:18 +00:00
public $servers ;
2025-01-09 13:13:09 +00:00
public bool $cloneVolumeData = false ;
2024-01-22 15:08:18 +00:00
public function mount ()
{
$parameters = get_route_parameters ();
2024-01-31 13:22:48 +00:00
$this -> projectUuid = data_get ( $parameters , 'project_uuid' );
2024-11-22 15:03:20 +00:00
$this -> environmentUuid = data_get ( $parameters , 'environment_uuid' );
2024-01-22 15:08:18 +00:00
$this -> projects = Project :: ownedByCurrentTeam () -> get ();
2025-06-13 14:49:27 +00:00
$this -> servers = currentTeam () -> servers -> filter ( fn ( $server ) => ! $server -> isBuildServer ());
2024-01-22 15:08:18 +00:00
}
2024-06-10 20:43:34 +00:00
2025-01-09 13:13:09 +00:00
public function toggleVolumeCloning ( bool $value )
{
$this -> cloneVolumeData = $value ;
}
2025-01-08 16:26:59 +00:00
public function cloneTo ( $destination_id )
2024-01-22 15:08:18 +00:00
{
2025-08-22 14:47:59 +00:00
$this -> authorize ( 'update' , $this -> resource );
2025-01-07 14:31:43 +00:00
$new_destination = StandaloneDocker :: find ( $destination_id );
2024-06-10 20:43:34 +00:00
if ( ! $new_destination ) {
2025-01-07 14:31:43 +00:00
$new_destination = SwarmDocker :: find ( $destination_id );
2024-01-22 15:08:18 +00:00
}
2024-06-10 20:43:34 +00:00
if ( ! $new_destination ) {
2024-01-22 15:08:18 +00:00
return $this -> addError ( 'destination_id' , 'Destination not found.' );
}
2024-07-25 11:31:59 +00:00
$uuid = ( string ) new Cuid2 ;
2024-01-22 15:08:18 +00:00
$server = $new_destination -> server ;
2025-01-07 18:26:25 +00:00
2025-01-07 14:31:43 +00:00
if ( $this -> resource -> getMorphClass () === \App\Models\Application :: class ) {
2025-09-11 08:38:08 +00:00
$new_resource = clone_application ( $this -> resource , $new_destination , [ 'uuid' => $uuid ], $this -> cloneVolumeData );
2025-01-08 16:26:59 +00:00
2024-01-22 15:08:18 +00:00
$route = route ( 'project.application.configuration' , [
'project_uuid' => $this -> projectUuid ,
2024-11-22 15:03:20 +00:00
'environment_uuid' => $this -> environmentUuid ,
2024-01-22 15:08:18 +00:00
'application_uuid' => $new_resource -> uuid ,
2024-06-10 20:43:34 +00:00
]) . '#resource-operations' ;
2024-01-22 15:08:18 +00:00
return redirect () -> to ( $route );
2025-01-07 14:31:43 +00:00
} elseif (
$this -> resource -> getMorphClass () === \App\Models\StandalonePostgresql :: class ||
$this -> resource -> getMorphClass () === \App\Models\StandaloneMongodb :: class ||
$this -> resource -> getMorphClass () === \App\Models\StandaloneMysql :: class ||
$this -> resource -> getMorphClass () === \App\Models\StandaloneMariadb :: class ||
$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-07-25 11:31:59 +00:00
$uuid = ( string ) new Cuid2 ;
2025-01-07 16:18:04 +00:00
$new_resource = $this -> resource -> replicate ([
'id' ,
'created_at' ,
'updated_at' ,
]) -> fill ([
2024-01-22 15:08:18 +00:00
'uuid' => $uuid ,
2024-06-10 20:43:34 +00:00
'name' => $this -> resource -> name . '-clone-' . $uuid ,
2024-01-22 15:08:18 +00:00
'status' => 'exited' ,
'started_at' => null ,
'destination_id' => $new_destination -> id ,
]);
$new_resource -> save ();
2025-01-08 16:26:59 +00:00
$tags = $this -> resource -> tags ;
foreach ( $tags as $tag ) {
$new_resource -> tags () -> attach ( $tag -> id );
}
$new_resource -> persistentStorages () -> delete ();
$persistentVolumes = $this -> resource -> persistentStorages () -> get ();
foreach ( $persistentVolumes as $volume ) {
$originalName = $volume -> name ;
$newName = '' ;
if ( str_starts_with ( $originalName , 'postgres-data-' )) {
$newName = 'postgres-data-' . $new_resource -> uuid ;
} elseif ( str_starts_with ( $originalName , 'mysql-data-' )) {
$newName = 'mysql-data-' . $new_resource -> uuid ;
} elseif ( str_starts_with ( $originalName , 'redis-data-' )) {
$newName = 'redis-data-' . $new_resource -> uuid ;
} elseif ( str_starts_with ( $originalName , 'clickhouse-data-' )) {
$newName = 'clickhouse-data-' . $new_resource -> uuid ;
} elseif ( str_starts_with ( $originalName , 'mariadb-data-' )) {
$newName = 'mariadb-data-' . $new_resource -> uuid ;
} elseif ( str_starts_with ( $originalName , 'mongodb-data-' )) {
$newName = 'mongodb-data-' . $new_resource -> uuid ;
} elseif ( str_starts_with ( $originalName , 'keydb-data-' )) {
$newName = 'keydb-data-' . $new_resource -> uuid ;
} elseif ( str_starts_with ( $originalName , 'dragonfly-data-' )) {
$newName = 'dragonfly-data-' . $new_resource -> uuid ;
} else {
2025-01-09 13:13:09 +00:00
if ( str_starts_with ( $volume -> name , $this -> resource -> uuid )) {
$newName = str ( $volume -> name ) -> replace ( $this -> resource -> uuid , $new_resource -> uuid );
} else {
$newName = $new_resource -> uuid . '-' . $volume -> name ;
}
2025-01-08 16:26:59 +00:00
}
$newPersistentVolume = $volume -> replicate ([
'id' ,
'created_at' ,
'updated_at' ,
]) -> fill ([
'name' => $newName ,
'resource_id' => $new_resource -> id ,
]);
$newPersistentVolume -> save ();
2025-01-09 13:13:09 +00:00
if ( $this -> cloneVolumeData ) {
try {
StopDatabase :: dispatch ( $this -> resource );
$sourceVolume = $volume -> name ;
$targetVolume = $newPersistentVolume -> name ;
2025-01-13 10:13:35 +00:00
$sourceServer = $this -> resource -> destination -> server ;
$targetServer = $new_resource -> destination -> server ;
2025-01-09 13:13:09 +00:00
2025-01-13 10:13:35 +00:00
VolumeCloneJob :: dispatch ( $sourceVolume , $targetVolume , $sourceServer , $targetServer , $newPersistentVolume );
2025-01-09 13:13:09 +00:00
StartDatabase :: dispatch ( $this -> resource );
} catch ( \Exception $e ) {
\Log :: error ( 'Failed to copy volume data for ' . $volume -> name . ': ' . $e -> getMessage ());
}
}
2025-01-08 16:26:59 +00:00
}
$fileStorages = $this -> resource -> fileStorages () -> get ();
foreach ( $fileStorages as $storage ) {
$newStorage = $storage -> replicate ([
'id' ,
'created_at' ,
'updated_at' ,
]) -> fill ([
'resource_id' => $new_resource -> id ,
]);
$newStorage -> save ();
}
$scheduledBackups = $this -> resource -> scheduledBackups () -> get ();
foreach ( $scheduledBackups as $backup ) {
$uuid = ( string ) new Cuid2 ;
$newBackup = $backup -> replicate ([
'id' ,
'created_at' ,
'updated_at' ,
]) -> fill ([
'uuid' => $uuid ,
'database_id' => $new_resource -> id ,
'database_type' => $new_resource -> getMorphClass (),
'team_id' => currentTeam () -> id ,
]);
$newBackup -> save ();
}
2024-01-22 15:08:18 +00:00
$environmentVaribles = $this -> resource -> environment_variables () -> get ();
foreach ( $environmentVaribles as $environmentVarible ) {
2025-01-08 16:26:59 +00:00
$payload = [
'resourceable_id' => $new_resource -> id ,
'resourceable_type' => $new_resource -> getMorphClass (),
];
2025-01-07 16:18:04 +00:00
$newEnvironmentVariable = $environmentVarible -> replicate ([
'id' ,
'created_at' ,
'updated_at' ,
]) -> fill ( $payload );
2024-01-22 15:08:18 +00:00
$newEnvironmentVariable -> save ();
}
2025-01-08 16:26:59 +00:00
2024-01-22 15:08:18 +00:00
$route = route ( 'project.database.configuration' , [
'project_uuid' => $this -> projectUuid ,
2024-11-22 15:03:20 +00:00
'environment_uuid' => $this -> environmentUuid ,
2024-01-22 15:08:18 +00:00
'database_uuid' => $new_resource -> uuid ,
2024-06-10 20:43:34 +00:00
]) . '#resource-operations' ;
2024-01-22 15:08:18 +00:00
return redirect () -> to ( $route );
2025-01-07 14:31:43 +00:00
} elseif ( $this -> resource -> type () === 'service' ) {
2024-07-25 11:31:59 +00:00
$uuid = ( string ) new Cuid2 ;
2025-01-07 16:18:04 +00:00
$new_resource = $this -> resource -> replicate ([
'id' ,
'created_at' ,
'updated_at' ,
]) -> fill ([
2024-01-22 15:08:18 +00:00
'uuid' => $uuid ,
2024-06-10 20:43:34 +00:00
'name' => $this -> resource -> name . '-clone-' . $uuid ,
2024-01-22 15:08:18 +00:00
'destination_id' => $new_destination -> id ,
2025-01-07 18:26:25 +00:00
'destination_type' => $new_destination -> getMorphClass (),
2025-01-09 13:13:09 +00:00
'server_id' => $new_destination -> server_id , // server_id is probably not needed anymore because of the new polymorphic relationships (here it is needed for clone to a different server to work - but maybe we can drop the column)
2024-01-22 15:08:18 +00:00
]);
2025-01-07 18:26:25 +00:00
2024-01-22 15:08:18 +00:00
$new_resource -> save ();
2025-01-08 16:26:59 +00:00
$tags = $this -> resource -> tags ;
foreach ( $tags as $tag ) {
$new_resource -> tags () -> attach ( $tag -> id );
}
$scheduledTasks = $this -> resource -> scheduled_tasks () -> get ();
foreach ( $scheduledTasks as $task ) {
$newTask = $task -> replicate ([
'id' ,
'created_at' ,
'updated_at' ,
]) -> fill ([
'uuid' => ( string ) new Cuid2 ,
'service_id' => $new_resource -> id ,
'team_id' => currentTeam () -> id ,
]);
$newTask -> save ();
}
$environmentVariables = $this -> resource -> environment_variables () -> get ();
foreach ( $environmentVariables as $environmentVariable ) {
$newEnvironmentVariable = $environmentVariable -> replicate ([
'id' ,
'created_at' ,
'updated_at' ,
]) -> fill ([
'resourceable_id' => $new_resource -> id ,
'resourceable_type' => $new_resource -> getMorphClass (),
]);
$newEnvironmentVariable -> save ();
}
2024-01-22 15:08:18 +00:00
foreach ( $new_resource -> applications () as $application ) {
$application -> update ([
'status' => 'exited' ,
]);
2025-01-09 13:13:09 +00:00
$persistentVolumes = $application -> persistentStorages () -> get ();
foreach ( $persistentVolumes as $volume ) {
$newName = '' ;
if ( str_starts_with ( $volume -> name , $volume -> resource -> uuid )) {
$newName = str ( $volume -> name ) -> replace ( $volume -> resource -> uuid , $application -> uuid );
} else {
$newName = $application -> uuid . '-' . str ( $volume -> name ) -> afterLast ( '-' );
}
$newPersistentVolume = $volume -> replicate ([
'id' ,
'created_at' ,
'updated_at' ,
]) -> fill ([
'name' => $newName ,
'resource_id' => $application -> id ,
]);
$newPersistentVolume -> save ();
if ( $this -> cloneVolumeData ) {
try {
2025-08-04 20:12:56 +00:00
StopService :: dispatch ( $application );
2025-01-09 13:13:09 +00:00
$sourceVolume = $volume -> name ;
$targetVolume = $newPersistentVolume -> name ;
2025-01-13 10:13:35 +00:00
$sourceServer = $application -> service -> destination -> server ;
$targetServer = $new_resource -> destination -> server ;
2025-01-09 13:13:09 +00:00
2025-01-13 10:13:35 +00:00
VolumeCloneJob :: dispatch ( $sourceVolume , $targetVolume , $sourceServer , $targetServer , $newPersistentVolume );
2025-01-09 13:13:09 +00:00
StartService :: dispatch ( $application );
} catch ( \Exception $e ) {
\Log :: error ( 'Failed to copy volume data for ' . $volume -> name . ': ' . $e -> getMessage ());
}
}
}
2024-01-22 15:08:18 +00:00
}
2025-01-08 16:26:59 +00:00
2024-01-22 15:08:18 +00:00
foreach ( $new_resource -> databases () as $database ) {
$database -> update ([
'status' => 'exited' ,
]);
2025-01-09 13:13:09 +00:00
$persistentVolumes = $database -> persistentStorages () -> get ();
foreach ( $persistentVolumes as $volume ) {
$newName = '' ;
if ( str_starts_with ( $volume -> name , $volume -> resource -> uuid )) {
$newName = str ( $volume -> name ) -> replace ( $volume -> resource -> uuid , $database -> uuid );
} else {
$newName = $database -> uuid . '-' . str ( $volume -> name ) -> afterLast ( '-' );
}
$newPersistentVolume = $volume -> replicate ([
'id' ,
'created_at' ,
'updated_at' ,
]) -> fill ([
'name' => $newName ,
'resource_id' => $database -> id ,
]);
$newPersistentVolume -> save ();
if ( $this -> cloneVolumeData ) {
try {
2025-08-04 20:12:56 +00:00
StopService :: dispatch ( $database -> service );
2025-01-09 13:13:09 +00:00
$sourceVolume = $volume -> name ;
$targetVolume = $newPersistentVolume -> name ;
2025-01-13 10:13:35 +00:00
$sourceServer = $database -> service -> destination -> server ;
$targetServer = $new_resource -> destination -> server ;
2025-01-09 13:13:09 +00:00
2025-01-13 10:13:35 +00:00
VolumeCloneJob :: dispatch ( $sourceVolume , $targetVolume , $sourceServer , $targetServer , $newPersistentVolume );
2025-01-09 13:13:09 +00:00
StartService :: dispatch ( $database -> service );
} catch ( \Exception $e ) {
\Log :: error ( 'Failed to copy volume data for ' . $volume -> name . ': ' . $e -> getMessage ());
}
}
}
2024-01-22 15:08:18 +00:00
}
2025-01-08 16:26:59 +00:00
2024-01-22 15:08:18 +00:00
$new_resource -> parse ();
2025-01-08 16:26:59 +00:00
2024-01-22 15:08:18 +00:00
$route = route ( 'project.service.configuration' , [
'project_uuid' => $this -> projectUuid ,
2024-11-22 15:03:20 +00:00
'environment_uuid' => $this -> environmentUuid ,
2024-01-22 15:08:18 +00:00
'service_uuid' => $new_resource -> uuid ,
2024-06-10 20:43:34 +00:00
]) . '#resource-operations' ;
2024-01-22 15:08:18 +00:00
return redirect () -> to ( $route );
}
}
2024-06-10 20:43:34 +00:00
2024-01-22 15:08:18 +00:00
public function moveTo ( $environment_id )
{
try {
2025-08-22 14:47:59 +00:00
$this -> authorize ( 'update' , $this -> resource );
2025-01-07 14:31:43 +00:00
$new_environment = Environment :: findOrFail ( $environment_id );
2024-01-22 15:08:18 +00:00
$this -> resource -> update ([
2024-06-10 20:43:34 +00:00
'environment_id' => $environment_id ,
2024-01-22 15:08:18 +00:00
]);
if ( $this -> resource -> type () === 'application' ) {
$route = route ( 'project.application.configuration' , [
'project_uuid' => $new_environment -> project -> uuid ,
2024-11-22 15:03:20 +00:00
'environment_uuid' => $new_environment -> uuid ,
2024-01-22 15:08:18 +00:00
'application_uuid' => $this -> resource -> uuid ,
2024-06-10 20:43:34 +00:00
]) . '#resource-operations' ;
2024-01-22 15:08:18 +00:00
return redirect () -> to ( $route );
2025-01-07 14:31:43 +00:00
} elseif ( str ( $this -> resource -> type ()) -> startsWith ( 'standalone-' )) {
2024-01-22 15:08:18 +00:00
$route = route ( 'project.database.configuration' , [
'project_uuid' => $new_environment -> project -> uuid ,
2024-11-22 15:03:20 +00:00
'environment_uuid' => $new_environment -> uuid ,
2024-01-22 15:08:18 +00:00
'database_uuid' => $this -> resource -> uuid ,
2024-06-10 20:43:34 +00:00
]) . '#resource-operations' ;
2024-01-22 15:08:18 +00:00
return redirect () -> to ( $route );
2025-01-07 14:31:43 +00:00
} elseif ( $this -> resource -> type () === 'service' ) {
2024-01-22 15:08:18 +00:00
$route = route ( 'project.service.configuration' , [
'project_uuid' => $new_environment -> project -> uuid ,
2024-11-22 15:03:20 +00:00
'environment_uuid' => $new_environment -> uuid ,
2024-01-22 15:08:18 +00:00
'service_uuid' => $this -> resource -> uuid ,
2024-06-10 20:43:34 +00:00
]) . '#resource-operations' ;
2024-01-22 15:08:18 +00:00
return redirect () -> to ( $route );
}
2025-01-07 14:31:43 +00:00
} catch ( \Throwable $e ) {
2024-01-22 15:08:18 +00:00
return handleError ( $e , $this );
}
}
2024-06-10 20:43:34 +00:00
2024-01-22 15:08:18 +00:00
public function render ()
{
return view ( 'livewire.project.shared.resource-operations' );
}
}