diff --git a/app/Actions/Database/StartClickhouse.php b/app/Actions/Database/StartClickhouse.php index 1e8616416..13667e829 100644 --- a/app/Actions/Database/StartClickhouse.php +++ b/app/Actions/Database/StartClickhouse.php @@ -99,8 +99,8 @@ class StartClickhouse } // Add custom docker run options - $docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options); - $docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); + $docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options); + $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = Yaml::dump($docker_compose, 10); $docker_compose_base64 = base64_encode($docker_compose); diff --git a/app/Actions/Database/StartDatabase.php b/app/Actions/Database/StartDatabase.php index 73b2f9ac0..869a88521 100644 --- a/app/Actions/Database/StartDatabase.php +++ b/app/Actions/Database/StartDatabase.php @@ -49,7 +49,7 @@ class StartDatabase break; } if ($database->is_public && $database->public_port) { - StartDatabaseProxy::dispatch($database); + StartDatabaseProxy::dispatch($database)->onQueue('high'); } return $activity; diff --git a/app/Actions/Database/StartDragonfly.php b/app/Actions/Database/StartDragonfly.php index 6274737a1..c72714e1c 100644 --- a/app/Actions/Database/StartDragonfly.php +++ b/app/Actions/Database/StartDragonfly.php @@ -96,8 +96,8 @@ class StartDragonfly } // Add custom docker run options - $docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options); - $docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); + $docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options); + $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = Yaml::dump($docker_compose, 10); $docker_compose_base64 = base64_encode($docker_compose); diff --git a/app/Actions/Database/StartKeydb.php b/app/Actions/Database/StartKeydb.php index 734b28322..bd98258ab 100644 --- a/app/Actions/Database/StartKeydb.php +++ b/app/Actions/Database/StartKeydb.php @@ -107,8 +107,8 @@ class StartKeydb } // Add custom docker run options - $docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options); - $docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); + $docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options); + $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = Yaml::dump($docker_compose, 10); $docker_compose_base64 = base64_encode($docker_compose); $this->commands[] = "echo '{$docker_compose_base64}' | base64 -d | tee $this->configuration_dir/docker-compose.yml > /dev/null"; diff --git a/app/Actions/Database/StartMariadb.php b/app/Actions/Database/StartMariadb.php index aa6b79126..696dd7ff4 100644 --- a/app/Actions/Database/StartMariadb.php +++ b/app/Actions/Database/StartMariadb.php @@ -101,8 +101,8 @@ class StartMariadb } // Add custom docker run options - $docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options); - $docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); + $docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options); + $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = Yaml::dump($docker_compose, 10); $docker_compose_base64 = base64_encode($docker_compose); diff --git a/app/Actions/Database/StartMongodb.php b/app/Actions/Database/StartMongodb.php index 8d0a6b3ca..6d67bdde6 100644 --- a/app/Actions/Database/StartMongodb.php +++ b/app/Actions/Database/StartMongodb.php @@ -117,8 +117,8 @@ class StartMongodb ]; // Add custom docker run options - $docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options); - $docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); + $docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options); + $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = Yaml::dump($docker_compose, 10); $docker_compose_base64 = base64_encode($docker_compose); diff --git a/app/Actions/Database/StartMysql.php b/app/Actions/Database/StartMysql.php index 0339501b0..a3694648f 100644 --- a/app/Actions/Database/StartMysql.php +++ b/app/Actions/Database/StartMysql.php @@ -101,8 +101,8 @@ class StartMysql } // Add custom docker run options - $docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options); - $docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); + $docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options); + $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = Yaml::dump($docker_compose, 10); $docker_compose_base64 = base64_encode($docker_compose); diff --git a/app/Actions/Database/StartPostgresql.php b/app/Actions/Database/StartPostgresql.php index 5c956acbc..f5e85087f 100644 --- a/app/Actions/Database/StartPostgresql.php +++ b/app/Actions/Database/StartPostgresql.php @@ -122,8 +122,8 @@ class StartPostgresql ]; } // Add custom docker run options - $docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options); - $docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); + $docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options); + $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = Yaml::dump($docker_compose, 10); $docker_compose_base64 = base64_encode($docker_compose); diff --git a/app/Actions/Database/StartRedis.php b/app/Actions/Database/StartRedis.php index e445a246e..7a2d2b34d 100644 --- a/app/Actions/Database/StartRedis.php +++ b/app/Actions/Database/StartRedis.php @@ -110,8 +110,8 @@ class StartRedis } // Add custom docker run options - $docker_run_options = convert_docker_run_to_compose($this->database->custom_docker_run_options); - $docker_compose = generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); + $docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options); + $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network); $docker_compose = Yaml::dump($docker_compose, 10); $docker_compose_base64 = base64_encode($docker_compose); diff --git a/app/Actions/Server/InstallDocker.php b/app/Actions/Server/InstallDocker.php index fd4dd150c..ba6c23ffc 100644 --- a/app/Actions/Server/InstallDocker.php +++ b/app/Actions/Server/InstallDocker.php @@ -12,11 +12,11 @@ class InstallDocker public function handle(Server $server) { + $dockerVersion = config('constants.docker_install_version'); $supported_os_type = $server->validateOS(); if (! $supported_os_type) { throw new \Exception('Server OS type is not supported for automated installation. Please install Docker manually before continuing: documentation.'); } - $dockerVersion = '26.0'; $config = base64_encode('{ "log-driver": "json-file", "log-opts": { diff --git a/app/Actions/Server/ServerCheck.php b/app/Actions/Server/ServerCheck.php index f61422807..d9de1464e 100644 --- a/app/Actions/Server/ServerCheck.php +++ b/app/Actions/Server/ServerCheck.php @@ -130,10 +130,10 @@ class ServerCheck if ($foundLogDrainContainer) { $status = data_get($foundLogDrainContainer, 'State.Status'); if ($status !== 'running') { - StartLogDrain::dispatch($this->server); + StartLogDrain::dispatch($this->server)->onQueue('high'); } } else { - StartLogDrain::dispatch($this->server); + StartLogDrain::dispatch($this->server)->onQueue('high'); } } diff --git a/app/Console/Commands/Init.php b/app/Console/Commands/Init.php index 8d4f51d1c..c802fb116 100644 --- a/app/Console/Commands/Init.php +++ b/app/Console/Commands/Init.php @@ -39,7 +39,7 @@ class Init extends Command } // Backward compatibility - $this->disable_metrics(); + // $this->disable_metrics(); $this->replace_slash_in_environment_name(); $this->restore_coolify_db_backup(); $this->update_user_emails(); @@ -80,19 +80,19 @@ class Init extends Command } } - private function disable_metrics() - { - if (version_compare('4.0.0-beta.312', config('version'), '<=')) { - foreach ($this->servers as $server) { - if ($server->settings->is_metrics_enabled === true) { - $server->settings->update(['is_metrics_enabled' => false]); - } - if ($server->isFunctional()) { - StopSentinel::dispatch($server); - } - } - } - } + // private function disable_metrics() + // { + // if (version_compare('4.0.0-beta.312', config('version'), '<=')) { + // foreach ($this->servers as $server) { + // if ($server->settings->is_metrics_enabled === true) { + // $server->settings->update(['is_metrics_enabled' => false]); + // } + // if ($server->isFunctional()) { + // StopSentinel::dispatch($server)->onQueue('high'); + // } + // } + // } + // } private function update_user_emails() { diff --git a/app/Console/Commands/ServicesDelete.php b/app/Console/Commands/ServicesDelete.php index b5a74166a..1e5d5808c 100644 --- a/app/Console/Commands/ServicesDelete.php +++ b/app/Console/Commands/ServicesDelete.php @@ -96,7 +96,7 @@ class ServicesDelete extends Command if (! $confirmed) { break; } - DeleteResourceJob::dispatch($toDelete); + DeleteResourceJob::dispatch($toDelete)->onQueue('high'); } } } @@ -122,7 +122,7 @@ class ServicesDelete extends Command if (! $confirmed) { return; } - DeleteResourceJob::dispatch($toDelete); + DeleteResourceJob::dispatch($toDelete)->onQueue('high'); } } } @@ -148,7 +148,7 @@ class ServicesDelete extends Command if (! $confirmed) { return; } - DeleteResourceJob::dispatch($toDelete); + DeleteResourceJob::dispatch($toDelete)->onQueue('high'); } } } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 8b60c694b..3e3ae0834 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -179,14 +179,11 @@ class Kernel extends ConsoleKernel private function checkScheduledTasks($schedule): void { - $scheduled_tasks = ScheduledTask::all(); + $scheduled_tasks = ScheduledTask::where('enabled', true)->get(); if ($scheduled_tasks->isEmpty()) { return; } foreach ($scheduled_tasks as $scheduled_task) { - if ($scheduled_task->enabled === false) { - continue; - } $service = $scheduled_task->service; $application = $scheduled_task->application; diff --git a/app/Http/Controllers/Api/ApplicationsController.php b/app/Http/Controllers/Api/ApplicationsController.php index dee75578f..f0eeb56d8 100644 --- a/app/Http/Controllers/Api/ApplicationsController.php +++ b/app/Http/Controllers/Api/ApplicationsController.php @@ -1203,7 +1203,7 @@ class ApplicationsController extends Controller $service->name = "service-$service->uuid"; $service->parse(isNew: true); if ($instantDeploy) { - StartService::dispatch($service); + StartService::dispatch($service)->onQueue('high'); } return response()->json(serializeApiResponse([ @@ -1358,7 +1358,7 @@ class ApplicationsController extends Controller deleteVolumes: $request->query->get('delete_volumes', true), dockerCleanup: $request->query->get('docker_cleanup', true), deleteConnectedNetworks: $request->query->get('delete_connected_networks', true) - ); + )->onQueue('high'); return response()->json([ 'message' => 'Application deletion request queued.', @@ -2482,7 +2482,7 @@ class ApplicationsController extends Controller if (! $application) { return response()->json(['message' => 'Application not found.'], 404); } - StopApplication::dispatch($application); + StopApplication::dispatch($application)->onQueue('high'); return response()->json( [ diff --git a/app/Http/Controllers/Api/DatabasesController.php b/app/Http/Controllers/Api/DatabasesController.php index ce658d2a2..eaa542a83 100644 --- a/app/Http/Controllers/Api/DatabasesController.php +++ b/app/Http/Controllers/Api/DatabasesController.php @@ -497,9 +497,9 @@ class DatabasesController extends Controller $database->update($request->all()); if ($whatToDoWithDatabaseProxy === 'start') { - StartDatabaseProxy::dispatch($database); + StartDatabaseProxy::dispatch($database)->onQueue('high'); } elseif ($whatToDoWithDatabaseProxy === 'stop') { - StopDatabaseProxy::dispatch($database); + StopDatabaseProxy::dispatch($database)->onQueue('high'); } return response()->json([ @@ -1151,7 +1151,7 @@ class DatabasesController extends Controller } $database = create_standalone_postgresql($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartDatabase::dispatch($database); + StartDatabase::dispatch($database)->onQueue('high'); } $database->refresh(); $payload = [ @@ -1206,7 +1206,7 @@ class DatabasesController extends Controller } $database = create_standalone_mariadb($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartDatabase::dispatch($database); + StartDatabase::dispatch($database)->onQueue('high'); } $database->refresh(); @@ -1264,7 +1264,7 @@ class DatabasesController extends Controller } $database = create_standalone_mysql($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartDatabase::dispatch($database); + StartDatabase::dispatch($database)->onQueue('high'); } $database->refresh(); @@ -1320,7 +1320,7 @@ class DatabasesController extends Controller } $database = create_standalone_redis($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartDatabase::dispatch($database); + StartDatabase::dispatch($database)->onQueue('high'); } $database->refresh(); @@ -1357,7 +1357,7 @@ class DatabasesController extends Controller removeUnnecessaryFieldsFromRequest($request); $database = create_standalone_dragonfly($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartDatabase::dispatch($database); + StartDatabase::dispatch($database)->onQueue('high'); } return response()->json(serializeApiResponse([ @@ -1406,7 +1406,7 @@ class DatabasesController extends Controller } $database = create_standalone_keydb($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartDatabase::dispatch($database); + StartDatabase::dispatch($database)->onQueue('high'); } $database->refresh(); @@ -1442,7 +1442,7 @@ class DatabasesController extends Controller removeUnnecessaryFieldsFromRequest($request); $database = create_standalone_clickhouse($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartDatabase::dispatch($database); + StartDatabase::dispatch($database)->onQueue('high'); } $database->refresh(); @@ -1500,7 +1500,7 @@ class DatabasesController extends Controller } $database = create_standalone_mongodb($environment->id, $destination->uuid, $request->all()); if ($instantDeploy) { - StartDatabase::dispatch($database); + StartDatabase::dispatch($database)->onQueue('high'); } $database->refresh(); @@ -1593,7 +1593,7 @@ class DatabasesController extends Controller deleteVolumes: $request->query->get('delete_volumes', true), dockerCleanup: $request->query->get('docker_cleanup', true), deleteConnectedNetworks: $request->query->get('delete_connected_networks', true) - ); + )->onQueue('high'); return response()->json([ 'message' => 'Database deletion request queued.', @@ -1666,7 +1666,7 @@ class DatabasesController extends Controller if (str($database->status)->contains('running')) { return response()->json(['message' => 'Database is already running.'], 400); } - StartDatabase::dispatch($database); + StartDatabase::dispatch($database)->onQueue('high'); return response()->json( [ @@ -1742,7 +1742,7 @@ class DatabasesController extends Controller if (str($database->status)->contains('stopped') || str($database->status)->contains('exited')) { return response()->json(['message' => 'Database is already stopped.'], 400); } - StopDatabase::dispatch($database); + StopDatabase::dispatch($database)->onQueue('high'); return response()->json( [ @@ -1815,7 +1815,7 @@ class DatabasesController extends Controller if (! $database) { return response()->json(['message' => 'Database not found.'], 404); } - RestartDatabase::dispatch($database); + RestartDatabase::dispatch($database)->onQueue('high'); return response()->json( [ diff --git a/app/Http/Controllers/Api/DeployController.php b/app/Http/Controllers/Api/DeployController.php index 666dc55a5..59b199d87 100644 --- a/app/Http/Controllers/Api/DeployController.php +++ b/app/Http/Controllers/Api/DeployController.php @@ -307,7 +307,7 @@ class DeployController extends Controller break; default: // Database resource - StartDatabase::dispatch($resource); + StartDatabase::dispatch($resource)->onQueue('high'); $resource->update([ 'started_at' => now(), ]); diff --git a/app/Http/Controllers/Api/ServersController.php b/app/Http/Controllers/Api/ServersController.php index af4e008ef..024ef35fa 100644 --- a/app/Http/Controllers/Api/ServersController.php +++ b/app/Http/Controllers/Api/ServersController.php @@ -538,7 +538,7 @@ class ServersController extends Controller 'is_build_server' => $request->is_build_server, ]); if ($request->instant_validate) { - ValidateServer::dispatch($server); + ValidateServer::dispatch($server)->onQueue('high'); } return response()->json([ @@ -651,7 +651,7 @@ class ServersController extends Controller ]); } if ($request->instant_validate) { - ValidateServer::dispatch($server); + ValidateServer::dispatch($server)->onQueue('high'); } return response()->json(serializeApiResponse($server))->setStatusCode(201); @@ -787,7 +787,7 @@ class ServersController extends Controller if (! $server) { return response()->json(['message' => 'Server not found.'], 404); } - ValidateServer::dispatch($server); + ValidateServer::dispatch($server)->onQueue('high'); return response()->json(['message' => 'Validation started.']); } diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index bf90322e2..bdb5612ad 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -342,7 +342,7 @@ class ServicesController extends Controller } $service->parse(isNew: true); if ($instantDeploy) { - StartService::dispatch($service); + StartService::dispatch($service)->onQueue('high'); } $domains = $service->applications()->get()->pluck('fqdn')->sort(); $domains = $domains->map(function ($domain) { @@ -487,7 +487,7 @@ class ServicesController extends Controller deleteVolumes: $request->query->get('delete_volumes', true), dockerCleanup: $request->query->get('docker_cleanup', true), deleteConnectedNetworks: $request->query->get('delete_connected_networks', true) - ); + )->onQueue('high'); return response()->json([ 'message' => 'Service deletion request queued.', @@ -1076,7 +1076,7 @@ class ServicesController extends Controller if (str($service->status())->contains('running')) { return response()->json(['message' => 'Service is already running.'], 400); } - StartService::dispatch($service); + StartService::dispatch($service)->onQueue('high'); return response()->json( [ @@ -1154,7 +1154,7 @@ class ServicesController extends Controller if (str($service->status())->contains('stopped') || str($service->status())->contains('exited')) { return response()->json(['message' => 'Service is already stopped.'], 400); } - StopService::dispatch($service); + StopService::dispatch($service)->onQueue('high'); return response()->json( [ @@ -1229,7 +1229,7 @@ class ServicesController extends Controller if (! $service) { return response()->json(['message' => 'Service not found.'], 404); } - RestartService::dispatch($service); + RestartService::dispatch($service)->onQueue('high'); return response()->json( [ diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index a0fdd0e97..5ceed332a 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -1836,7 +1836,7 @@ class ApplicationDeploymentJob implements ShouldBeEncrypted, ShouldQueue } if ($this->pull_request_id === 0) { - $custom_compose = convert_docker_run_to_compose($this->application->custom_docker_run_options); + $custom_compose = convertDockerRunToCompose($this->application->custom_docker_run_options); if ((bool) $this->application->settings->is_consistent_container_name_enabled) { if (! $this->application->settings->custom_internal_name) { $docker_compose['services'][$this->application->uuid] = $docker_compose['services'][$this->container_name]; diff --git a/app/Jobs/PushServerUpdateJob.php b/app/Jobs/PushServerUpdateJob.php index 24f8d1e6b..9822ca071 100644 --- a/app/Jobs/PushServerUpdateJob.php +++ b/app/Jobs/PushServerUpdateJob.php @@ -360,7 +360,7 @@ class PushServerUpdateJob implements ShouldBeEncrypted, ShouldQueue private function checkLogDrainContainer() { if ($this->server->isLogDrainEnabled() && $this->foundLogDrainContainer === false) { - StartLogDrain::dispatch($this->server); + StartLogDrain::dispatch($this->server)->onQueue('high'); } } } diff --git a/app/Jobs/ServerCheckJob.php b/app/Jobs/ServerCheckJob.php index 449a2da14..c584f493d 100644 --- a/app/Jobs/ServerCheckJob.php +++ b/app/Jobs/ServerCheckJob.php @@ -13,6 +13,7 @@ use Illuminate\Contracts\Queue\ShouldBeEncrypted; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use Illuminate\Queue\InteractsWithQueue; +use Illuminate\Queue\Middleware\WithoutOverlapping; use Illuminate\Queue\SerializesModels; class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue @@ -25,6 +26,11 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue public $containers; + public function middleware(): array + { + return [(new WithoutOverlapping($this->server->id))->dontRelease()]; + } + public function __construct(public Server $server) {} public function handle() @@ -88,10 +94,10 @@ class ServerCheckJob implements ShouldBeEncrypted, ShouldQueue if ($foundLogDrainContainer) { $status = data_get($foundLogDrainContainer, 'State.Status'); if ($status !== 'running') { - StartLogDrain::dispatch($this->server); + StartLogDrain::dispatch($this->server)->onQueue('high'); } } else { - StartLogDrain::dispatch($this->server); + StartLogDrain::dispatch($this->server)->onQueue('high'); } } } diff --git a/app/Jobs/ServerLimitCheckJob.php b/app/Jobs/ServerLimitCheckJob.php index 084b6bf81..aa82c6dad 100644 --- a/app/Jobs/ServerLimitCheckJob.php +++ b/app/Jobs/ServerLimitCheckJob.php @@ -30,8 +30,7 @@ class ServerLimitCheckJob implements ShouldBeEncrypted, ShouldQueue try { $servers = $this->team->servers; $servers_count = $servers->count(); - $limit = data_get($this->team->limits, 'serverLimit', 2); - $number_of_servers_to_disable = $servers_count - $limit; + $number_of_servers_to_disable = $servers_count - $this->team->limits; if ($number_of_servers_to_disable > 0) { $servers = $servers->sortbyDesc('created_at'); $servers_to_disable = $servers->take($number_of_servers_to_disable); diff --git a/app/Livewire/Destination/New/Docker.php b/app/Livewire/Destination/New/Docker.php index 1ef8761fa..8cf58ec41 100644 --- a/app/Livewire/Destination/New/Docker.php +++ b/app/Livewire/Destination/New/Docker.php @@ -6,7 +6,7 @@ use App\Models\Server; use App\Models\StandaloneDocker; use App\Models\SwarmDocker; use Livewire\Attributes\Locked; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; use Visus\Cuid2\Cuid2; @@ -18,16 +18,16 @@ class Docker extends Component #[Locked] public Server $selectedServer; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $name; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $network; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $serverId; - #[Rule(['required', 'boolean'])] + #[Validate(['required', 'boolean'])] public bool $isSwarm = false; public function mount(?string $server_id = null) diff --git a/app/Livewire/Destination/Show.php b/app/Livewire/Destination/Show.php index f75749382..5c4d6c170 100644 --- a/app/Livewire/Destination/Show.php +++ b/app/Livewire/Destination/Show.php @@ -6,7 +6,7 @@ use App\Models\Server; use App\Models\StandaloneDocker; use App\Models\SwarmDocker; use Livewire\Attributes\Locked; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class Show extends Component @@ -14,13 +14,13 @@ class Show extends Component #[Locked] public $destination; - #[Rule(['string', 'required'])] + #[Validate(['string', 'required'])] public string $name; - #[Rule(['string', 'required'])] + #[Validate(['string', 'required'])] public string $network; - #[Rule(['string', 'required'])] + #[Validate(['string', 'required'])] public string $serverIp; public function mount(string $destination_uuid) diff --git a/app/Livewire/Help.php b/app/Livewire/Help.php index b174b1429..f51527fbe 100644 --- a/app/Livewire/Help.php +++ b/app/Livewire/Help.php @@ -5,17 +5,17 @@ namespace App\Livewire; use DanHarrin\LivewireRateLimiting\WithRateLimiting; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Support\Facades\Http; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class Help extends Component { use WithRateLimiting; - #[Rule(['required', 'min:10', 'max:1000'])] + #[Validate(['required', 'min:10', 'max:1000'])] public string $description; - #[Rule(['required', 'min:3'])] + #[Validate(['required', 'min:3'])] public string $subject; public function submit() diff --git a/app/Livewire/Notifications/Discord.php b/app/Livewire/Notifications/Discord.php index df5489a24..7a177a227 100644 --- a/app/Livewire/Notifications/Discord.php +++ b/app/Livewire/Notifications/Discord.php @@ -4,35 +4,35 @@ namespace App\Livewire\Notifications; use App\Models\Team; use App\Notifications\Test; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class Discord extends Component { public Team $team; - #[Rule(['boolean'])] + #[Validate(['boolean'])] public bool $discordEnabled = false; - #[Rule(['url', 'nullable'])] + #[Validate(['url', 'nullable'])] public ?string $discordWebhookUrl = null; - #[Rule(['boolean'])] + #[Validate(['boolean'])] public bool $discordNotificationsTest = false; - #[Rule(['boolean'])] + #[Validate(['boolean'])] public bool $discordNotificationsDeployments = false; - #[Rule(['boolean'])] + #[Validate(['boolean'])] public bool $discordNotificationsStatusChanges = false; - #[Rule(['boolean'])] + #[Validate(['boolean'])] public bool $discordNotificationsDatabaseBackups = false; - #[Rule(['boolean'])] + #[Validate(['boolean'])] public bool $discordNotificationsScheduledTasks = false; - #[Rule(['boolean'])] + #[Validate(['boolean'])] public bool $discordNotificationsServerDiskUsage = false; public function mount() @@ -41,7 +41,7 @@ class Discord extends Component $this->team = auth()->user()->currentTeam(); $this->syncData(); } catch (\Throwable $e) { - handleError($e, $this); + return handleError($e, $this); } } @@ -57,11 +57,8 @@ class Discord extends Component $this->team->discord_notifications_database_backups = $this->discordNotificationsDatabaseBackups; $this->team->discord_notifications_scheduled_tasks = $this->discordNotificationsScheduledTasks; $this->team->discord_notifications_server_disk_usage = $this->discordNotificationsServerDiskUsage; - try { - $this->saveModel(); - } catch (\Throwable $e) { - return handleError($e, $this); - } + $this->team->save(); + refreshSession(); } else { $this->discordEnabled = $this->team->discord_enabled; $this->discordWebhookUrl = $this->team->discord_webhook_url; @@ -74,6 +71,22 @@ class Discord extends Component } } + public function instantSaveDiscordEnabled() + { + try { + $this->validate([ + 'discordWebhookUrl' => 'required', + ], [ + 'discordWebhookUrl.required' => 'Discord Webhook URL is required.', + ]); + $this->saveModel(); + } catch (\Throwable $e) { + $this->discordEnabled = false; + + return handleError($e, $this); + } + } + public function instantSave() { try { @@ -96,7 +109,7 @@ class Discord extends Component public function saveModel() { - $this->team->save(); + $this->syncData(true); refreshSession(); $this->dispatch('success', 'Settings saved.'); } diff --git a/app/Livewire/Notifications/Email.php b/app/Livewire/Notifications/Email.php index 08415731d..b49638b48 100644 --- a/app/Livewire/Notifications/Email.php +++ b/app/Livewire/Notifications/Email.php @@ -5,75 +5,133 @@ namespace App\Livewire\Notifications; use App\Models\Team; use App\Notifications\Test; use Illuminate\Support\Facades\RateLimiter; +use Livewire\Attributes\Locked; +use Livewire\Attributes\Validate; use Livewire\Component; class Email extends Component { public Team $team; + #[Locked] public string $emails; - public bool $sharedEmailEnabled = false; + #[Validate(['boolean'])] + public bool $smtpEnabled = false; - protected $rules = [ - 'team.smtp_enabled' => 'nullable|boolean', - 'team.smtp_from_address' => 'required|email', - 'team.smtp_from_name' => 'required', - 'team.smtp_recipients' => 'nullable', - 'team.smtp_host' => 'required', - 'team.smtp_port' => 'required', - 'team.smtp_encryption' => 'nullable', - 'team.smtp_username' => 'nullable', - 'team.smtp_password' => 'nullable', - 'team.smtp_timeout' => 'nullable', - 'team.smtp_notifications_test' => 'nullable|boolean', - 'team.smtp_notifications_deployments' => 'nullable|boolean', - 'team.smtp_notifications_status_changes' => 'nullable|boolean', - 'team.smtp_notifications_database_backups' => 'nullable|boolean', - 'team.smtp_notifications_scheduled_tasks' => 'nullable|boolean', - 'team.smtp_notifications_server_disk_usage' => 'nullable|boolean', - 'team.use_instance_email_settings' => 'boolean', - 'team.resend_enabled' => 'nullable|boolean', - 'team.resend_api_key' => 'nullable', - ]; + #[Validate(['boolean'])] + public bool $useInstanceEmailSettings = false; - protected $validationAttributes = [ - 'team.smtp_from_address' => 'From Address', - 'team.smtp_from_name' => 'From Name', - 'team.smtp_recipients' => 'Recipients', - 'team.smtp_host' => 'Host', - 'team.smtp_port' => 'Port', - 'team.smtp_encryption' => 'Encryption', - 'team.smtp_username' => 'Username', - 'team.smtp_password' => 'Password', - 'team.smtp_timeout' => 'Timeout', - 'team.resend_enabled' => 'Resend Enabled', - 'team.resend_api_key' => 'Resend API Key', - ]; + #[Validate(['nullable', 'email'])] + public ?string $smtpFromAddress = null; + + #[Validate(['nullable', 'string'])] + public ?string $smtpFromName = null; + + #[Validate(['nullable', 'string'])] + public ?string $smtpRecipients = null; + + #[Validate(['nullable', 'string'])] + public ?string $smtpHost = null; + + #[Validate(['nullable', 'numeric'])] + public ?int $smtpPort = null; + + #[Validate(['nullable', 'string'])] + public ?string $smtpEncryption = null; + + #[Validate(['nullable', 'string'])] + public ?string $smtpUsername = null; + + #[Validate(['nullable', 'string'])] + public ?string $smtpPassword = null; + + #[Validate(['nullable', 'numeric'])] + public ?int $smtpTimeout = null; + + #[Validate(['boolean'])] + public bool $smtpNotificationsTest; + + #[Validate(['boolean'])] + public bool $smtpNotificationsDeployments; + + #[Validate(['boolean'])] + public bool $smtpNotificationsStatusChanges; + + #[Validate(['boolean'])] + public bool $smtpNotificationsDatabaseBackups; + + #[Validate(['boolean'])] + public bool $smtpNotificationsScheduledTasks; + + #[Validate(['boolean'])] + public bool $smtpNotificationsServerDiskUsage; + + #[Validate(['boolean'])] + public bool $resendEnabled; + + #[Validate(['nullable', 'string'])] + public ?string $resendApiKey = null; public function mount() - { - $this->team = auth()->user()->currentTeam(); - ['sharedEmailEnabled' => $this->sharedEmailEnabled] = $this->team->limits; - $this->emails = auth()->user()->email; - } - - public function submitFromFields() { try { - $this->resetErrorBag(); - $this->validate([ - 'team.smtp_from_address' => 'required|email', - 'team.smtp_from_name' => 'required', - ]); - $this->team->save(); - refreshSession(); - $this->dispatch('success', 'Settings saved.'); + $this->team = auth()->user()->currentTeam(); + $this->emails = auth()->user()->email; + $this->syncData(); } catch (\Throwable $e) { return handleError($e, $this); } } + public function syncData(bool $toModel = false) + { + if ($toModel) { + $this->validate(); + $this->team->smtp_enabled = $this->smtpEnabled; + $this->team->smtp_from_address = $this->smtpFromAddress; + $this->team->smtp_from_name = $this->smtpFromName; + $this->team->smtp_host = $this->smtpHost; + $this->team->smtp_port = $this->smtpPort; + $this->team->smtp_encryption = $this->smtpEncryption; + $this->team->smtp_username = $this->smtpUsername; + $this->team->smtp_password = $this->smtpPassword; + $this->team->smtp_timeout = $this->smtpTimeout; + $this->team->smtp_recipients = $this->smtpRecipients; + $this->team->smtp_notifications_test = $this->smtpNotificationsTest; + $this->team->smtp_notifications_deployments = $this->smtpNotificationsDeployments; + $this->team->smtp_notifications_status_changes = $this->smtpNotificationsStatusChanges; + $this->team->smtp_notifications_database_backups = $this->smtpNotificationsDatabaseBackups; + $this->team->smtp_notifications_scheduled_tasks = $this->smtpNotificationsScheduledTasks; + $this->team->smtp_notifications_server_disk_usage = $this->smtpNotificationsServerDiskUsage; + $this->team->use_instance_email_settings = $this->useInstanceEmailSettings; + $this->team->resend_enabled = $this->resendEnabled; + $this->team->resend_api_key = $this->resendApiKey; + $this->team->save(); + refreshSession(); + } else { + $this->smtpEnabled = $this->team->smtp_enabled; + $this->smtpFromAddress = $this->team->smtp_from_address; + $this->smtpFromName = $this->team->smtp_from_name; + $this->smtpHost = $this->team->smtp_host; + $this->smtpPort = $this->team->smtp_port; + $this->smtpEncryption = $this->team->smtp_encryption; + $this->smtpUsername = $this->team->smtp_username; + $this->smtpPassword = $this->team->smtp_password; + $this->smtpTimeout = $this->team->smtp_timeout; + $this->smtpRecipients = $this->team->smtp_recipients; + $this->smtpNotificationsTest = $this->team->smtp_notifications_test; + $this->smtpNotificationsDeployments = $this->team->smtp_notifications_deployments; + $this->smtpNotificationsStatusChanges = $this->team->smtp_notifications_status_changes; + $this->smtpNotificationsDatabaseBackups = $this->team->smtp_notifications_database_backups; + $this->smtpNotificationsScheduledTasks = $this->team->smtp_notifications_scheduled_tasks; + $this->smtpNotificationsServerDiskUsage = $this->team->smtp_notifications_server_disk_usage; + $this->useInstanceEmailSettings = $this->team->use_instance_email_settings; + $this->resendEnabled = $this->team->resend_enabled; + $this->resendApiKey = $this->team->resend_api_key; + } + } + public function sendTestNotification() { try { @@ -98,38 +156,44 @@ class Email extends Component public function instantSaveInstance() { try { - if (! $this->sharedEmailEnabled) { - throw new \Exception('Not allowed to change settings. Please upgrade your subscription.'); - } - $this->team->smtp_enabled = false; - $this->team->resend_enabled = false; - $this->team->save(); - refreshSession(); - $this->dispatch('success', 'Settings saved.'); + $this->smtpEnabled = false; + $this->resendEnabled = false; + $this->saveModel(); } catch (\Throwable $e) { return handleError($e, $this); } } + public function instantSaveSmtpEnabled() + { + try { + $this->validate([ + 'smtpHost' => 'required', + 'smtpPort' => 'required|numeric', + ], [ + 'smtpHost.required' => 'SMTP Host is required.', + 'smtpPort.required' => 'SMTP Port is required.', + ]); + $this->resendEnabled = false; + $this->saveModel(); + } catch (\Throwable $e) { + $this->smtpEnabled = false; + + return handleError($e, $this); + } + } + public function instantSaveResend() { try { - $this->team->smtp_enabled = false; - $this->submitResend(); + $this->validate([ + ], [ + 'resendApiKey.required' => 'Resend API Key is required.', + ]); + $this->smtpEnabled = false; + $this->saveModel(); } catch (\Throwable $e) { - $this->team->smtp_enabled = false; - - return handleError($e, $this); - } - } - - public function instantSave() - { - try { - $this->team->resend_enabled = false; - $this->submit(); - } catch (\Throwable $e) { - $this->team->smtp_enabled = false; + $this->resendEnabled = false; return handleError($e, $this); } @@ -137,7 +201,7 @@ class Email extends Component public function saveModel() { - $this->team->save(); + $this->syncData(true); refreshSession(); $this->dispatch('success', 'Settings saved.'); } @@ -146,43 +210,8 @@ class Email extends Component { try { $this->resetErrorBag(); - if (! $this->team->use_instance_email_settings) { - $this->validate([ - 'team.smtp_from_address' => 'required|email', - 'team.smtp_from_name' => 'required', - 'team.smtp_host' => 'required', - 'team.smtp_port' => 'required|numeric', - 'team.smtp_encryption' => 'nullable', - 'team.smtp_username' => 'nullable', - 'team.smtp_password' => 'nullable', - 'team.smtp_timeout' => 'nullable', - ]); - } - $this->team->save(); - refreshSession(); - $this->dispatch('success', 'Settings saved.'); + $this->saveModel(); } catch (\Throwable $e) { - $this->team->smtp_enabled = false; - - return handleError($e, $this); - } - } - - public function submitResend() - { - try { - $this->resetErrorBag(); - $this->validate([ - 'team.smtp_from_address' => 'required|email', - 'team.smtp_from_name' => 'required', - 'team.resend_api_key' => 'required', - ]); - $this->team->save(); - refreshSession(); - $this->dispatch('success', 'Settings saved.'); - } catch (\Throwable $e) { - $this->team->resend_enabled = false; - return handleError($e, $this); } } @@ -190,35 +219,28 @@ class Email extends Component public function copyFromInstanceSettings() { $settings = instanceSettings(); + if ($settings->smtp_enabled) { - $team = currentTeam(); - $team->update([ - 'smtp_enabled' => $settings->smtp_enabled, - 'smtp_from_address' => $settings->smtp_from_address, - 'smtp_from_name' => $settings->smtp_from_name, - 'smtp_recipients' => $settings->smtp_recipients, - 'smtp_host' => $settings->smtp_host, - 'smtp_port' => $settings->smtp_port, - 'smtp_encryption' => $settings->smtp_encryption, - 'smtp_username' => $settings->smtp_username, - 'smtp_password' => $settings->smtp_password, - 'smtp_timeout' => $settings->smtp_timeout, - ]); - refreshSession(); - $this->team = $team; - $this->dispatch('success', 'Settings saved.'); + $this->smtpEnabled = true; + $this->smtpFromAddress = $settings->smtp_from_address; + $this->smtpFromName = $settings->smtp_from_name; + $this->smtpRecipients = $settings->smtp_recipients; + $this->smtpHost = $settings->smtp_host; + $this->smtpPort = $settings->smtp_port; + $this->smtpEncryption = $settings->smtp_encryption; + $this->smtpUsername = $settings->smtp_username; + $this->smtpPassword = $settings->smtp_password; + $this->smtpTimeout = $settings->smtp_timeout; + $this->resendEnabled = false; + $this->saveModel(); return; } if ($settings->resend_enabled) { - $team = currentTeam(); - $team->update([ - 'resend_enabled' => $settings->resend_enabled, - 'resend_api_key' => $settings->resend_api_key, - ]); - refreshSession(); - $this->team = $team; - $this->dispatch('success', 'Settings saved.'); + $this->resendEnabled = true; + $this->resendApiKey = $settings->resend_api_key; + $this->smtpEnabled = false; + $this->saveModel(); return; } diff --git a/app/Livewire/Notifications/Telegram.php b/app/Livewire/Notifications/Telegram.php index 862b9b3ea..15ec20577 100644 --- a/app/Livewire/Notifications/Telegram.php +++ b/app/Livewire/Notifications/Telegram.php @@ -4,67 +4,157 @@ namespace App\Livewire\Notifications; use App\Models\Team; use App\Notifications\Test; +use Livewire\Attributes\Validate; use Livewire\Component; class Telegram extends Component { public Team $team; - protected $rules = [ - 'team.telegram_enabled' => 'nullable|boolean', - 'team.telegram_token' => 'required|string', - 'team.telegram_chat_id' => 'required|string', - 'team.telegram_notifications_test' => 'nullable|boolean', - 'team.telegram_notifications_deployments' => 'nullable|boolean', - 'team.telegram_notifications_status_changes' => 'nullable|boolean', - 'team.telegram_notifications_database_backups' => 'nullable|boolean', - 'team.telegram_notifications_scheduled_tasks' => 'nullable|boolean', - 'team.telegram_notifications_test_message_thread_id' => 'nullable|string', - 'team.telegram_notifications_deployments_message_thread_id' => 'nullable|string', - 'team.telegram_notifications_status_changes_message_thread_id' => 'nullable|string', - 'team.telegram_notifications_database_backups_message_thread_id' => 'nullable|string', - 'team.telegram_notifications_scheduled_tasks_thread_id' => 'nullable|string', - 'team.telegram_notifications_server_disk_usage' => 'nullable|boolean', - ]; + #[Validate(['boolean'])] + public bool $telegramEnabled = false; - protected $validationAttributes = [ - 'team.telegram_token' => 'Token', - 'team.telegram_chat_id' => 'Chat ID', - ]; + #[Validate(['nullable', 'string'])] + public ?string $telegramToken = null; + + #[Validate(['nullable', 'string'])] + public ?string $telegramChatId = null; + + #[Validate(['boolean'])] + public bool $telegramNotificationsTest = false; + + #[Validate(['boolean'])] + public bool $telegramNotificationsDeployments = false; + + #[Validate(['boolean'])] + public bool $telegramNotificationsStatusChanges = false; + + #[Validate(['boolean'])] + public bool $telegramNotificationsDatabaseBackups = false; + + #[Validate(['boolean'])] + public bool $telegramNotificationsScheduledTasks = false; + + #[Validate(['nullable', 'string'])] + public ?string $telegramNotificationsTestMessageThreadId = null; + + #[Validate(['nullable', 'string'])] + public ?string $telegramNotificationsDeploymentsMessageThreadId = null; + + #[Validate(['nullable', 'string'])] + public ?string $telegramNotificationsStatusChangesMessageThreadId = null; + + #[Validate(['nullable', 'string'])] + public ?string $telegramNotificationsDatabaseBackupsMessageThreadId = null; + + #[Validate(['nullable', 'string'])] + public ?string $telegramNotificationsScheduledTasksThreadId = null; + + #[Validate(['boolean'])] + public bool $telegramNotificationsServerDiskUsage = false; public function mount() { - $this->team = auth()->user()->currentTeam(); + try { + $this->team = auth()->user()->currentTeam(); + $this->syncData(); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + + public function syncData(bool $toModel = false) + { + if ($toModel) { + $this->validate(); + $this->team->telegram_enabled = $this->telegramEnabled; + $this->team->telegram_token = $this->telegramToken; + $this->team->telegram_chat_id = $this->telegramChatId; + $this->team->telegram_notifications_test = $this->telegramNotificationsTest; + $this->team->telegram_notifications_deployments = $this->telegramNotificationsDeployments; + $this->team->telegram_notifications_status_changes = $this->telegramNotificationsStatusChanges; + $this->team->telegram_notifications_database_backups = $this->telegramNotificationsDatabaseBackups; + $this->team->telegram_notifications_scheduled_tasks = $this->telegramNotificationsScheduledTasks; + $this->team->telegram_notifications_test_message_thread_id = $this->telegramNotificationsTestMessageThreadId; + $this->team->telegram_notifications_deployments_message_thread_id = $this->telegramNotificationsDeploymentsMessageThreadId; + $this->team->telegram_notifications_status_changes_message_thread_id = $this->telegramNotificationsStatusChangesMessageThreadId; + $this->team->telegram_notifications_database_backups_message_thread_id = $this->telegramNotificationsDatabaseBackupsMessageThreadId; + $this->team->telegram_notifications_scheduled_tasks_thread_id = $this->telegramNotificationsScheduledTasksThreadId; + $this->team->telegram_notifications_server_disk_usage = $this->telegramNotificationsServerDiskUsage; + $this->team->save(); + refreshSession(); + } else { + $this->telegramEnabled = $this->team->telegram_enabled; + $this->telegramToken = $this->team->telegram_token; + $this->telegramChatId = $this->team->telegram_chat_id; + $this->telegramNotificationsTest = $this->team->telegram_notifications_test; + $this->telegramNotificationsDeployments = $this->team->telegram_notifications_deployments; + $this->telegramNotificationsStatusChanges = $this->team->telegram_notifications_status_changes; + $this->telegramNotificationsDatabaseBackups = $this->team->telegram_notifications_database_backups; + $this->telegramNotificationsScheduledTasks = $this->team->telegram_notifications_scheduled_tasks; + $this->telegramNotificationsTestMessageThreadId = $this->team->telegram_notifications_test_message_thread_id; + $this->telegramNotificationsDeploymentsMessageThreadId = $this->team->telegram_notifications_deployments_message_thread_id; + $this->telegramNotificationsStatusChangesMessageThreadId = $this->team->telegram_notifications_status_changes_message_thread_id; + $this->telegramNotificationsDatabaseBackupsMessageThreadId = $this->team->telegram_notifications_database_backups_message_thread_id; + $this->telegramNotificationsScheduledTasksThreadId = $this->team->telegram_notifications_scheduled_tasks_thread_id; + $this->telegramNotificationsServerDiskUsage = $this->team->telegram_notifications_server_disk_usage; + } + } public function instantSave() { try { - $this->submit(); - } catch (\Throwable) { - $this->team->telegram_enabled = false; - $this->validate(); + $this->syncData(true); + } catch (\Throwable $e) { + return handleError($e, $this); } } public function submit() { - $this->resetErrorBag(); - $this->validate(); - $this->saveModel(); + try { + $this->resetErrorBag(); + $this->syncData(true); + $this->saveModel(); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + + public function instantSaveTelegramEnabled() + { + try { + $this->validate([ + 'telegramToken' => 'required', + 'telegramChatId' => 'required', + ], [ + 'telegramToken.required' => 'Telegram Token is required.', + 'telegramChatId.required' => 'Telegram Chat ID is required.', + ]); + $this->saveModel(); + } catch (\Throwable $e) { + $this->telegramEnabled = false; + + return handleError($e, $this); + } } public function saveModel() { - $this->team->save(); + $this->syncData(true); refreshSession(); $this->dispatch('success', 'Settings saved.'); } public function sendTestNotification() { - $this->team?->notify(new Test); - $this->dispatch('success', 'Test notification sent.'); + try { + $this->team->notify(new Test); + $this->dispatch('success', 'Test notification sent.'); + } catch (\Throwable $e) { + return handleError($e, $this); + } } public function render() diff --git a/app/Livewire/Project/AddEmpty.php b/app/Livewire/Project/AddEmpty.php index c8c063960..fd976548a 100644 --- a/app/Livewire/Project/AddEmpty.php +++ b/app/Livewire/Project/AddEmpty.php @@ -3,15 +3,15 @@ namespace App\Livewire\Project; use App\Models\Project; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class AddEmpty extends Component { - #[Rule(['required', 'string', 'min:3'])] + #[Validate(['required', 'string', 'min:3'])] public string $name; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public string $description = ''; public function submit() diff --git a/app/Livewire/Project/Application/Advanced.php b/app/Livewire/Project/Application/Advanced.php index a3a688f7c..05ac25429 100644 --- a/app/Livewire/Project/Application/Advanced.php +++ b/app/Livewire/Project/Application/Advanced.php @@ -3,120 +3,200 @@ namespace App\Livewire\Project\Application; use App\Models\Application; +use Livewire\Attributes\Validate; use Livewire\Component; class Advanced extends Component { public Application $application; - public bool $is_force_https_enabled; + #[Validate(['boolean'])] + public bool $isForceHttpsEnabled = false; - public bool $is_gzip_enabled; + #[Validate(['boolean'])] + public bool $isGitSubmodulesEnabled = false; - public bool $is_stripprefix_enabled; + #[Validate(['boolean'])] + public bool $isGitLfsEnabled = false; - protected $rules = [ - 'application.settings.is_git_submodules_enabled' => 'boolean|required', - 'application.settings.is_git_lfs_enabled' => 'boolean|required', - 'application.settings.is_preview_deployments_enabled' => 'boolean|required', - 'application.settings.is_auto_deploy_enabled' => 'boolean|required', - 'is_force_https_enabled' => 'boolean|required', - 'application.settings.is_log_drain_enabled' => 'boolean|required', - 'application.settings.is_gpu_enabled' => 'boolean|required', - 'application.settings.is_build_server_enabled' => 'boolean|required', - 'application.settings.is_consistent_container_name_enabled' => 'boolean|required', - 'application.settings.custom_internal_name' => 'string|nullable', - 'application.settings.is_gzip_enabled' => 'boolean|required', - 'application.settings.is_stripprefix_enabled' => 'boolean|required', - 'application.settings.gpu_driver' => 'string|required', - 'application.settings.gpu_count' => 'string|required', - 'application.settings.gpu_device_ids' => 'string|required', - 'application.settings.gpu_options' => 'string|required', - 'application.settings.is_raw_compose_deployment_enabled' => 'boolean|required', - 'application.settings.connect_to_docker_network' => 'boolean|required', - ]; + #[Validate(['boolean'])] + public bool $isPreviewDeploymentsEnabled = false; + + #[Validate(['boolean'])] + public bool $isAutoDeployEnabled = true; + + #[Validate(['boolean'])] + public bool $isLogDrainEnabled = false; + + #[Validate(['boolean'])] + public bool $isGpuEnabled = false; + + #[Validate(['string'])] + public string $gpuDriver = ''; + + #[Validate(['string', 'nullable'])] + public ?string $gpuCount = null; + + #[Validate(['string', 'nullable'])] + public ?string $gpuDeviceIds = null; + + #[Validate(['string', 'nullable'])] + public ?string $gpuOptions = null; + + #[Validate(['boolean'])] + public bool $isBuildServerEnabled = false; + + #[Validate(['boolean'])] + public bool $isConsistentContainerNameEnabled = false; + + #[Validate(['string', 'nullable'])] + public ?string $customInternalName = null; + + #[Validate(['boolean'])] + public bool $isGzipEnabled = true; + + #[Validate(['boolean'])] + public bool $isStripprefixEnabled = true; + + #[Validate(['boolean'])] + public bool $isRawComposeDeploymentEnabled = false; + + #[Validate(['boolean'])] + public bool $isConnectToDockerNetworkEnabled = false; public function mount() { - $this->is_force_https_enabled = $this->application->isForceHttpsEnabled(); - $this->is_gzip_enabled = $this->application->isGzipEnabled(); - $this->is_stripprefix_enabled = $this->application->isStripprefixEnabled(); + try { + $this->syncData(); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + + public function syncData(bool $toModel = false) + { + if ($toModel) { + $this->validate(); + $this->application->settings->is_force_https_enabled = $this->isForceHttpsEnabled; + $this->application->settings->is_git_submodules_enabled = $this->isGitSubmodulesEnabled; + $this->application->settings->is_git_lfs_enabled = $this->isGitLfsEnabled; + $this->application->settings->is_preview_deployments_enabled = $this->isPreviewDeploymentsEnabled; + $this->application->settings->is_auto_deploy_enabled = $this->isAutoDeployEnabled; + $this->application->settings->is_log_drain_enabled = $this->isLogDrainEnabled; + $this->application->settings->is_gpu_enabled = $this->isGpuEnabled; + $this->application->settings->gpu_driver = $this->gpuDriver; + $this->application->settings->gpu_count = $this->gpuCount; + $this->application->settings->gpu_device_ids = $this->gpuDeviceIds; + $this->application->settings->gpu_options = $this->gpuOptions; + $this->application->settings->is_build_server_enabled = $this->isBuildServerEnabled; + $this->application->settings->is_consistent_container_name_enabled = $this->isConsistentContainerNameEnabled; + $this->application->settings->custom_internal_name = $this->customInternalName; + $this->application->settings->is_gzip_enabled = $this->isGzipEnabled; + $this->application->settings->is_stripprefix_enabled = $this->isStripprefixEnabled; + $this->application->settings->is_raw_compose_deployment_enabled = $this->isRawComposeDeploymentEnabled; + $this->application->settings->connect_to_docker_network = $this->isConnectToDockerNetworkEnabled; + $this->application->settings->save(); + } else { + $this->isForceHttpsEnabled = $this->application->isForceHttpsEnabled(); + $this->isGzipEnabled = $this->application->isGzipEnabled(); + $this->isStripprefixEnabled = $this->application->isStripprefixEnabled(); + $this->isLogDrainEnabled = $this->application->isLogDrainEnabled(); + + $this->isGitSubmodulesEnabled = $this->application->settings->is_git_submodules_enabled; + $this->isGitLfsEnabled = $this->application->settings->is_git_lfs_enabled; + $this->isPreviewDeploymentsEnabled = $this->application->settings->is_preview_deployments_enabled; + $this->isAutoDeployEnabled = $this->application->settings->is_auto_deploy_enabled; + $this->isGpuEnabled = $this->application->settings->is_gpu_enabled; + $this->gpuDriver = $this->application->settings->gpu_driver; + $this->gpuCount = $this->application->settings->gpu_count; + $this->gpuDeviceIds = $this->application->settings->gpu_device_ids; + $this->gpuOptions = $this->application->settings->gpu_options; + $this->isBuildServerEnabled = $this->application->settings->is_build_server_enabled; + $this->isConsistentContainerNameEnabled = $this->application->settings->is_consistent_container_name_enabled; + $this->customInternalName = $this->application->settings->custom_internal_name; + $this->isRawComposeDeploymentEnabled = $this->application->settings->is_raw_compose_deployment_enabled; + $this->isConnectToDockerNetworkEnabled = $this->application->settings->connect_to_docker_network; + } } public function instantSave() { - if ($this->application->isLogDrainEnabled()) { - if (! $this->application->destination->server->isLogDrainEnabled()) { - $this->application->settings->is_log_drain_enabled = false; - $this->dispatch('error', 'Log drain is not enabled on this server.'); + try { + if ($this->isLogDrainEnabled) { + if (! $this->application->destination->server->isLogDrainEnabled()) { + $this->isLogDrainEnabled = false; + $this->syncData(true); + $this->dispatch('error', 'Log drain is not enabled on this server.'); - return; + return; + } } + if ($this->application->isForceHttpsEnabled() !== $this->isForceHttpsEnabled || + $this->application->isGzipEnabled() !== $this->isGzipEnabled || + $this->application->isStripprefixEnabled() !== $this->isStripprefixEnabled + ) { + $this->dispatch('resetDefaultLabels', false); + } + + if ($this->application->settings->is_raw_compose_deployment_enabled) { + $this->application->oldRawParser(); + } else { + $this->application->parse(); + } + $this->syncData(true); + $this->dispatch('success', 'Settings saved.'); + $this->dispatch('configurationChanged'); + } catch (\Throwable $e) { + return handleError($e, $this); } - if ($this->application->settings->is_force_https_enabled !== $this->is_force_https_enabled) { - $this->application->settings->is_force_https_enabled = $this->is_force_https_enabled; - $this->dispatch('resetDefaultLabels', false); - } - if ($this->application->settings->is_gzip_enabled !== $this->is_gzip_enabled) { - $this->application->settings->is_gzip_enabled = $this->is_gzip_enabled; - $this->dispatch('resetDefaultLabels', false); - } - if ($this->application->settings->is_stripprefix_enabled !== $this->is_stripprefix_enabled) { - $this->application->settings->is_stripprefix_enabled = $this->is_stripprefix_enabled; - $this->dispatch('resetDefaultLabels', false); - } - if ($this->application->settings->is_raw_compose_deployment_enabled) { - $this->application->oldRawParser(); - } else { - $this->application->parse(); - } - $this->application->settings->save(); - $this->dispatch('success', 'Settings saved.'); - $this->dispatch('configurationChanged'); } public function submit() { - if ($this->application->settings->gpu_count && $this->application->settings->gpu_device_ids) { - $this->dispatch('error', 'You cannot set both GPU count and GPU device IDs.'); - $this->application->settings->gpu_count = null; - $this->application->settings->gpu_device_ids = null; - $this->application->settings->save(); + try { + if ($this->gpuCount && $this->gpuDeviceIds) { + $this->dispatch('error', 'You cannot set both GPU count and GPU device IDs.'); + $this->gpuCount = null; + $this->gpuDeviceIds = null; + $this->syncData(true); - return; + return; + } + $this->syncData(true); + $this->dispatch('success', 'Settings saved.'); + } catch (\Throwable $e) { + return handleError($e, $this); } - $this->application->settings->save(); - $this->dispatch('success', 'Settings saved.'); } public function saveCustomName() { - if (str($this->application->settings->custom_internal_name)->isNotEmpty()) { - $this->application->settings->custom_internal_name = str($this->application->settings->custom_internal_name)->slug()->value(); + if (str($this->customInternalName)->isNotEmpty()) { + $this->customInternalName = str($this->customInternalName)->slug()->value(); } else { - $this->application->settings->custom_internal_name = null; + $this->customInternalName = null; } - if (is_null($this->application->settings->custom_internal_name)) { - $this->application->settings->save(); + if (is_null($this->customInternalName)) { + $this->syncData(true); $this->dispatch('success', 'Custom name saved.'); return; } - $customInternalName = $this->application->settings->custom_internal_name; + $customInternalName = $this->customInternalName; $server = $this->application->destination->server; $allApplications = $server->applications(); $foundSameInternalName = $allApplications->filter(function ($application) { - return $application->id !== $this->application->id && $application->settings->custom_internal_name === $this->application->settings->custom_internal_name; + return $application->id !== $this->application->id && $application->settings->custom_internal_name === $this->customInternalName; }); if ($foundSameInternalName->isNotEmpty()) { $this->dispatch('error', 'This custom container name is already in use by another application on this server.'); - $this->application->settings->custom_internal_name = $customInternalName; - $this->application->settings->refresh(); + $this->customInternalName = $customInternalName; + $this->syncData(true); return; } - $this->application->settings->save(); + $this->syncData(true); $this->dispatch('success', 'Custom name saved.'); } diff --git a/app/Livewire/Project/Application/Preview/Form.php b/app/Livewire/Project/Application/Preview/Form.php index 79b3276a1..c7b2e8184 100644 --- a/app/Livewire/Project/Application/Preview/Form.php +++ b/app/Livewire/Project/Application/Preview/Form.php @@ -3,7 +3,6 @@ namespace App\Livewire\Project\Application\Preview; use App\Models\Application; -use Livewire\Attributes\Rule; use Livewire\Component; use Spatie\Url\Url; @@ -11,7 +10,7 @@ class Form extends Component { public Application $application; - #[Rule('required')] + #[Validate('required')] public string $previewUrlTemplate; public function mount() diff --git a/app/Livewire/Project/Application/Source.php b/app/Livewire/Project/Application/Source.php index 2d75d91f2..ade297d50 100644 --- a/app/Livewire/Project/Application/Source.php +++ b/app/Livewire/Project/Application/Source.php @@ -5,7 +5,7 @@ namespace App\Livewire\Project\Application; use App\Models\Application; use App\Models\PrivateKey; use Livewire\Attributes\Locked; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class Source extends Component @@ -15,19 +15,19 @@ class Source extends Component #[Locked] public $privateKeys; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $privateKeyName = null; - #[Rule(['nullable', 'integer'])] + #[Validate(['nullable', 'integer'])] public ?int $privateKeyId = null; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $gitRepository; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $gitBranch; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $gitCommitSha = null; public function mount() diff --git a/app/Livewire/Project/Application/Swarm.php b/app/Livewire/Project/Application/Swarm.php index c2cdcd667..197dc41ed 100644 --- a/app/Livewire/Project/Application/Swarm.php +++ b/app/Livewire/Project/Application/Swarm.php @@ -3,20 +3,20 @@ namespace App\Livewire\Project\Application; use App\Models\Application; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class Swarm extends Component { public Application $application; - #[Rule('required')] + #[Validate('required')] public int $swarmReplicas; - #[Rule(['nullable'])] + #[Validate(['nullable'])] public ?string $swarmPlacementConstraints = null; - #[Rule('required')] + #[Validate('required')] public bool $isSwarmOnlyWorkerNodes; public function mount() diff --git a/app/Livewire/Project/Database/BackupEdit.php b/app/Livewire/Project/Database/BackupEdit.php index 1e70eaba9..bcf2f959e 100644 --- a/app/Livewire/Project/Database/BackupEdit.php +++ b/app/Livewire/Project/Database/BackupEdit.php @@ -8,7 +8,7 @@ use Exception; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Livewire\Attributes\Locked; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; use Spatie\Url\Url; @@ -22,37 +22,37 @@ class BackupEdit extends Component #[Locked] public $parameters; - #[Rule(['required', 'boolean'])] + #[Validate(['required', 'boolean'])] public bool $delete_associated_backups_locally = false; - #[Rule(['required', 'boolean'])] + #[Validate(['required', 'boolean'])] public bool $delete_associated_backups_s3 = false; - #[Rule(['required', 'boolean'])] + #[Validate(['required', 'boolean'])] public bool $delete_associated_backups_sftp = false; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $status = null; - #[Rule(['required', 'boolean'])] + #[Validate(['required', 'boolean'])] public bool $backupEnabled = false; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $frequency = ''; - #[Rule(['required', 'integer', 'min:1'])] + #[Validate(['required', 'integer', 'min:1'])] public int $numberOfBackupsLocally = 1; - #[Rule(['required', 'boolean'])] + #[Validate(['required', 'boolean'])] public bool $saveS3 = false; - #[Rule(['required', 'integer'])] + #[Validate(['required', 'integer'])] public int $s3StorageId = 1; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $databasesToBackup = null; - #[Rule(['required', 'boolean'])] + #[Validate(['required', 'boolean'])] public bool $dumpAll = false; public function mount() diff --git a/app/Livewire/Project/Database/Clickhouse/General.php b/app/Livewire/Project/Database/Clickhouse/General.php index 46688adca..2d39c5151 100644 --- a/app/Livewire/Project/Database/Clickhouse/General.php +++ b/app/Livewire/Project/Database/Clickhouse/General.php @@ -8,7 +8,7 @@ use App\Models\Server; use App\Models\StandaloneClickhouse; use Exception; use Illuminate\Support\Facades\Auth; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class General extends Component @@ -17,40 +17,40 @@ class General extends Component public StandaloneClickhouse $database; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $name; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $description = null; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $clickhouseAdminUser; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $clickhouseAdminPassword; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $image; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $portsMappings = null; - #[Rule(['nullable', 'boolean'])] + #[Validate(['nullable', 'boolean'])] public ?bool $isPublic = null; - #[Rule(['nullable', 'integer'])] + #[Validate(['nullable', 'integer'])] public ?int $publicPort = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $customDockerRunOptions = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $dbUrl = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $dbUrlPublic = null; - #[Rule(['nullable', 'boolean'])] + #[Validate(['nullable', 'boolean'])] public bool $isLogDrainEnabled = false; public function getListeners() diff --git a/app/Livewire/Project/Database/CreateScheduledBackup.php b/app/Livewire/Project/Database/CreateScheduledBackup.php index d26348f5f..0903efdfd 100644 --- a/app/Livewire/Project/Database/CreateScheduledBackup.php +++ b/app/Livewire/Project/Database/CreateScheduledBackup.php @@ -5,15 +5,15 @@ namespace App\Livewire\Project\Database; use App\Models\ScheduledDatabaseBackup; use Illuminate\Support\Collection; use Livewire\Attributes\Locked; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class CreateScheduledBackup extends Component { - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public $frequency; - #[Rule(['required', 'boolean'])] + #[Validate(['required', 'boolean'])] public bool $saveToS3 = false; #[Locked] @@ -21,7 +21,7 @@ class CreateScheduledBackup extends Component public bool $enabled = true; - #[Rule(['required', 'integer'])] + #[Validate(['required', 'integer'])] public int $s3StorageId; public Collection $definedS3s; diff --git a/app/Livewire/Project/Database/Dragonfly/General.php b/app/Livewire/Project/Database/Dragonfly/General.php index 5037f6283..ea6cd46b0 100644 --- a/app/Livewire/Project/Database/Dragonfly/General.php +++ b/app/Livewire/Project/Database/Dragonfly/General.php @@ -8,7 +8,7 @@ use App\Models\Server; use App\Models\StandaloneDragonfly; use Exception; use Illuminate\Support\Facades\Auth; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class General extends Component @@ -17,37 +17,37 @@ class General extends Component public StandaloneDragonfly $database; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $name; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $description = null; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $dragonflyPassword; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $image; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $portsMappings = null; - #[Rule(['nullable', 'boolean'])] + #[Validate(['nullable', 'boolean'])] public ?bool $isPublic = null; - #[Rule(['nullable', 'integer'])] + #[Validate(['nullable', 'integer'])] public ?int $publicPort = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $customDockerRunOptions = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $dbUrl = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $dbUrlPublic = null; - #[Rule(['nullable', 'boolean'])] + #[Validate(['nullable', 'boolean'])] public bool $isLogDrainEnabled = false; public function getListeners() diff --git a/app/Livewire/Project/Database/InitScript.php b/app/Livewire/Project/Database/InitScript.php index 6bc6904d7..e3baa1c8e 100644 --- a/app/Livewire/Project/Database/InitScript.php +++ b/app/Livewire/Project/Database/InitScript.php @@ -4,7 +4,7 @@ namespace App\Livewire\Project\Database; use Exception; use Livewire\Attributes\Locked; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class InitScript extends Component @@ -15,10 +15,10 @@ class InitScript extends Component #[Locked] public int $index; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $filename = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $content = null; public function mount() diff --git a/app/Livewire/Project/Database/Keydb/General.php b/app/Livewire/Project/Database/Keydb/General.php index f95173651..e768495eb 100644 --- a/app/Livewire/Project/Database/Keydb/General.php +++ b/app/Livewire/Project/Database/Keydb/General.php @@ -8,7 +8,7 @@ use App\Models\Server; use App\Models\StandaloneKeydb; use Exception; use Illuminate\Support\Facades\Auth; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class General extends Component @@ -17,40 +17,40 @@ class General extends Component public StandaloneKeydb $database; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $name; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $description = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $keydbConf = null; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $keydbPassword; - #[Rule(['required', 'string'])] + #[Validate(['required', 'string'])] public string $image; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $portsMappings = null; - #[Rule(['nullable', 'boolean'])] + #[Validate(['nullable', 'boolean'])] public ?bool $isPublic = null; - #[Rule(['nullable', 'integer'])] + #[Validate(['nullable', 'integer'])] public ?int $publicPort = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $customDockerRunOptions = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $dbUrl = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $dbUrlPublic = null; - #[Rule(['nullable', 'boolean'])] + #[Validate(['nullable', 'boolean'])] public bool $isLogDrainEnabled = false; public function getListeners() diff --git a/app/Livewire/Project/Edit.php b/app/Livewire/Project/Edit.php index 62c1bfc11..463febb10 100644 --- a/app/Livewire/Project/Edit.php +++ b/app/Livewire/Project/Edit.php @@ -3,17 +3,17 @@ namespace App\Livewire\Project; use App\Models\Project; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class Edit extends Component { public Project $project; - #[Rule(['required', 'string', 'min:3', 'max:255'])] + #[Validate(['required', 'string', 'min:3', 'max:255'])] public string $name; - #[Rule(['nullable', 'string', 'max:255'])] + #[Validate(['nullable', 'string', 'max:255'])] public ?string $description = null; public function mount(string $project_uuid) diff --git a/app/Livewire/Project/EnvironmentEdit.php b/app/Livewire/Project/EnvironmentEdit.php index fc33cf6b6..f48220b3d 100644 --- a/app/Livewire/Project/EnvironmentEdit.php +++ b/app/Livewire/Project/EnvironmentEdit.php @@ -5,7 +5,7 @@ namespace App\Livewire\Project; use App\Models\Application; use App\Models\Project; use Livewire\Attributes\Locked; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class EnvironmentEdit extends Component @@ -17,10 +17,10 @@ class EnvironmentEdit extends Component #[Locked] public $environment; - #[Rule(['required', 'string', 'min:3', 'max:255'])] + #[Validate(['required', 'string', 'min:3', 'max:255'])] public string $name; - #[Rule(['nullable', 'string', 'max:255'])] + #[Validate(['nullable', 'string', 'max:255'])] public ?string $description = null; public function mount(string $project_uuid, string $environment_name) diff --git a/app/Livewire/Project/Show.php b/app/Livewire/Project/Show.php index 8374a98cc..2335519c7 100644 --- a/app/Livewire/Project/Show.php +++ b/app/Livewire/Project/Show.php @@ -4,17 +4,17 @@ namespace App\Livewire\Project; use App\Models\Environment; use App\Models\Project; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class Show extends Component { public Project $project; - #[Rule(['required', 'string', 'min:3'])] + #[Validate(['required', 'string', 'min:3'])] public string $name; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $description = null; public function mount(string $project_uuid) diff --git a/app/Livewire/Server/Advanced.php b/app/Livewire/Server/Advanced.php index becae9f04..0852abebf 100644 --- a/app/Livewire/Server/Advanced.php +++ b/app/Livewire/Server/Advanced.php @@ -4,7 +4,7 @@ namespace App\Livewire\Server; use App\Jobs\DockerCleanupJob; use App\Models\Server; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class Advanced extends Component @@ -13,28 +13,28 @@ class Advanced extends Component public array $parameters = []; - #[Rule(['integer', 'min:1'])] + #[Validate(['integer', 'min:1'])] public int $concurrentBuilds = 1; - #[Rule(['integer', 'min:1'])] + #[Validate(['integer', 'min:1'])] public int $dynamicTimeout = 1; - #[Rule('boolean')] + #[Validate('boolean')] public bool $forceDockerCleanup = false; - #[Rule('string')] + #[Validate('string')] public string $dockerCleanupFrequency = '*/10 * * * *'; - #[Rule(['integer', 'min:1', 'max:99'])] + #[Validate(['integer', 'min:1', 'max:99'])] public int $dockerCleanupThreshold = 10; - #[Rule(['integer', 'min:1', 'max:99'])] + #[Validate(['integer', 'min:1', 'max:99'])] public int $serverDiskUsageNotificationThreshold = 50; - #[Rule('boolean')] + #[Validate('boolean')] public bool $deleteUnusedVolumes = false; - #[Rule('boolean')] + #[Validate('boolean')] public bool $deleteUnusedNetworks = false; public function mount(string $server_uuid) diff --git a/app/Livewire/Server/CloudflareTunnels.php b/app/Livewire/Server/CloudflareTunnels.php index f16962bca..311196473 100644 --- a/app/Livewire/Server/CloudflareTunnels.php +++ b/app/Livewire/Server/CloudflareTunnels.php @@ -3,14 +3,14 @@ namespace App\Livewire\Server; use App\Models\Server; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class CloudflareTunnels extends Component { public Server $server; - #[Rule(['required', 'boolean'])] + #[Validate(['required', 'boolean'])] public bool $isCloudflareTunnelsEnabled; public function mount(string $server_uuid) diff --git a/app/Livewire/Server/Destinations.php b/app/Livewire/Server/Destinations.php index c10958bd1..dbab6e03f 100644 --- a/app/Livewire/Server/Destinations.php +++ b/app/Livewire/Server/Destinations.php @@ -19,7 +19,6 @@ class Destinations extends Component try { $this->networks = collect(); $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail(); - loggy($this->server); } catch (\Throwable $e) { return handleError($e, $this); } diff --git a/app/Livewire/Server/Form.php b/app/Livewire/Server/Form.php deleted file mode 100644 index 740421373..000000000 --- a/app/Livewire/Server/Form.php +++ /dev/null @@ -1,281 +0,0 @@ -user()->currentTeam()->id; - - return [ - "echo-private:team.{$teamId},CloudflareTunnelConfigured" => 'cloudflareTunnelConfigured', - 'refreshServerShow' => 'serverInstalled', - 'revalidate' => '$refresh', - ]; - } - - protected $rules = [ - 'server.name' => 'required', - 'server.description' => 'nullable', - 'server.ip' => 'required', - 'server.user' => 'required', - 'server.port' => 'required', - 'wildcard_domain' => 'nullable|url', - 'server.settings.is_reachable' => 'required', - 'server.settings.is_swarm_manager' => 'required|boolean', - 'server.settings.is_swarm_worker' => 'required|boolean', - 'server.settings.is_build_server' => 'required|boolean', - 'server.settings.is_metrics_enabled' => 'required|boolean', - 'server.settings.sentinel_token' => 'required', - 'server.settings.sentinel_metrics_refresh_rate_seconds' => 'required|integer|min:1', - 'server.settings.sentinel_metrics_history_days' => 'required|integer|min:1', - 'server.settings.sentinel_push_interval_seconds' => 'required|integer|min:10', - 'server.settings.sentinel_custom_url' => 'nullable|url', - 'server.settings.is_sentinel_enabled' => 'required|boolean', - 'server.settings.is_sentinel_debug_enabled' => 'required|boolean', - 'server.settings.server_timezone' => 'required|string|timezone', - ]; - - protected $validationAttributes = [ - 'server.name' => 'Name', - 'server.description' => 'Description', - 'server.ip' => 'IP address/Domain', - 'server.user' => 'User', - 'server.port' => 'Port', - 'server.settings.is_reachable' => 'Is reachable', - 'server.settings.is_swarm_manager' => 'Swarm Manager', - 'server.settings.is_swarm_worker' => 'Swarm Worker', - 'server.settings.is_build_server' => 'Build Server', - 'server.settings.is_metrics_enabled' => 'Metrics', - 'server.settings.sentinel_token' => 'Metrics Token', - 'server.settings.sentinel_metrics_refresh_rate_seconds' => 'Metrics Interval', - 'server.settings.sentinel_metrics_history_days' => 'Metrics History', - 'server.settings.sentinel_push_interval_seconds' => 'Push Interval', - 'server.settings.is_sentinel_enabled' => 'Server API', - 'server.settings.is_sentinel_debug_enabled' => 'Debug', - 'server.settings.sentinel_custom_url' => 'Coolify URL', - 'server.settings.server_timezone' => 'Server Timezone', - ]; - - public function mount(Server $server) - { - $this->server = $server; - $this->timezones = collect(timezone_identifiers_list())->sort()->values()->toArray(); - $this->wildcard_domain = $this->server->settings->wildcard_domain; - } - - public function checkSyncStatus() - { - $this->server->refresh(); - $this->server->settings->refresh(); - } - - public function regenerateSentinelToken() - { - try { - $this->server->settings->generateSentinelToken(); - $this->server->settings->refresh(); - // $this->restartSentinel(notification: false); - $this->dispatch('success', 'Token regenerated & Sentinel restarted.'); - } catch (\Throwable $e) { - return handleError($e, $this); - } - } - - public function updated($field) - { - if ($field === 'server.settings.docker_cleanup_frequency') { - $frequency = $this->server->settings->docker_cleanup_frequency; - if (! validate_cron_expression($frequency)) { - $this->dispatch('error', 'Invalid Cron / Human expression for Docker Cleanup Frequency. Resetting to default 10 minutes.'); - $this->server->settings->docker_cleanup_frequency = '*/10 * * * *'; - } - } - } - - public function cloudflareTunnelConfigured() - { - $this->serverInstalled(); - $this->dispatch('success', 'Cloudflare Tunnels configured successfully.'); - } - - public function serverInstalled() - { - $this->server->refresh(); - $this->server->settings->refresh(); - } - - public function updatedServerSettingsIsBuildServer() - { - $this->dispatch('refreshServerShow'); - $this->dispatch('serverRefresh'); - $this->dispatch('proxyStatusUpdated'); - } - - public function updatedServerSettingsIsSentinelEnabled($value) - { - $this->validate([ - 'server.settings.sentinel_custom_url' => 'required|url', - ]); - if ($value === false) { - StopSentinel::dispatch($this->server); - $this->server->settings->is_metrics_enabled = false; - $this->server->settings->save(); - $this->server->sentinelHeartbeat(isReset: true); - } else { - try { - StartSentinel::run($this->server); - } catch (\Throwable $e) { - return handleError($e, $this); - } - } - } - - public function updatedServerSettingsIsMetricsEnabled() - { - $this->restartSentinel(); - } - - public function updatedServerSettingsIsSentinelDebugEnabled() - { - $this->restartSentinel(); - } - - public function instantSave() - { - try { - $this->validate(); - refresh_server_connection($this->server->privateKey); - $this->validateServer(false); - - $this->server->settings->save(); - $this->server->save(); - $this->dispatch('success', 'Server updated.'); - $this->dispatch('refreshServerShow'); - } catch (\Throwable $e) { - $this->server->settings->refresh(); - - return handleError($e, $this); - } finally { - } - } - - public function saveSentinel() - { - try { - $this->validate(); - $this->server->settings->save(); - $this->dispatch('success', 'Sentinel updated.'); - } catch (\Throwable $e) { - return handleError($e, $this); - } finally { - $this->checkSyncStatus(); - } - } - - public function restartSentinel($notification = true) - { - try { - $this->validate(); - $this->validate([ - 'server.settings.sentinel_custom_url' => 'required|url', - ]); - $this->server->settings->save(); - $this->server->restartSentinel(async: false); - if ($notification) { - $this->dispatch('success', 'Sentinel restarted.'); - } - } catch (\Throwable $e) { - return handleError($e, $this); - } - } - - public function revalidate() - { - $this->revalidate = true; - } - - public function checkLocalhostConnection() - { - $this->submit(); - ['uptime' => $uptime, 'error' => $error] = $this->server->validateConnection(); - if ($uptime) { - $this->dispatch('success', 'Server is reachable.'); - $this->server->settings->is_reachable = true; - $this->server->settings->is_usable = true; - $this->server->settings->save(); - $this->dispatch('proxyStatusUpdated'); - } else { - $this->dispatch('error', 'Server is not reachable.', 'Please validate your configuration and connection.

Check this documentation for further help.

Error: '.$error); - - return; - } - } - - public function validateServer($install = true) - { - $this->server->update([ - 'validation_logs' => null, - ]); - $this->dispatch('init', $install); - } - - public function submit() - { - try { - if (isCloud() && ! isDev()) { - $this->validate(); - $this->validate([ - 'server.ip' => 'required', - ]); - } else { - $this->validate(); - } - $uniqueIPs = Server::all()->reject(function (Server $server) { - return $server->id === $this->server->id; - })->pluck('ip')->toArray(); - if (in_array($this->server->ip, $uniqueIPs)) { - $this->dispatch('error', 'IP address is already in use by another team.'); - - return; - } - refresh_server_connection($this->server->privateKey); - $this->server->settings->wildcard_domain = $this->wildcard_domain; - $currentTimezone = $this->server->settings->getOriginal('server_timezone'); - $newTimezone = $this->server->settings->server_timezone; - if ($currentTimezone !== $newTimezone || $currentTimezone === '') { - $this->server->settings->server_timezone = $newTimezone; - } - $this->server->settings->save(); - $this->server->save(); - - $this->dispatch('success', 'Server updated.'); - } catch (\Throwable $e) { - return handleError($e, $this); - } - } -} diff --git a/app/Livewire/Server/LogDrains.php b/app/Livewire/Server/LogDrains.php index 67a6962dc..6599149c4 100644 --- a/app/Livewire/Server/LogDrains.php +++ b/app/Livewire/Server/LogDrains.php @@ -5,38 +5,38 @@ namespace App\Livewire\Server; use App\Actions\Server\StartLogDrain; use App\Actions\Server\StopLogDrain; use App\Models\Server; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class LogDrains extends Component { public Server $server; - #[Rule(['boolean'])] + #[Validate(['boolean'])] public bool $isLogDrainNewRelicEnabled = false; - #[Rule(['boolean'])] + #[Validate(['boolean'])] public bool $isLogDrainCustomEnabled = false; - #[Rule(['boolean'])] + #[Validate(['boolean'])] public bool $isLogDrainAxiomEnabled = false; - #[Rule(['string', 'nullable'])] + #[Validate(['string', 'nullable'])] public ?string $logDrainNewRelicLicenseKey = null; - #[Rule(['url', 'nullable'])] + #[Validate(['url', 'nullable'])] public ?string $logDrainNewRelicBaseUri = null; - #[Rule(['string', 'nullable'])] + #[Validate(['string', 'nullable'])] public ?string $logDrainAxiomDatasetName = null; - #[Rule(['string', 'nullable'])] + #[Validate(['string', 'nullable'])] public ?string $logDrainAxiomApiKey = null; - #[Rule(['string', 'nullable'])] + #[Validate(['string', 'nullable'])] public ?string $logDrainCustomConfig = null; - #[Rule(['string', 'nullable'])] + #[Validate(['string', 'nullable'])] public ?string $logDrainCustomConfigParser = null; public function mount(string $server_uuid) diff --git a/app/Livewire/Server/Show.php b/app/Livewire/Server/Show.php index 2e4b67cc6..524e21908 100644 --- a/app/Livewire/Server/Show.php +++ b/app/Livewire/Server/Show.php @@ -5,77 +5,77 @@ namespace App\Livewire\Server; use App\Actions\Server\StartSentinel; use App\Actions\Server\StopSentinel; use App\Models\Server; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class Show extends Component { public Server $server; - #[Rule(['required'])] + #[Validate(['required'])] public string $name; - #[Rule(['nullable'])] + #[Validate(['nullable'])] public ?string $description; - #[Rule(['required'])] + #[Validate(['required'])] public string $ip; - #[Rule(['required'])] + #[Validate(['required'])] public string $user; - #[Rule(['required'])] + #[Validate(['required'])] public string $port; - #[Rule(['nullable'])] + #[Validate(['nullable'])] public ?string $validationLogs = null; - #[Rule(['nullable', 'url'])] + #[Validate(['nullable', 'url'])] public ?string $wildcardDomain; - #[Rule(['required'])] + #[Validate(['required'])] public bool $isReachable; - #[Rule(['required'])] + #[Validate(['required'])] public bool $isUsable; - #[Rule(['required'])] + #[Validate(['required'])] public bool $isSwarmManager; - #[Rule(['required'])] + #[Validate(['required'])] public bool $isSwarmWorker; - #[Rule(['required'])] + #[Validate(['required'])] public bool $isBuildServer; - #[Rule(['required'])] + #[Validate(['required'])] public bool $isMetricsEnabled; - #[Rule(['required'])] + #[Validate(['required'])] public string $sentinelToken; - #[Rule(['nullable'])] + #[Validate(['nullable'])] public ?string $sentinelUpdatedAt; - #[Rule(['required', 'integer', 'min:1'])] + #[Validate(['required', 'integer', 'min:1'])] public int $sentinelMetricsRefreshRateSeconds; - #[Rule(['required', 'integer', 'min:1'])] + #[Validate(['required', 'integer', 'min:1'])] public int $sentinelMetricsHistoryDays; - #[Rule(['required', 'integer', 'min:10'])] + #[Validate(['required', 'integer', 'min:10'])] public int $sentinelPushIntervalSeconds; - #[Rule(['nullable', 'url'])] + #[Validate(['nullable', 'url'])] public ?string $sentinelCustomUrl; - #[Rule(['required'])] + #[Validate(['required'])] public bool $isSentinelEnabled; - #[Rule(['required'])] + #[Validate(['required'])] public bool $isSentinelDebugEnabled; - #[Rule(['required'])] + #[Validate(['required'])] public string $serverTimezone; public array $timezones; @@ -85,8 +85,8 @@ class Show extends Component $teamId = auth()->user()->currentTeam()->id; return [ - "echo-private:team.{$teamId},CloudflareTunnelConfigured" => '$refresh', - 'refreshServerShow' => '$refresh', + "echo-private:team.{$teamId},CloudflareTunnelConfigured" => 'refresh', + 'refreshServerShow' => 'refresh', ]; } @@ -151,6 +151,12 @@ class Show extends Component } } + public function refresh() + { + $this->syncData(); + $this->dispatch('$refresh'); + } + public function validateServer($install = true) { try { diff --git a/app/Livewire/Settings/Index.php b/app/Livewire/Settings/Index.php index 0a6c5bae6..2991b8ae8 100644 --- a/app/Livewire/Settings/Index.php +++ b/app/Livewire/Settings/Index.php @@ -8,7 +8,7 @@ use App\Models\Server; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; use Livewire\Attributes\Locked; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class Index extends Component @@ -20,58 +20,58 @@ class Index extends Component #[Locked] public $timezones; - #[Rule('boolean')] + #[Validate('boolean')] public bool $is_auto_update_enabled; - #[Rule('nullable|string|max:255')] + #[Validate('nullable|string|max:255')] public ?string $fqdn = null; - #[Rule('nullable|string|max:255')] + #[Validate('nullable|string|max:255')] public ?string $resale_license = null; - #[Rule('required|integer|min:1025|max:65535')] + #[Validate('required|integer|min:1025|max:65535')] public int $public_port_min; - #[Rule('required|integer|min:1025|max:65535')] + #[Validate('required|integer|min:1025|max:65535')] public int $public_port_max; - #[Rule('nullable|string')] + #[Validate('nullable|string')] public ?string $custom_dns_servers = null; - #[Rule('nullable|string|max:255')] + #[Validate('nullable|string|max:255')] public ?string $instance_name = null; - #[Rule('nullable|string')] + #[Validate('nullable|string')] public ?string $allowed_ips = null; - #[Rule('nullable|string')] + #[Validate('nullable|string')] public ?string $public_ipv4 = null; - #[Rule('nullable|string')] + #[Validate('nullable|string')] public ?string $public_ipv6 = null; - #[Rule('string')] + #[Validate('string')] public string $auto_update_frequency; - #[Rule('string')] + #[Validate('string')] public string $update_check_frequency; - #[Rule('required|string|timezone')] + #[Validate('required|string|timezone')] public string $instance_timezone; - #[Rule('boolean')] + #[Validate('boolean')] public bool $do_not_track; - #[Rule('boolean')] + #[Validate('boolean')] public bool $is_registration_enabled; - #[Rule('boolean')] + #[Validate('boolean')] public bool $is_dns_validation_enabled; - #[Rule('boolean')] + #[Validate('boolean')] public bool $is_api_enabled; - #[Rule('boolean')] + #[Validate('boolean')] public bool $disable_two_step_confirmation; public function render() diff --git a/app/Livewire/SettingsBackup.php b/app/Livewire/SettingsBackup.php index 38f7e548a..6dc5d6ab3 100644 --- a/app/Livewire/SettingsBackup.php +++ b/app/Livewire/SettingsBackup.php @@ -8,7 +8,7 @@ use App\Models\ScheduledDatabaseBackup; use App\Models\Server; use App\Models\StandalonePostgresql; use Livewire\Attributes\Locked; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class SettingsBackup extends Component @@ -25,19 +25,19 @@ class SettingsBackup extends Component #[Locked] public $executions = []; - #[Rule(['required'])] + #[Validate(['required'])] public string $uuid; - #[Rule(['required'])] + #[Validate(['required'])] public string $name; - #[Rule(['nullable'])] + #[Validate(['nullable'])] public ?string $description = null; - #[Rule(['required'])] + #[Validate(['required'])] public string $postgres_user; - #[Rule(['required'])] + #[Validate(['required'])] public string $postgres_password; public function mount() diff --git a/app/Livewire/SettingsEmail.php b/app/Livewire/SettingsEmail.php index 2a017ed34..0ab5754f2 100644 --- a/app/Livewire/SettingsEmail.php +++ b/app/Livewire/SettingsEmail.php @@ -3,44 +3,44 @@ namespace App\Livewire; use App\Models\InstanceSettings; -use Livewire\Attributes\Rule; +use Livewire\Attributes\Validate; use Livewire\Component; class SettingsEmail extends Component { public InstanceSettings $settings; - #[Rule(['boolean'])] + #[Validate(['boolean'])] public bool $smtpEnabled = false; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $smtpHost = null; - #[Rule(['nullable', 'numeric', 'min:1', 'max:65535'])] + #[Validate(['nullable', 'numeric', 'min:1', 'max:65535'])] public ?int $smtpPort = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $smtpEncryption = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $smtpUsername = null; - #[Rule(['nullable'])] + #[Validate(['nullable'])] public ?string $smtpPassword = null; - #[Rule(['nullable', 'numeric'])] + #[Validate(['nullable', 'numeric'])] public ?int $smtpTimeout = null; - #[Rule(['nullable', 'email'])] + #[Validate(['nullable', 'email'])] public ?string $smtpFromAddress = null; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $smtpFromName = null; - #[Rule(['boolean'])] + #[Validate(['boolean'])] public bool $resendEnabled = false; - #[Rule(['nullable', 'string'])] + #[Validate(['nullable', 'string'])] public ?string $resendApiKey = null; public function mount() diff --git a/app/Livewire/Tags/Index.php b/app/Livewire/Tags/Index.php deleted file mode 100644 index 116f19e4e..000000000 --- a/app/Livewire/Tags/Index.php +++ /dev/null @@ -1,82 +0,0 @@ - 'updateDeployments']; - - public function render() - { - return view('livewire.tags.index'); - } - - public function mount() - { - $this->tags = Tag::ownedByCurrentTeam()->get()->unique('name')->sortBy('name'); - if ($this->tag) { - $this->tagUpdated(); - } - } - - public function updateDeployments($deployments) - { - $this->deploymentsPerTagPerServer = $deployments; - } - - public function tagUpdated() - { - if ($this->tag === '') { - return; - } - $sanitizedTag = htmlspecialchars($this->tag, ENT_QUOTES, 'UTF-8'); - $tag = $this->tags->where('name', $sanitizedTag)->first(); - if (! $tag) { - $this->dispatch('error', 'Tag ('.e($sanitizedTag).') not found.'); - $this->tag = ''; - - return; - } - $this->webhook = generateTagDeployWebhook($tag->name); - $this->applications = $tag->applications()->get(); - $this->services = $tag->services()->get(); - } - - public function redeployAll() - { - try { - $this->applications->each(function ($resource) { - $deploy = new DeployController; - $deploy->deploy_resource($resource); - }); - $this->services->each(function ($resource) { - $deploy = new DeployController; - $deploy->deploy_resource($resource); - }); - $this->dispatch('success', 'Mass deployment started.'); - } catch (\Exception $e) { - return handleError($e, $this); - } - } -} diff --git a/app/Livewire/Tags/Show.php b/app/Livewire/Tags/Show.php index 0dffcce57..fc5b13374 100644 --- a/app/Livewire/Tags/Show.php +++ b/app/Livewire/Tags/Show.php @@ -5,43 +5,57 @@ namespace App\Livewire\Tags; use App\Http\Controllers\Api\DeployController; use App\Models\ApplicationDeploymentQueue; use App\Models\Tag; +use Illuminate\Support\Collection; +use Livewire\Attributes\Locked; use Livewire\Attributes\Title; use Livewire\Component; #[Title('Tags | Coolify')] class Show extends Component { - public $tags; + #[Locked] + public ?string $tagName = null; - public Tag $tag; + #[Locked] + public ?Collection $tags = null; - public $applications; + #[Locked] + public ?Tag $tag = null; - public $services; + #[Locked] + public ?Collection $applications = null; - public $webhook = null; + #[Locked] + public ?Collection $services = null; - public $deployments_per_tag_per_server = []; + #[Locked] + public ?string $webhook = null; + + #[Locked] + public ?array $deploymentsPerTagPerServer = null; public function mount() { - $this->tags = Tag::ownedByCurrentTeam()->get()->unique('name')->sortBy('name'); - $tag = $this->tags->where('name', request()->tag_name)->first(); - if (! $tag) { - return redirect()->route('tags.index'); + try { + $this->tags = Tag::ownedByCurrentTeam()->get()->unique('name')->sortBy('name'); + if (str($this->tagName)->isNotEmpty()) { + $tag = $this->tags->where('name', $this->tagName)->first(); + $this->webhook = generateTagDeployWebhook($tag->name); + $this->applications = $tag->applications()->get(); + $this->services = $tag->services()->get(); + $this->tag = $tag; + $this->getDeployments(); + } + } catch (\Exception $e) { + return handleError($e, $this); } - $this->webhook = generateTagDeployWebhook($tag->name); - $this->applications = $tag->applications()->get(); - $this->services = $tag->services()->get(); - $this->tag = $tag; - $this->get_deployments(); } - public function get_deployments() + public function getDeployments() { try { $resource_ids = $this->applications->pluck('id'); - $this->deployments_per_tag_per_server = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('application_id', $resource_ids)->get([ + $this->deploymentsPerTagPerServer = ApplicationDeploymentQueue::whereIn('status', ['in_progress', 'queued'])->whereIn('application_id', $resource_ids)->get([ 'id', 'application_id', 'application_name', @@ -56,7 +70,7 @@ class Show extends Component } } - public function redeploy_all() + public function redeployAll() { try { $message = collect([]); diff --git a/app/Livewire/Team/Create.php b/app/Livewire/Team/Create.php index 992833da5..f805d6122 100644 --- a/app/Livewire/Team/Create.php +++ b/app/Livewire/Team/Create.php @@ -3,28 +3,21 @@ namespace App\Livewire\Team; use App\Models\Team; +use Livewire\Attributes\Validate; use Livewire\Component; class Create extends Component { + #[Validate(['required', 'min:3', 'max:255'])] public string $name = ''; + #[Validate(['nullable', 'min:3', 'max:255'])] public ?string $description = null; - protected $rules = [ - 'name' => 'required|min:3|max:255', - 'description' => 'nullable|min:3|max:255', - ]; - - protected $validationAttributes = [ - 'name' => 'name', - 'description' => 'description', - ]; - public function submit() { - $this->validate(); try { + $this->validate(); $team = Team::create([ 'name' => $this->name, 'description' => $this->description, diff --git a/app/Models/Server.php b/app/Models/Server.php index e9b6fd929..0fcfd89ab 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -974,10 +974,10 @@ $schema://$host { public function serverStatus(): bool { - if ($this->isFunctional() === false) { + if ($this->status() === false) { return false; } - if ($this->status() === false) { + if ($this->isFunctional() === false) { return false; } @@ -986,9 +986,6 @@ $schema://$host { public function status(): bool { - if ($this->isFunctional() === false) { - return false; - } ['uptime' => $uptime] = $this->validateConnection(false); if ($uptime === false) { foreach ($this->applications() as $application) { @@ -1232,7 +1229,7 @@ $schema://$host { return str($this->ip)->contains(':'); } - public function restartSentinel(bool $async = true): void + public function restartSentinel(bool $async = true) { try { if ($async) { @@ -1241,7 +1238,7 @@ $schema://$host { StartSentinel::run($this, true); } } catch (\Throwable $e) { - loggy('Error restarting Sentinel: '.$e->getMessage()); + return handleError($e, $this); } } diff --git a/app/Models/ServerSetting.php b/app/Models/ServerSetting.php index bca16536e..e72c8c444 100644 --- a/app/Models/ServerSetting.php +++ b/app/Models/ServerSetting.php @@ -4,6 +4,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Log; use OpenApi\Attributes as OA; #[OA\Schema( @@ -69,7 +70,7 @@ class ServerSetting extends Model $setting->generateSentinelUrl(save: false); } } catch (\Throwable $e) { - loggy('Error creating server setting: '.$e->getMessage()); + Log::error('Error creating server setting: '.$e->getMessage()); } }); static::updated(function ($settings) { diff --git a/app/Models/Team.php b/app/Models/Team.php index 5b4a80cb1..db485054b 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -165,7 +165,7 @@ class Team extends Model implements SendsDiscord, SendsEmail return 0; } - return data_get($team, 'limits.serverLimit', 0); + return data_get($team, 'limits', 0); } public function limits(): Attribute @@ -187,9 +187,8 @@ class Team extends Model implements SendsDiscord, SendsEmail } else { $serverLimit = config('constants.limits.server')[strtolower($subscription)]; } - $sharedEmailEnabled = config('constants.limits.email')[strtolower($subscription)]; - return ['serverLimit' => $serverLimit, 'sharedEmailEnabled' => $sharedEmailEnabled]; + return $serverLimit ?? 2; } ); diff --git a/app/Notifications/Channels/DiscordChannel.php b/app/Notifications/Channels/DiscordChannel.php index 3a33d8902..86276fec9 100644 --- a/app/Notifications/Channels/DiscordChannel.php +++ b/app/Notifications/Channels/DiscordChannel.php @@ -17,6 +17,6 @@ class DiscordChannel if (! $webhookUrl) { return; } - dispatch(new SendMessageToDiscordJob($message, $webhookUrl)); + dispatch(new SendMessageToDiscordJob($message, $webhookUrl))->onQueue('high'); } } diff --git a/app/Notifications/Channels/TelegramChannel.php b/app/Notifications/Channels/TelegramChannel.php index 4b1fa49dd..b3d4e384b 100644 --- a/app/Notifications/Channels/TelegramChannel.php +++ b/app/Notifications/Channels/TelegramChannel.php @@ -41,6 +41,6 @@ class TelegramChannel if (! $telegramToken || ! $chatId || ! $message) { return; } - dispatch(new SendMessageToTelegramJob($message, $buttons, $telegramToken, $chatId, $topicId)); + dispatch(new SendMessageToTelegramJob($message, $buttons, $telegramToken, $chatId, $topicId))->onQueue('high'); } } diff --git a/bootstrap/helpers/applications.php b/bootstrap/helpers/applications.php index b3e8011b9..eb331f8c2 100644 --- a/bootstrap/helpers/applications.php +++ b/bootstrap/helpers/applications.php @@ -91,7 +91,7 @@ function next_queuable(string $server_id, string $application_id): bool $server = Server::find($server_id); $concurrent_builds = $server->settings->concurrent_builds; - ray("serverId:{$server->id}", "concurrentBuilds:{$concurrent_builds}", "deployments:{$deployments->count()}", "sameApplicationDeployments:{$same_application_deployments->count()}")->green(); + // ray("serverId:{$server->id}", "concurrentBuilds:{$concurrent_builds}", "deployments:{$deployments->count()}", "sameApplicationDeployments:{$same_application_deployments->count()}")->green(); if ($deployments->count() > $concurrent_builds) { return false; diff --git a/bootstrap/helpers/docker.php b/bootstrap/helpers/docker.php index 52435703c..2e583b94d 100644 --- a/bootstrap/helpers/docker.php +++ b/bootstrap/helpers/docker.php @@ -654,7 +654,7 @@ function isDatabaseImage(?string $image = null) return false; } -function convert_docker_run_to_compose(?string $custom_docker_run_options = null) +function convertDockerRunToCompose(?string $custom_docker_run_options = null) { $options = []; $compose_options = collect([]); @@ -679,9 +679,17 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null '--privileged' => 'privileged', '--ip' => 'ip', '--shm-size' => 'shm_size', + '--gpus' => 'gpus', ]); foreach ($matches as $match) { $option = $match[1]; + if ($option === '--gpus') { + $regexForParsingDeviceIds = '/device=([0-9A-Za-z-,]+)/'; + preg_match($regexForParsingDeviceIds, $custom_docker_run_options, $device_matches); + $value = $device_matches[1] ?? 'all'; + $options[$option][] = $value; + $options[$option] = array_unique($options[$option]); + } if (isset($match[2]) && $match[2] !== '') { $value = $match[2]; $options[$option][] = $value; @@ -694,7 +702,6 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null $options = collect($options); // Easily get mappings from https://github.com/composerize/composerize/blob/master/packages/composerize/src/mappings.js foreach ($options as $option => $value) { - // ray($option,$value); if (! data_get($mapping, $option)) { continue; } @@ -723,6 +730,28 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null if (! is_null($value) && is_array($value) && count($value) > 0) { $compose_options->put($mapping[$option], $value[0]); } + } elseif ($option === '--gpus') { + $payload = [ + 'driver' => 'nvidia', + 'capabilities' => ['gpu'], + ]; + if (! is_null($value) && is_array($value) && count($value) > 0) { + if (str($value[0]) != 'all') { + if (str($value[0])->contains(',')) { + $payload['device_ids'] = str($value[0])->explode(',')->toArray(); + } else { + $payload['device_ids'] = [$value[0]]; + } + } + } + ray($payload); + $compose_options->put('deploy', [ + 'resources' => [ + 'reservations' => [ + 'devices' => [$payload], + ], + ], + ]); } else { if ($list_options->contains($option)) { if ($compose_options->has($mapping[$option])) { @@ -744,7 +773,7 @@ function convert_docker_run_to_compose(?string $custom_docker_run_options = null return $compose_options->toArray(); } -function generate_custom_docker_run_options_for_databases($docker_run_options, $docker_compose, $container_name, $network) +function generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $network) { $ipv4 = data_get($docker_run_options, 'ip.0'); $ipv6 = data_get($docker_run_options, 'ip6.0'); diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index ec13a1be2..77749a633 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -106,7 +106,7 @@ function currentTeam() function showBoarding(): bool { - if (Auth::user()->isMember()) { + if (Auth::user()?->isMember()) { return false; } diff --git a/config/constants.php b/config/constants.php index 5792b358c..e979b2087 100644 --- a/config/constants.php +++ b/config/constants.php @@ -1,6 +1,7 @@ '26.0', 'docs' => [ 'base_url' => 'https://coolify.io/docs', 'contact' => 'https://coolify.io/docs/contact', diff --git a/database/migrations/2024_06_25_184323_update_db.php b/database/migrations/2024_06_25_184323_update_db.php index 8f9405b86..d9cddb15f 100644 --- a/database/migrations/2024_06_25_184323_update_db.php +++ b/database/migrations/2024_06_25_184323_update_db.php @@ -5,6 +5,7 @@ use App\Models\Server; use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Schema; use Visus\Cuid2\Cuid2; @@ -52,7 +53,7 @@ return new class extends Migration DB::table('server_settings')->update(['metrics_history_days' => 7]); } catch (\Exception $e) { - loggy($e); + Log::error('Error updating db: '.$e->getMessage()); } } diff --git a/database/seeders/SentinelSeeder.php b/database/seeders/SentinelSeeder.php index 117ba6782..d61c2f55d 100644 --- a/database/seeders/SentinelSeeder.php +++ b/database/seeders/SentinelSeeder.php @@ -4,6 +4,7 @@ namespace Database\Seeders; use App\Models\Server; use Illuminate\Database\Seeder; +use Illuminate\Support\Facades\Log; class SentinelSeeder extends Seeder { @@ -23,7 +24,7 @@ class SentinelSeeder extends Seeder } } } catch (\Throwable $e) { - loggy("Error: {$e->getMessage()}\n"); + Log::error('Error seeding sentinel: '.$e->getMessage()); } } }); diff --git a/docker/prod/Dockerfile b/docker/prod/Dockerfile index 22a5b143a..36ca5ebb3 100644 --- a/docker/prod/Dockerfile +++ b/docker/prod/Dockerfile @@ -45,6 +45,8 @@ RUN composer dump-autoload COPY --from=static-assets --chown=9999:9999 /app/public/build ./public/build COPY --chmod=755 docker/prod/etc/s6-overlay/ /etc/s6-overlay/ +RUN php artisan route:clear +RUN php artisan view:clear RUN php artisan route:cache RUN php artisan view:cache diff --git a/other/nightly/install.sh b/other/nightly/install.sh index 04faf50ea..e005b7493 100755 --- a/other/nightly/install.sh +++ b/other/nightly/install.sh @@ -5,7 +5,7 @@ set -e # Exit immediately if a command exits with a non-zero status ## $1 could be empty, so we need to disable this check #set -u # Treat unset variables as an error and exit set -o pipefail # Cause a pipeline to return the status of the last command that exited with a non-zero status -CDN="https://cdn.coollabs.io/coolify-nightly" +CDN="https://cdn.coollabs.io/coolify" DATE=$(date +"%Y%m%d-%H%M%S") VERSION="1.6" @@ -13,7 +13,7 @@ DOCKER_VERSION="26.0" # TODO: Ask for a user CURRENT_USER=$USER -mkdir -p /data/coolify/{source,ssh,applications,databases,backups,services,proxy,webhooks-during-maintenance,metrics,logs} +mkdir -p /data/coolify/{source,ssh,applications,databases,backups,services,proxy,webhooks-during-maintenance,sentinel} mkdir -p /data/coolify/ssh/{keys,mux} mkdir -p /data/coolify/proxy/dynamic @@ -164,7 +164,6 @@ sles | opensuse-leap | opensuse-tumbleweed) esac - echo -e "2. Check OpenSSH server configuration. " # Detect OpenSSH server @@ -262,9 +261,14 @@ if ! [ -x "$(command -v docker)" ]; then fi ;; *) - curl -s https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh >/dev/null 2>&1 + if [ "$OS_TYPE" = "ubuntu" ] && [ "$OS_VERSION" = "24.10" ]; then + echo "Docker automated installation is not supported on Ubuntu 24.10 (non-LTS release)." + echo "Please install Docker manually." + exit 1 + fi + curl -s https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh 2>&1 if ! [ -x "$(command -v docker)" ]; then - curl -s https://get.docker.com | sh -s -- --version ${DOCKER_VERSION} >/dev/null 2>&1 + curl -s https://get.docker.com | sh -s -- --version ${DOCKER_VERSION} 2>&1 if ! [ -x "$(command -v docker)" ]; then echo " - Docker installation failed." echo " Maybe your OS is not supported?" @@ -404,10 +408,10 @@ if [ ! -f ~/.ssh/authorized_keys ]; then fi set +e -IF_COOLIFY_VOLUME_EXISTS=$(docker volume ls | grep coolify-db | wc -l) +IS_COOLIFY_VOLUME_EXISTS=$(docker volume ls | grep coolify-db | wc -l) set -e -if [ "$IF_COOLIFY_VOLUME_EXISTS" -eq 0 ]; then +if [ "$IS_COOLIFY_VOLUME_EXISTS" -eq 0 ]; then echo " - Generating SSH key." ssh-keygen -t ed25519 -a 100 -f /data/coolify/ssh/keys/id.$CURRENT_USER@host.docker.internal -q -N "" -C coolify chown 9999 /data/coolify/ssh/keys/id.$CURRENT_USER@host.docker.internal @@ -424,7 +428,7 @@ echo -e " - It could take a while based on your server's performance, network sp echo -e " - Please wait." getAJoke -bash /data/coolify/source/upgrade.sh "${LATEST_VERSION:-latest}" "${LATEST_HELPER_VERSION:-latest}" >/dev/null 2>&1 +bash /data/coolify/source/upgrade.sh "${LATEST_VERSION:-latest}" "${LATEST_HELPER_VERSION:-latest}" echo " - Coolify installed successfully." rm -f $ENV_FILE-$DATE diff --git a/resources/views/components/navbar.blade.php b/resources/views/components/navbar.blade.php index ae98f85cb..8a2f4d5d6 100644 --- a/resources/views/components/navbar.blade.php +++ b/resources/views/components/navbar.blade.php @@ -215,7 +215,7 @@
  • + href="{{ route('tags.show') }}"> diff --git a/resources/views/components/notification/navbar.blade.php b/resources/views/components/notification/navbar.blade.php index 0fbbc69a2..b43fc38af 100644 --- a/resources/views/components/notification/navbar.blade.php +++ b/resources/views/components/notification/navbar.blade.php @@ -3,15 +3,15 @@
    Get notified about your infrastructure.
    - +
    Save - @if (isInstanceAdmin() && !$team->use_instance_email_settings) + @if (isInstanceAdmin() && !$useInstanceEmailSettings) Copy from Instance Settings @@ -25,99 +25,90 @@ @endif - - @if (isCloud()) - @if ($this->sharedEmailEnabled) -
    - -
    - @else -
    - + @if (!isCloud()) +
    +
    @endif - @else -
    - + @if (!$useInstanceEmailSettings) +
    + + +
    + @endif + + @if (isCloud()) +
    +
    @endif - @if (!$team->use_instance_email_settings) -
    - - - - Save - - + @if (!$useInstanceEmailSettings)
    -
    -

    SMTP Server

    -
    - +
    +
    +

    SMTP Server

    + + Save +
    - +
    + +
    +
    - - - + +
    - - - + +
    -
    - - Save - -
    - -
    -
    -

    Resend

    -
    -
    -
    +
    +
    +
    +

    Resend

    + + Save + +
    +
    + +
    +
    -
    -
    - - Save - -
    - -
    +
    +
    @endif - @if (isEmailEnabled($team) || data_get($team, 'use_instance_email_settings')) + @if (isEmailEnabled($team) || $useInstanceEmailSettings)

    Subscribe to events

    @if (isDev()) - + @endif - - - - + - +
    @endif
    diff --git a/resources/views/livewire/notifications/telegram.blade.php b/resources/views/livewire/notifications/telegram.blade.php index 41fee8122..76378ada1 100644 --- a/resources/views/livewire/notifications/telegram.blade.php +++ b/resources/views/livewire/notifications/telegram.blade.php @@ -9,7 +9,7 @@ Save - @if ($team->telegram_enabled) + @if ($telegramEnabled) Send Test Notifications @@ -17,64 +17,61 @@ @endif
    - +
    - + + id="telegramChatId" label="Chat ID" />
    - @if (data_get($team, 'telegram_enabled')) + @if ($telegramEnabled)

    Subscribe to events

    @if (isDev())

    Test Notification

    - + + id="telegramNotificationsTestMessageThreadId" label="Custom Topic ID" />
    @endif

    Container Status Changes

    - + + id="telegramNotificationsStatusChangesMessageThreadId" label="Custom Topic ID" />

    Application Deployments

    - + + id="telegramNotificationsDeploymentsMessageThreadId" label="Custom Topic ID" />

    Database Backup Status

    - + id="telegramNotificationsDatabaseBackupsMessageThreadId" label="Custom Topic ID" />

    Scheduled Tasks Status

    - + + id="telegramNotificationsScheduledTasksMessageThreadId" label="Custom Topic ID" />

    Server Disk Usage

    -
    diff --git a/resources/views/livewire/project/application/advanced.blade.php b/resources/views/livewire/project/application/advanced.blade.php index dc3f135db..6658c0ed2 100644 --- a/resources/views/livewire/project/application/advanced.blade.php +++ b/resources/views/livewire/project/application/advanced.blade.php @@ -8,90 +8,86 @@

    General

    @if ($application->git_based()) + id="isAutoDeployEnabled" label="Auto Deploy" /> + instantSave id="isPreviewDeploymentsEnabled" label="Preview Deployments" /> @endif + instantSave id="isForceHttpsEnabled" label="Force Https" /> + instantSave id="isGzipEnabled" /> + instantSave id="isStripprefixEnabled" label="Strip Prefixes" /> @if ($application->build_pack === 'dockercompose')

    Docker Compose

    - @endif -

    Container Names

    +

    Container Names

    - @if (!$application->settings->is_consistent_container_name_enabled) -
    + instantSave id="isConsistentContainerNameEnabled" label="Consistent Container Names" /> + @if ($isConsistentContainerNameEnabled === false) + + instantSave id="customInternalName" label="Custom Container Name" /> Save @endif @if ($application->build_pack === 'dockercompose') -

    Network

    - Network + @endif - @if (!$application->settings->is_raw_compose_deployment_enabled) -

    Logs

    + @if ($isLogDrainEnabled === false) +

    Logs

    + instantSave id="isLogDrainEnabled" label="Drain Logs" /> @endif @if ($application->git_based())

    Git

    - - @endif - {{-- - - --}}
    - @if ($application->build_pack !== 'dockercompose') -

    GPU

    - @endif -
    - @if ($application->build_pack !== 'dockercompose') - - @if ($application->settings->is_gpu_enabled) -
    GPU Settings
    +
    + + @if ($application->build_pack !== 'dockercompose') +
    +

    GPU

    + @if ($isGpuEnabled) Save @endif - @endif - @if ($application->settings->is_gpu_enabled) -
    - - - +
    + @endif + @if ($application->build_pack !== 'dockercompose') +
    + +
    + @endif + @if ($isGpuEnabled) +
    +
    + + + +
    -
    -
    - - -
    - @endif - -
    + + + + @endif + diff --git a/resources/views/livewire/project/database/create-scheduled-backup.blade.php b/resources/views/livewire/project/database/create-scheduled-backup.blade.php index ee2f3e121..23f2be8cd 100644 --- a/resources/views/livewire/project/database/create-scheduled-backup.blade.php +++ b/resources/views/livewire/project/database/create-scheduled-backup.blade.php @@ -2,6 +2,7 @@ +

    S3

    @if ($definedS3s->count() === 0)
    No validated S3 Storages found.
    @else diff --git a/resources/views/livewire/server/form.blade.php b/resources/views/livewire/server/form.blade.php deleted file mode 100644 index 9ee45c131..000000000 --- a/resources/views/livewire/server/form.blade.php +++ /dev/null @@ -1,226 +0,0 @@ -
    -
    -
    -

    General

    - @if ($server->id === 0) - - @else - Save - @if ($server->isFunctional()) - - Validate & configure - - - - - Revalidate server - - - @endif - @endif -
    - @if ($server->isFunctional()) - Server is reachable and validated. - @else - You can't use this server until it is validated. - @endif - @if ((!$server->settings->is_reachable || !$server->settings->is_usable) && $server->id !== 0) - - Validate & configure - - - - - Validate Server & Install Docker Engine - - - @if ($server->validation_logs) -

    Previous Validation Logs

    -
    - {!! $server->validation_logs !!} -
    - @endif - @endif - @if ((!$server->settings->is_reachable || !$server->settings->is_usable) && $server->id === 0) - - Validate Server - - @endif - @if ($server->isForceDisabled() && isCloud()) -
    The system has disabled the server because you have exceeded the - number of servers for which you have paid.
    - @endif -
    -
    - - - @if (!$server->settings->is_swarm_worker && !$server->settings->is_build_server) - - @endif - -
    -
    - -
    - - -
    -
    -
    -
    - - -
    -
    -
    - - - - -
    -
    - -
    -
    -
    - -
    - @if (!$server->isLocalhost()) -
    - -
    - - @if (!$server->isBuildServer() && !$server->settings->is_cloudflare_tunnel) -

    Swarm (experimental)

    -
    Read the docs here. -
    -
    - @if ($server->settings->is_swarm_worker) - - @else - - @endif - - @if ($server->settings->is_swarm_manager) - - @else - - @endif -
    - @endif - @endif -
    -
    -
    - @if ($server->isFunctional() && !$server->isSwarm() && !$server->isBuildServer()) -
    -
    -

    Sentinel

    - @if ($server->isSentinelEnabled()) -
    - @if ($server->isSentinelLive()) - - Save - Restart - @else - - Save - Sync - @endif -
    - @endif -
    -
    -
    Experimental feature
    -
    - - @if ($server->isSentinelEnabled()) - - - @else - - - @endif -
    - @if ($server->isSentinelEnabled()) -
    - - Regenerate -
    - - - -
    -
    - - - -
    -
    - @endif -
    -
    - @endif - -
    diff --git a/resources/views/livewire/tags/index.blade.php b/resources/views/livewire/tags/index.blade.php deleted file mode 100644 index 3a98519a7..000000000 --- a/resources/views/livewire/tags/index.blade.php +++ /dev/null @@ -1,64 +0,0 @@ -
    -

    Tags

    -
    -
    Tags help you to perform actions on multiple resources.
    -
    - @if ($tags->count() === 0) -
    No tags yet defined yet. Go to a resource and add a tag there.
    - @else - - @foreach ($tags as $oneTag) - - @endforeach - - @if ($tag) -
    -
    -
    - -
    - -
    - -
    -

    Deployments

    - @if (count($deploymentsPerTagPerServer) > 0) - - @endif -
    - -
    - @endif - @endif -
    -
    -
    diff --git a/resources/views/livewire/tags/show.blade.php b/resources/views/livewire/tags/show.blade.php index f135a8246..9127278ef 100644 --- a/resources/views/livewire/tags/show.blade.php +++ b/resources/views/livewire/tags/show.blade.php @@ -1,87 +1,96 @@
    -
    +

    Tags

    +
    Tags help you to perform actions on multiple resources.
    -
    -
    Available tags
    -
    - @forelse ($tags as $oneTag) - {{ $oneTag->name }} - @empty -
    No tags yet defined yet. Go to a resource and add a tag there.
    - @endforelse -
    +
    + @forelse ($tags as $oneTag) + {{ $oneTag->name }} + @empty +
    No tags yet defined yet. Go to a resource and add a tag there.
    + @endforelse
    -
    -

    Details

    -
    -
    - + @if (isset($tag)) +
    +

    Details

    +
    +
    + +
    +
    - -
    - -
    -

    Deployments

    - @if (count($deployments_per_tag_per_server) > 0) - - @endif -
    -
    - @forelse ($deployments_per_tag_per_server as $server_name => $deployments) -

    {{ $server_name }}

    - + @endif
    diff --git a/routes/web.php b/routes/web.php index 6ea3bc857..eaf849ce6 100644 --- a/routes/web.php +++ b/routes/web.php @@ -65,7 +65,6 @@ use App\Livewire\Storage\Index as StorageIndex; use App\Livewire\Storage\Show as StorageShow; use App\Livewire\Subscription\Index as SubscriptionIndex; use App\Livewire\Subscription\Show as SubscriptionShow; -use App\Livewire\Tags\Index as TagsIndex; use App\Livewire\Tags\Show as TagsShow; use App\Livewire\Team\AdminView as TeamAdminView; use App\Livewire\Team\Index as TeamIndex; @@ -127,8 +126,7 @@ Route::middleware(['auth', 'verified'])->group(function () { Route::get('/profile', ProfileIndex::class)->name('profile'); Route::prefix('tags')->group(function () { - Route::get('/', TagsIndex::class)->name('tags.index'); - Route::get('/{tag_name}', TagsShow::class)->name('tags.show'); + Route::get('/{tagName?}', TagsShow::class)->name('tags.show'); }); Route::prefix('notifications')->group(function () { diff --git a/scripts/install.sh b/scripts/install.sh index af3b4d464..e005b7493 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -164,7 +164,6 @@ sles | opensuse-leap | opensuse-tumbleweed) esac - echo -e "2. Check OpenSSH server configuration. " # Detect OpenSSH server @@ -262,9 +261,14 @@ if ! [ -x "$(command -v docker)" ]; then fi ;; *) - curl -s https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh >/dev/null 2>&1 + if [ "$OS_TYPE" = "ubuntu" ] && [ "$OS_VERSION" = "24.10" ]; then + echo "Docker automated installation is not supported on Ubuntu 24.10 (non-LTS release)." + echo "Please install Docker manually." + exit 1 + fi + curl -s https://releases.rancher.com/install-docker/${DOCKER_VERSION}.sh | sh 2>&1 if ! [ -x "$(command -v docker)" ]; then - curl -s https://get.docker.com | sh -s -- --version ${DOCKER_VERSION} >/dev/null 2>&1 + curl -s https://get.docker.com | sh -s -- --version ${DOCKER_VERSION} 2>&1 if ! [ -x "$(command -v docker)" ]; then echo " - Docker installation failed." echo " Maybe your OS is not supported?" diff --git a/tests/Feature/DockerCustomCommandsTest.php b/tests/Feature/DockerCustomCommandsTest.php index a0baeb215..a07372069 100644 --- a/tests/Feature/DockerCustomCommandsTest.php +++ b/tests/Feature/DockerCustomCommandsTest.php @@ -2,7 +2,7 @@ test('ConvertCapAdd', function () { $input = '--cap-add=NET_ADMIN --cap-add=NET_RAW --cap-add SYS_ADMIN'; - $output = convert_docker_run_to_compose($input); + $output = convertDockerRunToCompose($input); expect($output)->toBe([ 'cap_add' => ['NET_ADMIN', 'NET_RAW', 'SYS_ADMIN'], ]); @@ -10,7 +10,7 @@ test('ConvertCapAdd', function () { test('ConvertIp', function () { $input = '--cap-add=NET_ADMIN --cap-add=NET_RAW --cap-add SYS_ADMIN --ip 127.0.0.1 --ip 127.0.0.2'; - $output = convert_docker_run_to_compose($input); + $output = convertDockerRunToCompose($input); expect($output)->toBe([ 'cap_add' => ['NET_ADMIN', 'NET_RAW', 'SYS_ADMIN'], 'ip' => ['127.0.0.1', '127.0.0.2'], @@ -19,7 +19,7 @@ test('ConvertIp', function () { test('ConvertPrivilegedAndInit', function () { $input = '---privileged --init'; - $output = convert_docker_run_to_compose($input); + $output = convertDockerRunToCompose($input); expect($output)->toBe([ 'privileged' => true, 'init' => true, @@ -28,7 +28,7 @@ test('ConvertPrivilegedAndInit', function () { test('ConvertUlimit', function () { $input = '--ulimit nofile=262144:262144'; - $output = convert_docker_run_to_compose($input); + $output = convertDockerRunToCompose($input); expect($output)->toBe([ 'ulimits' => [ 'nofile' => [ @@ -38,3 +38,61 @@ test('ConvertUlimit', function () { ], ]); }); +test('ConvertGpusWithGpuId', function () { + $input = '--gpus "device=GPU-0000000000000000"'; + $output = convertDockerRunToCompose($input); + expect($output)->toBe([ + 'deploy' => [ + 'resources' => [ + 'reservations' => [ + 'devices' => [ + [ + 'driver' => 'nvidia', + 'capabilities' => ['gpu'], + 'device_ids' => ['GPU-0000000000000000'], + ], + ], + ], + ], + ], + ]); +}); + +test('ConvertGpus', function () { + $input = '--gpus all'; + $output = convertDockerRunToCompose($input); + expect($output)->toBe([ + 'deploy' => [ + 'resources' => [ + 'reservations' => [ + 'devices' => [ + [ + 'driver' => 'nvidia', + 'capabilities' => ['gpu'], + ], + ], + ], + ], + ], + ]); +}); + +test('ConvertGpusWithQuotes', function () { + $input = '--gpus "device=0,1"'; + $output = convertDockerRunToCompose($input); + expect($output)->toBe([ + 'deploy' => [ + 'resources' => [ + 'reservations' => [ + 'devices' => [ + [ + 'driver' => 'nvidia', + 'capabilities' => ['gpu'], + 'device_ids' => ['0', '1'], + ], + ], + ], + ], + ], + ]); +});