Drop restart-limit fields and enforcement from database resources, clear cached Traefik version data when proxies change, and show missing service environment variables from disabled deploy actions.
204 lines
6.6 KiB
PHP
204 lines
6.6 KiB
PHP
<?php
|
|
|
|
namespace App\Jobs;
|
|
|
|
use App\Enums\ProxyStatus;
|
|
use App\Enums\ProxyTypes;
|
|
use App\Events\ProxyStatusChangedUI;
|
|
use App\Models\Server;
|
|
use App\Notifications\Server\TraefikVersionOutdated;
|
|
use Illuminate\Bus\Queueable;
|
|
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Bus\Dispatchable;
|
|
use Illuminate\Queue\InteractsWithQueue;
|
|
use Illuminate\Queue\SerializesModels;
|
|
|
|
class CheckTraefikVersionForServerJob implements ShouldBeEncrypted, ShouldQueue
|
|
{
|
|
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
|
|
|
public $tries = 3;
|
|
|
|
public $timeout = 60;
|
|
|
|
/**
|
|
* Create a new job instance.
|
|
*/
|
|
public function __construct(
|
|
public Server $server,
|
|
public array $traefikVersions
|
|
) {}
|
|
|
|
/**
|
|
* Execute the job.
|
|
*/
|
|
public function handle(): void
|
|
{
|
|
$this->server->refresh();
|
|
$this->clearOutdatedInfo();
|
|
|
|
if ($this->server->proxyType() !== ProxyTypes::TRAEFIK->value || $this->server->proxy->get('status') !== ProxyStatus::RUNNING->value) {
|
|
return;
|
|
}
|
|
|
|
// Detect current version (makes SSH call)
|
|
$currentVersion = getTraefikVersionFromDockerCompose($this->server);
|
|
|
|
$this->server->update(['detected_traefik_version' => $currentVersion]);
|
|
|
|
if (! $currentVersion) {
|
|
ProxyStatusChangedUI::dispatch($this->server->team_id);
|
|
|
|
return;
|
|
}
|
|
|
|
// Check if image tag is 'latest' by inspecting the image (makes SSH call)
|
|
$imageTag = instant_remote_process([
|
|
"docker inspect coolify-proxy --format '{{.Config.Image}}' 2>/dev/null",
|
|
], $this->server, false);
|
|
|
|
// Handle empty/null response from SSH command
|
|
if (empty(trim($imageTag))) {
|
|
ProxyStatusChangedUI::dispatch($this->server->team_id);
|
|
|
|
return;
|
|
}
|
|
|
|
if (str_contains(strtolower(trim($imageTag)), ':latest')) {
|
|
ProxyStatusChangedUI::dispatch($this->server->team_id);
|
|
|
|
return;
|
|
}
|
|
|
|
// Parse current version to extract major.minor.patch
|
|
$current = ltrim($currentVersion, 'v');
|
|
if (! preg_match('/^(\d+\.\d+)\.(\d+)$/', $current, $matches)) {
|
|
ProxyStatusChangedUI::dispatch($this->server->team_id);
|
|
|
|
return;
|
|
}
|
|
|
|
$currentBranch = $matches[1]; // e.g., "3.6"
|
|
|
|
// Find the latest version for this branch
|
|
$latestForBranch = $this->traefikVersions["v{$currentBranch}"] ?? null;
|
|
|
|
if (! $latestForBranch) {
|
|
// User is on a branch we don't track - check if newer branches exist
|
|
$newerBranchInfo = $this->getNewerBranchInfo($currentBranch);
|
|
|
|
if ($newerBranchInfo) {
|
|
$this->storeOutdatedInfo($current, $newerBranchInfo['latest'], 'minor_upgrade', $newerBranchInfo['target']);
|
|
} else {
|
|
// No newer branch found, clear outdated info
|
|
$this->server->update(['traefik_outdated_info' => null]);
|
|
}
|
|
|
|
ProxyStatusChangedUI::dispatch($this->server->team_id);
|
|
|
|
return;
|
|
}
|
|
|
|
// Compare patch version within the same branch
|
|
$latest = ltrim($latestForBranch, 'v');
|
|
|
|
// Always check for newer branches first
|
|
$newerBranchInfo = $this->getNewerBranchInfo($currentBranch);
|
|
|
|
if (version_compare($current, $latest, '<')) {
|
|
// Patch update available
|
|
$this->storeOutdatedInfo($current, $latest, 'patch_update', null, $newerBranchInfo);
|
|
} elseif ($newerBranchInfo) {
|
|
// Only newer branch available (no patch update)
|
|
$this->storeOutdatedInfo($current, $newerBranchInfo['latest'], 'minor_upgrade', $newerBranchInfo['target']);
|
|
} else {
|
|
// Fully up to date
|
|
$this->server->update(['traefik_outdated_info' => null]);
|
|
}
|
|
|
|
// Dispatch UI update event so warning state refreshes in real-time
|
|
ProxyStatusChangedUI::dispatch($this->server->team_id);
|
|
}
|
|
|
|
private function clearOutdatedInfo(): void
|
|
{
|
|
$this->server->update([
|
|
'detected_traefik_version' => null,
|
|
'traefik_outdated_info' => null,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get information about newer branches if available.
|
|
*/
|
|
private function getNewerBranchInfo(string $currentBranch): ?array
|
|
{
|
|
$newestBranch = null;
|
|
$newestVersion = null;
|
|
|
|
foreach ($this->traefikVersions as $branch => $version) {
|
|
$branchNum = ltrim($branch, 'v');
|
|
if (version_compare($branchNum, $currentBranch, '>')) {
|
|
if (! $newestVersion || version_compare($version, $newestVersion, '>')) {
|
|
$newestBranch = $branchNum;
|
|
$newestVersion = $version;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($newestVersion) {
|
|
return [
|
|
'target' => "v{$newestBranch}",
|
|
'latest' => ltrim($newestVersion, 'v'),
|
|
];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Store outdated information in database and send immediate notification.
|
|
*/
|
|
private function storeOutdatedInfo(string $current, string $latest, string $type, ?string $upgradeTarget = null, ?array $newerBranchInfo = null): void
|
|
{
|
|
$outdatedInfo = [
|
|
'current' => $current,
|
|
'latest' => $latest,
|
|
'type' => $type,
|
|
'checked_at' => now()->toIso8601String(),
|
|
];
|
|
|
|
// For minor upgrades, add the upgrade_target field (e.g., "v3.6")
|
|
if ($type === 'minor_upgrade' && $upgradeTarget) {
|
|
$outdatedInfo['upgrade_target'] = $upgradeTarget;
|
|
}
|
|
|
|
// If there's a newer branch available (even for patch updates), include that info
|
|
if ($newerBranchInfo) {
|
|
$outdatedInfo['newer_branch_target'] = $newerBranchInfo['target'];
|
|
$outdatedInfo['newer_branch_latest'] = $newerBranchInfo['latest'];
|
|
}
|
|
|
|
$this->server->update(['traefik_outdated_info' => $outdatedInfo]);
|
|
|
|
// Send immediate notification to the team
|
|
$this->sendNotification($outdatedInfo);
|
|
}
|
|
|
|
/**
|
|
* Send notification to team about outdated Traefik.
|
|
*/
|
|
private function sendNotification(array $outdatedInfo): void
|
|
{
|
|
// Attach the outdated info as a dynamic property for the notification
|
|
$this->server->outdatedInfo = $outdatedInfo;
|
|
|
|
// Get the team and send notification
|
|
$team = $this->server->team()->first();
|
|
|
|
if ($team) {
|
|
$team->notify(new TraefikVersionOutdated(collect([$this->server])));
|
|
}
|
|
}
|
|
}
|