mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-16 20:49:28 +00:00
Feat: more conformations and fixes
This commit is contained in:
@@ -17,7 +17,6 @@ class StopService
|
|||||||
if (!$server->isFunctional()) {
|
if (!$server->isFunctional()) {
|
||||||
return 'Server is not functional';
|
return 'Server is not functional';
|
||||||
}
|
}
|
||||||
ray('Stopping service: ' . $service->name);
|
|
||||||
|
|
||||||
$containersToStop = $service->getContainersToStop();
|
$containersToStop = $service->getContainersToStop();
|
||||||
$service->stopContainers($containersToStop, $server);
|
$service->stopContainers($containersToStop, $server);
|
||||||
|
|||||||
@@ -92,6 +92,9 @@ class DeleteResourceJob implements ShouldBeEncrypted, ShouldQueue
|
|||||||
throw $e;
|
throw $e;
|
||||||
} finally {
|
} finally {
|
||||||
$this->resource->forceDelete();
|
$this->resource->forceDelete();
|
||||||
|
if ($this->dockerCleanup) {
|
||||||
|
CleanupDocker::run($server, true);
|
||||||
|
}
|
||||||
Artisan::queue('cleanup:stucked-resources');
|
Artisan::queue('cleanup:stucked-resources');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,25 @@ namespace App\Livewire;
|
|||||||
|
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
|
||||||
class NavbarDeleteTeam extends Component
|
class NavbarDeleteTeam extends Component
|
||||||
{
|
{
|
||||||
public function delete()
|
public $team;
|
||||||
|
|
||||||
|
public function mount()
|
||||||
{
|
{
|
||||||
|
$this->team = currentTeam()->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete($password)
|
||||||
|
{
|
||||||
|
if (!Hash::check($password, Auth::user()->password)) {
|
||||||
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$currentTeam = currentTeam();
|
$currentTeam = currentTeam();
|
||||||
$currentTeam->delete();
|
$currentTeam->delete();
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ use Illuminate\Support\Collection;
|
|||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Spatie\Url\Url;
|
use Spatie\Url\Url;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
use Illuminate\Process\InvokedProcess;
|
||||||
|
use Illuminate\Support\Facades\Process;
|
||||||
|
|
||||||
class Previews extends Component
|
class Previews extends Component
|
||||||
{
|
{
|
||||||
@@ -177,17 +179,20 @@ class Previews extends Component
|
|||||||
public function stop(int $pull_request_id)
|
public function stop(int $pull_request_id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$server = $this->application->destination->server;
|
||||||
|
$timeout = 300;
|
||||||
|
|
||||||
if ($this->application->destination->server->isSwarm()) {
|
if ($this->application->destination->server->isSwarm()) {
|
||||||
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $this->application->destination->server);
|
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $server);
|
||||||
} else {
|
} else {
|
||||||
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id, $pull_request_id);
|
$containers = getCurrentApplicationContainerStatus($server, $this->application->id, $pull_request_id)->toArray();
|
||||||
foreach ($containers as $container) {
|
$this->stopContainers($containers, $server, $timeout);
|
||||||
$name = str_replace('/', '', $container['Names']);
|
|
||||||
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
GetContainersStatus::dispatchSync($this->application->destination->server)->onQueue('high');
|
|
||||||
$this->dispatch('reloadWindow');
|
GetContainersStatus::run($server);
|
||||||
|
$this->application->refresh();
|
||||||
|
$this->dispatch('containerStatusUpdated');
|
||||||
|
$this->dispatch('success', 'Preview Deployment stopped.');
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
@@ -196,16 +201,21 @@ class Previews extends Component
|
|||||||
public function delete(int $pull_request_id)
|
public function delete(int $pull_request_id)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
$server = $this->application->destination->server;
|
||||||
|
$timeout = 300;
|
||||||
|
|
||||||
if ($this->application->destination->server->isSwarm()) {
|
if ($this->application->destination->server->isSwarm()) {
|
||||||
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $this->application->destination->server);
|
instant_remote_process(["docker stack rm {$this->application->uuid}-{$pull_request_id}"], $server);
|
||||||
} else {
|
} else {
|
||||||
$containers = getCurrentApplicationContainerStatus($this->application->destination->server, $this->application->id, $pull_request_id);
|
$containers = getCurrentApplicationContainerStatus($server, $this->application->id, $pull_request_id)->toArray();
|
||||||
foreach ($containers as $container) {
|
$this->stopContainers($containers, $server, $timeout);
|
||||||
$name = str_replace('/', '', $container['Names']);
|
|
||||||
instant_remote_process(["docker rm -f $name"], $this->application->destination->server, throwError: false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ApplicationPreview::where('application_id', $this->application->id)->where('pull_request_id', $pull_request_id)->first()->delete();
|
|
||||||
|
ApplicationPreview::where('application_id', $this->application->id)
|
||||||
|
->where('pull_request_id', $pull_request_id)
|
||||||
|
->first()
|
||||||
|
->delete();
|
||||||
|
|
||||||
$this->application->refresh();
|
$this->application->refresh();
|
||||||
$this->dispatch('update_links');
|
$this->dispatch('update_links');
|
||||||
$this->dispatch('success', 'Preview deleted.');
|
$this->dispatch('success', 'Preview deleted.');
|
||||||
@@ -213,4 +223,49 @@ class Previews extends Component
|
|||||||
return handleError($e, $this);
|
return handleError($e, $this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function stopContainers(array $containers, $server, int $timeout)
|
||||||
|
{
|
||||||
|
$processes = [];
|
||||||
|
foreach ($containers as $container) {
|
||||||
|
$containerName = str_replace('/', '', $container['Names']);
|
||||||
|
$processes[$containerName] = $this->stopContainer($containerName, $timeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
$startTime = time();
|
||||||
|
while (count($processes) > 0) {
|
||||||
|
$finishedProcesses = array_filter($processes, function ($process) {
|
||||||
|
return !$process->running();
|
||||||
|
});
|
||||||
|
foreach (array_keys($finishedProcesses) as $containerName) {
|
||||||
|
unset($processes[$containerName]);
|
||||||
|
$this->removeContainer($containerName, $server);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (time() - $startTime >= $timeout) {
|
||||||
|
$this->forceStopRemainingContainers(array_keys($processes), $server);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep(100000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function stopContainer(string $containerName, int $timeout): InvokedProcess
|
||||||
|
{
|
||||||
|
return Process::timeout($timeout)->start("docker stop --time=$timeout $containerName");
|
||||||
|
}
|
||||||
|
|
||||||
|
private function removeContainer(string $containerName, $server)
|
||||||
|
{
|
||||||
|
instant_remote_process(["docker rm -f $containerName"], $server, throwError: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function forceStopRemainingContainers(array $containerNames, $server)
|
||||||
|
{
|
||||||
|
foreach ($containerNames as $containerName) {
|
||||||
|
instant_remote_process(["docker kill $containerName"], $server, throwError: false);
|
||||||
|
$this->removeContainer($containerName, $server);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ use App\Models\StandaloneMysql;
|
|||||||
use App\Models\StandalonePostgresql;
|
use App\Models\StandalonePostgresql;
|
||||||
use App\Models\StandaloneRedis;
|
use App\Models\StandaloneRedis;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class FileStorage extends Component
|
class FileStorage extends Component
|
||||||
{
|
{
|
||||||
@@ -77,8 +79,13 @@ class FileStorage extends Component
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete()
|
public function delete($password)
|
||||||
{
|
{
|
||||||
|
if (!Hash::check($password, Auth::user()->password)) {
|
||||||
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$message = 'File deleted.';
|
$message = 'File deleted.';
|
||||||
if ($this->fileStorage->is_directory) {
|
if ($this->fileStorage->is_directory) {
|
||||||
@@ -121,8 +128,15 @@ class FileStorage extends Component
|
|||||||
$this->submit();
|
$this->submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function render()
|
public function render()
|
||||||
{
|
{
|
||||||
return view('livewire.project.service.file-storage');
|
return view('livewire.project.service.file-storage', [
|
||||||
|
'directoryDeletionCheckboxes' => [
|
||||||
|
['id' => 'permanently_delete', 'label' => 'The selected directory and all its contents will be permantely deleted form the server.'],
|
||||||
|
],
|
||||||
|
'fileDeletionCheckboxes' => [
|
||||||
|
['id' => 'permanently_delete', 'label' => 'The selected file will be permanently deleted form the server.'],
|
||||||
|
]
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ namespace App\Livewire\Project\Service;
|
|||||||
|
|
||||||
use App\Models\ServiceApplication;
|
use App\Models\ServiceApplication;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class ServiceApplicationView extends Component
|
class ServiceApplicationView extends Component
|
||||||
{
|
{
|
||||||
@@ -11,6 +13,10 @@ class ServiceApplicationView extends Component
|
|||||||
|
|
||||||
public $parameters;
|
public $parameters;
|
||||||
|
|
||||||
|
public $docker_cleanup = true;
|
||||||
|
public $delete_volumes = true;
|
||||||
|
|
||||||
|
|
||||||
protected $rules = [
|
protected $rules = [
|
||||||
'application.human_name' => 'nullable',
|
'application.human_name' => 'nullable',
|
||||||
'application.description' => 'nullable',
|
'application.description' => 'nullable',
|
||||||
@@ -23,12 +29,7 @@ class ServiceApplicationView extends Component
|
|||||||
'application.is_stripprefix_enabled' => 'nullable|boolean',
|
'application.is_stripprefix_enabled' => 'nullable|boolean',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function render()
|
public function updatedApplicationFqdn()
|
||||||
{
|
|
||||||
return view('livewire.project.service.service-application-view');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updatedApplicationFqdn()
|
|
||||||
{
|
{
|
||||||
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
$this->application->fqdn = str($this->application->fqdn)->replaceEnd(',', '')->trim();
|
||||||
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
$this->application->fqdn = str($this->application->fqdn)->replaceStart(',', '')->trim();
|
||||||
@@ -56,8 +57,13 @@ class ServiceApplicationView extends Component
|
|||||||
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
$this->dispatch('success', 'You need to restart the service for the changes to take effect.');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete()
|
public function delete($password)
|
||||||
{
|
{
|
||||||
|
if (!Hash::check($password, Auth::user()->password)) {
|
||||||
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->application->delete();
|
$this->application->delete();
|
||||||
$this->dispatch('success', 'Application deleted.');
|
$this->dispatch('success', 'Application deleted.');
|
||||||
@@ -91,4 +97,17 @@ class ServiceApplicationView extends Component
|
|||||||
$this->dispatch('generateDockerCompose');
|
$this->dispatch('generateDockerCompose');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function render()
|
||||||
|
{
|
||||||
|
return view('livewire.project.service.service-application-view', [
|
||||||
|
'checkboxes' => [
|
||||||
|
['id' => 'delete_volumes', 'label' => 'All associated volumes with this resource will be permanently deleted'],
|
||||||
|
['id' => 'docker_cleanup', 'label' => 'Docker cleanup will be run on the server which removes builder cache and unused images'],
|
||||||
|
// ['id' => 'delete_associated_backups_locally', 'label' => 'All backups associated with this Ressource will be permanently deleted from local storage.'],
|
||||||
|
// ['id' => 'delete_associated_backups_s3', 'label' => 'All backups associated with this Ressource will be permanently deleted from the selected S3 Storage.'],
|
||||||
|
// ['id' => 'delete_associated_backups_sftp', 'label' => 'All backups associated with this Ressource will be permanently deleted from the selected SFTP Storage.']
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ use App\Models\Server;
|
|||||||
use App\Models\StandaloneDocker;
|
use App\Models\StandaloneDocker;
|
||||||
use Livewire\Component;
|
use Livewire\Component;
|
||||||
use Visus\Cuid2\Cuid2;
|
use Visus\Cuid2\Cuid2;
|
||||||
|
use Illuminate\Support\Facades\Hash;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class Destination extends Component
|
class Destination extends Component
|
||||||
{
|
{
|
||||||
@@ -115,8 +117,13 @@ class Destination extends Component
|
|||||||
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
ApplicationStatusChanged::dispatch(data_get($this->resource, 'environment.project.team.id'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function removeServer(int $network_id, int $server_id)
|
public function removeServer(int $network_id, int $server_id, $password)
|
||||||
{
|
{
|
||||||
|
if (!Hash::check($password, Auth::user()->password)) {
|
||||||
|
$this->addError('password', 'The provided password is incorrect.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->resource->destination->server->id == $server_id && $this->resource->destination->id == $network_id) {
|
if ($this->resource->destination->server->id == $server_id && $this->resource->destination->id == $network_id) {
|
||||||
$this->dispatch('error', 'You cannot remove this destination server.', 'You are trying to remove the main server.');
|
$this->dispatch('error', 'You cannot remove this destination server.', 'You are trying to remove the main server.');
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ class Show extends Component
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test deletion in more detail
|
|
||||||
$this->storage->delete();
|
$this->storage->delete();
|
||||||
$this->dispatch('refreshStorages');
|
$this->dispatch('refreshStorages');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
<div>
|
<div>
|
||||||
<x-modal-confirmation buttonFullWidth isErrorButton buttonTitle="Delete Team">
|
<x-modal-confirmation
|
||||||
This team be deleted. It is not reversible. <br>Please think again.
|
title="Confirm Team Deletion?"
|
||||||
</x-modal-confirmation>
|
buttonTitle="Delete Team"
|
||||||
|
isErrorButton
|
||||||
|
submitAction="delete"
|
||||||
|
:actions="['The current Team will be permanently deleted.']"
|
||||||
|
confirmationText="{{ $team }}"
|
||||||
|
confirmationLabel="Please confirm the execution of the actions by entering the Team Name below"
|
||||||
|
shortConfirmationLabel="Team Name"
|
||||||
|
step3ButtonText="Permanently Delete Team"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -160,8 +160,6 @@
|
|||||||
:confirmWithText="false"
|
:confirmWithText="false"
|
||||||
:confirmWithPassword="false"
|
:confirmWithPassword="false"
|
||||||
step2ButtonText="Stop Preview Deployment"
|
step2ButtonText="Stop Preview Deployment"
|
||||||
:dispatchEvent="true"
|
|
||||||
dispatchEventType="stopEvent"
|
|
||||||
>
|
>
|
||||||
<x-slot:customButton>
|
<x-slot:customButton>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error"
|
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5 text-error"
|
||||||
@@ -189,9 +187,6 @@
|
|||||||
confirmationLabel="Please confirm the execution of the actions by entering the Preview Deployment name below"
|
confirmationLabel="Please confirm the execution of the actions by entering the Preview Deployment name below"
|
||||||
shortConfirmationLabel="Preview Deployment Name"
|
shortConfirmationLabel="Preview Deployment Name"
|
||||||
:confirmWithPassword="false"
|
:confirmWithPassword="false"
|
||||||
:dispatchEvent="true"
|
|
||||||
dispatchEventType="deleteEvent"
|
|
||||||
dispatchEventMessage="Preview Deployment Deleted"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,12 +7,20 @@
|
|||||||
<h2>{{ Str::headline($application->name) }}</h2>
|
<h2>{{ Str::headline($application->name) }}</h2>
|
||||||
@endif
|
@endif
|
||||||
<x-forms.button type="submit">Save</x-forms.button>
|
<x-forms.button type="submit">Save</x-forms.button>
|
||||||
<x-modal-confirmation isErrorButton>
|
<x-modal-confirmation
|
||||||
<x-slot:button-title>
|
title="Confirm Service Application Deletion?"
|
||||||
Delete
|
buttonTitle="Delete"
|
||||||
</x-slot:button-title>
|
isErrorButton
|
||||||
This will delete this service application. It is not reversible.<br>Please think again.
|
submitAction="delete"
|
||||||
</x-modal-confirmation>
|
{{-- :checkboxes="$checkboxes" --}}
|
||||||
|
:actions="[
|
||||||
|
'The selected service application container will be stopped and permanently deleted.'
|
||||||
|
]"
|
||||||
|
confirmationText="{{ Str::headline($application->name) }}"
|
||||||
|
confirmationLabel="Please confirm the execution of the actions by entering the Service Application Name below"
|
||||||
|
shortConfirmationLabel="Service Application Name"
|
||||||
|
step3ButtonText="Permanently Delete Service Application"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
|
|||||||
@@ -66,11 +66,16 @@
|
|||||||
wire:click="stop('{{ data_get($destination, 'server.id') }}')">Stop</x-forms.button>
|
wire:click="stop('{{ data_get($destination, 'server.id') }}')">Stop</x-forms.button>
|
||||||
@endif
|
@endif
|
||||||
<x-modal-confirmation
|
<x-modal-confirmation
|
||||||
action="removeServer({{ data_get($destination, 'id') }},{{ data_get($destination, 'server.id') }})"
|
title="Confirm server removal?"
|
||||||
isErrorButton buttonTitle="Remove Server">
|
isErrorButton
|
||||||
This will stop the running application in this server and remove it as a deployment
|
buttonTitle="Remove Server"
|
||||||
destination.<br><br>Please think again.
|
submitAction="removeServer({{ data_get($destination, 'id') }},{{ data_get($destination, 'server.id') }})"
|
||||||
</x-modal-confirmation>
|
:actions="['This will stop the all running applications on this server and remove it as a deployment destination.']"
|
||||||
|
confirmationText="{{ data_get($destination, 'server.name') }}"
|
||||||
|
confirmationLabel="Please confirm the execution of the actions by entering the Server Name below"
|
||||||
|
shortConfirmationLabel="Server Name"
|
||||||
|
step3ButtonText="Permanently Remove Server"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endforeach
|
@endforeach
|
||||||
|
|||||||
Reference in New Issue
Block a user