diff --git a/app/Console/Commands/CleanupApplicationDeploymentQueue.php b/app/Console/Commands/CleanupApplicationDeploymentQueue.php new file mode 100644 index 000000000..7c871d10b --- /dev/null +++ b/app/Console/Commands/CleanupApplicationDeploymentQueue.php @@ -0,0 +1,25 @@ +option('team-id'); + $servers = \App\Models\Server::where('team_id', $team_id)->get(); + foreach ($servers as $server) { + $deployments = ApplicationDeploymentQueue::whereIn("status", ["in_progress", "queued"])->where("server_id", $server->id)->get(); + foreach ($deployments as $deployment) { + $deployment->update(['status' => 'failed']); + instant_remote_process(['docker rm -f ' . $deployment->deployment_uuid], $server, false); + } + } + } +} diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php index 0949ef5e6..bc4b655b3 100644 --- a/app/Jobs/ApplicationDeploymentJob.php +++ b/app/Jobs/ApplicationDeploymentJob.php @@ -167,65 +167,71 @@ class ApplicationDeploymentJob implements ShouldQueue, ShouldBeEncrypted if ($this->application->is_github_based()) { ApplicationPullRequestUpdateJob::dispatch(application: $this->application, preview: $this->preview, deployment_uuid: $this->deployment_uuid, status: ProcessStatus::IN_PROGRESS); } + if ($this->application->build_pack === 'dockerfile') { + if (data_get($this->application, 'dockerfile_location')) { + $this->dockerfile_location = $this->application->dockerfile_location; + } + } } } public function handle(): void { - // Generate custom host<->ip mapping - $allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server); - if (!is_null($allContainers)) { - $allContainers = format_docker_command_output_to_json($allContainers); - $ips = collect([]); - if (count($allContainers) > 0) { - $allContainers = $allContainers[0]; - $allContainers = collect($allContainers)->sort()->values(); - foreach ($allContainers as $container) { - $containerName = data_get($container, 'Name'); - if ($containerName === 'coolify-proxy') { - continue; - } - if (preg_match('/-(\d{12})/', $containerName)) { - continue; - } - $containerIp = data_get($container, 'IPv4Address'); - if ($containerName && $containerIp) { - $containerIp = str($containerIp)->before('/'); - $ips->put($containerName, $containerIp->value()); + try { + // Generate custom host<->ip mapping + $allContainers = instant_remote_process(["docker network inspect {$this->destination->network} -f '{{json .Containers}}' "], $this->server); + + if (!is_null($allContainers)) { + $allContainers = format_docker_command_output_to_json($allContainers); + $ips = collect([]); + if (count($allContainers) > 0) { + $allContainers = $allContainers[0]; + $allContainers = collect($allContainers)->sort()->values(); + foreach ($allContainers as $container) { + $containerName = data_get($container, 'Name'); + if ($containerName === 'coolify-proxy') { + continue; + } + if (preg_match('/-(\d{12})/', $containerName)) { + continue; + } + $containerIp = data_get($container, 'IPv4Address'); + if ($containerName && $containerIp) { + $containerIp = str($containerIp)->before('/'); + $ips->put($containerName, $containerIp->value()); + } } } + $this->addHosts = $ips->map(function ($ip, $name) { + return "--add-host $name:$ip"; + })->implode(' '); } - $this->addHosts = $ips->map(function ($ip, $name) { - return "--add-host $name:$ip"; - })->implode(' '); - } - if ($this->application->dockerfile_target_build) { - $this->buildTarget = " --target {$this->application->dockerfile_target_build} "; - } + if ($this->application->dockerfile_target_build) { + $this->buildTarget = " --target {$this->application->dockerfile_target_build} "; + } - // Check custom port - ['repository' => $this->customRepository, 'port' => $this->customPort] = $this->application->customRepository(); + // Check custom port + ['repository' => $this->customRepository, 'port' => $this->customPort] = $this->application->customRepository(); - if (data_get($this->application, 'settings.is_build_server_enabled')) { - $teamId = data_get($this->application, 'environment.project.team.id'); - $buildServers = Server::buildServers($teamId)->get(); - if ($buildServers->count() === 0) { - $this->application_deployment_queue->addLogEntry("Build server feature activated, but no suitable build server found. Using the deployment server."); + if (data_get($this->application, 'settings.is_build_server_enabled')) { + $teamId = data_get($this->application, 'environment.project.team.id'); + $buildServers = Server::buildServers($teamId)->get(); + if ($buildServers->count() === 0) { + $this->application_deployment_queue->addLogEntry("Build server feature activated, but no suitable build server found. Using the deployment server."); + $this->build_server = $this->server; + $this->original_server = $this->server; + } else { + $this->application_deployment_queue->addLogEntry("Build server feature activated and found a suitable build server. Using it to build your application - if needed."); + $this->build_server = $buildServers->random(); + $this->original_server = $this->server; + $this->use_build_server = true; + } + } else { + // Set build server & original_server to the same as deployment server $this->build_server = $this->server; $this->original_server = $this->server; - } else { - $this->application_deployment_queue->addLogEntry("Build server feature activated and found a suitable build server. Using it to build your application - if needed."); - $this->build_server = $buildServers->random(); - $this->original_server = $this->server; - $this->use_build_server = true; } - } else { - // Set build server & original_server to the same as deployment server - $this->build_server = $this->server; - $this->original_server = $this->server; - } - try { if ($this->restart_only && $this->application->build_pack !== 'dockerimage') { $this->just_restart(); if ($this->server->isProxyShouldRun()) { @@ -1660,6 +1666,8 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); public function failed(Throwable $exception): void { + + $this->next(ApplicationDeploymentStatus::FAILED->value); $this->application_deployment_queue->addLogEntry("Oops something is not okay, are you okay? 😢", 'stderr'); if (str($exception->getMessage())->isNotEmpty()) { $this->application_deployment_queue->addLogEntry($exception->getMessage(), 'stderr'); @@ -1667,6 +1675,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); if ($this->application->build_pack !== 'dockercompose') { $code = $exception->getCode(); + ray($code); if ($code !== 69420) { // 69420 means failed to push the image to the registry, so we don't need to remove the new version as it is the currently running one $this->application_deployment_queue->addLogEntry("Deployment failed. Removing the new version of your application.", 'stderr'); @@ -1675,6 +1684,5 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf"); ); } } - $this->next(ApplicationDeploymentStatus::FAILED->value); } } diff --git a/app/Livewire/Admin/Index.php b/app/Livewire/Admin/Index.php index 27e912eed..c6b1d0a34 100644 --- a/app/Livewire/Admin/Index.php +++ b/app/Livewire/Admin/Index.php @@ -15,30 +15,26 @@ class Index extends Component if (!isCloud()) { return redirect()->route('dashboard'); } - if (auth()->user()->id !== 0 && session('adminToken') === null) { + if (auth()->user()->id !== 0) { return redirect()->route('dashboard'); } $this->users = User::whereHas('teams', function ($query) { $query->whereRelation('subscription', 'stripe_subscription_id', '!=', null); - })->get(); + })->get()->filter(function ($user) { + return $user->id !== 0; + }); } public function switchUser(int $user_id) { - $user = User::find($user_id); - auth()->login($user); - - if ($user_id === 0) { - Cache::forget('team:0'); - session()->forget('adminToken'); - } else { - $token_payload = [ - 'valid' => true, - ]; - $token = Crypt::encrypt($token_payload); - session(['adminToken' => $token]); + if (auth()->user()->id !== 0) { + return redirect()->route('dashboard'); } - session()->regenerate(); - return refreshSession(); + $user = User::find($user_id); + $team_to_switch_to = $user->teams->first(); + Cache::forget("team:{$user->id}"); + auth()->login($user); + refreshSession($team_to_switch_to); + return redirect(request()->header('Referer')); } public function render() { diff --git a/app/Livewire/Dashboard.php b/app/Livewire/Dashboard.php index a44cd18af..632f14d65 100644 --- a/app/Livewire/Dashboard.php +++ b/app/Livewire/Dashboard.php @@ -23,8 +23,8 @@ class Dashboard extends Component public function cleanup_queue() { $this->dispatch('success', 'Cleanup started.'); - Artisan::queue('app:init', [ - '--cleanup-deployments' => 'true' + Artisan::queue('cleanup:application-deployment-queue', [ + '--team-id' => currentTeam()->id ]); } public function get_deployments() diff --git a/app/Livewire/Project/Database/Import.php b/app/Livewire/Project/Database/Import.php index c111753d5..42683c161 100644 --- a/app/Livewire/Project/Database/Import.php +++ b/app/Livewire/Project/Database/Import.php @@ -29,8 +29,8 @@ class Import extends Component public string $container; public array $importCommands = []; public string $postgresqlRestoreCommand = 'pg_restore -U $POSTGRES_USER -d $POSTGRES_DB'; - public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p $MYSQL_PASSWORD $MYSQL_DATABASE'; - public string $mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p $MARIADB_PASSWORD $MARIADB_DATABASE'; + public string $mysqlRestoreCommand = 'mysql -u $MYSQL_USER -p$MYSQL_PASSWORD $MYSQL_DATABASE'; + public string $mariadbRestoreCommand = 'mariadb -u $MARIADB_USER -p$MARIADB_PASSWORD $MARIADB_DATABASE'; public function getListeners() { diff --git a/app/Livewire/Project/Service/Navbar.php b/app/Livewire/Project/Service/Navbar.php index fda86321b..5640084b9 100644 --- a/app/Livewire/Project/Service/Navbar.php +++ b/app/Livewire/Project/Service/Navbar.php @@ -64,7 +64,7 @@ class Navbar extends Component StopService::run($this->service); $this->service->refresh(); if ($forceCleanup) { - $this->dispatch('success', 'Force cleanup service.'); + $this->dispatch('success', 'Containers cleaned up.'); } else { $this->dispatch('success', 'Service stopped.'); } diff --git a/app/Livewire/Project/Shared/ExecuteContainerCommand.php b/app/Livewire/Project/Shared/ExecuteContainerCommand.php index 3687669a9..a33716e72 100644 --- a/app/Livewire/Project/Shared/ExecuteContainerCommand.php +++ b/app/Livewire/Project/Shared/ExecuteContainerCommand.php @@ -107,6 +107,9 @@ class ExecuteContainerCommand extends Component { $this->validate(); try { + if ($this->server->isForceDisabled()) { + throw new \RuntimeException('Server is disabled.'); + } // Wrap command to prevent escaped execution in the host. $cmd = 'sh -c "if [ -f ~/.profile ]; then . ~/.profile; fi; ' . str_replace('"', '\"', $this->command) . '"'; if (!empty($this->workDir)) { diff --git a/app/Models/Server.php b/app/Models/Server.php index fc400935a..73e10a587 100644 --- a/app/Models/Server.php +++ b/app/Models/Server.php @@ -147,11 +147,11 @@ class Server extends BaseModel public function skipServer() { if ($this->ip === '1.2.3.4') { - ray('skipping 1.2.3.4'); + // ray('skipping 1.2.3.4'); return true; } if ($this->settings->force_disabled === true) { - ray('force_disabled'); + // ray('force_disabled'); return true; } return false; diff --git a/app/Traits/ExecuteRemoteCommand.php b/app/Traits/ExecuteRemoteCommand.php index 9b34fabae..529dacd7a 100644 --- a/app/Traits/ExecuteRemoteCommand.php +++ b/app/Traits/ExecuteRemoteCommand.php @@ -24,12 +24,6 @@ trait ExecuteRemoteCommand if ($this->server instanceof Server === false) { throw new \RuntimeException('Server is not set or is not an instance of Server model'); } - if ($this->server->settings->force_disabled) { - $this->application_deployment_queue->update([ - 'status' => ApplicationDeploymentStatus::FAILED->value, - ]); - throw new \RuntimeException('Server is disabled'); - } $commandsText->each(function ($single_command) { $command = data_get($single_command, 'command') ?? $single_command[0] ?? null; if ($command === null) { diff --git a/bootstrap/helpers/remoteProcess.php b/bootstrap/helpers/remoteProcess.php index cec7e82aa..13905391e 100644 --- a/bootstrap/helpers/remoteProcess.php +++ b/bootstrap/helpers/remoteProcess.php @@ -110,6 +110,9 @@ function instant_scp(string $source, string $dest, Server $server, $throwError = } function generateSshCommand(Server $server, string $command) { + if ($server->settings->force_disabled) { + throw new \RuntimeException('Server is disabled.'); + } $user = $server->user; $port = $server->port; $privateKeyLocation = savePrivateKeyToFs($server); diff --git a/config/sentry.php b/config/sentry.php index 40a4c9906..96c7207e0 100644 --- a/config/sentry.php +++ b/config/sentry.php @@ -7,7 +7,7 @@ return [ // The release version of your application // Example with dynamic git hash: trim(exec('git --git-dir ' . base_path('.git') . ' log --pretty="%h" -n1 HEAD')) - 'release' => '4.0.0-beta.225', + 'release' => '4.0.0-beta.226', // When left empty or `null` the Laravel environment will be used 'environment' => config('app.env'), diff --git a/config/version.php b/config/version.php index c3f92910d..b0f6d7453 100644 --- a/config/version.php +++ b/config/version.php @@ -1,3 +1,3 @@ user()->name }}