coolify/app/Notifications/Server/ServerPatchCheck.php

403 lines
17 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Notifications\Server;
use App\Models\Server;
use App\Notifications\CustomEmailNotification;
use App\Notifications\Dto\DiscordMessage;
use App\Notifications\Dto\PushoverMessage;
use App\Notifications\Dto\SlackMessage;
use Illuminate\Notifications\Messages\MailMessage;
class ServerPatchCheck extends CustomEmailNotification
{
public string $serverUrl;
public function __construct(public Server $server, public array $patchData)
{
$this->onQueue('high');
$this->serverUrl = base_url().'/server/'.$this->server->uuid.'/security/patches';
}
public function via(object $notifiable): array
{
return $notifiable->getEnabledChannels('server_patch');
}
public function toMail($notifiable = null): MailMessage
{
$mail = new MailMessage;
// Handle error case
if (isset($this->patchData['error'])) {
$mail->subject("Coolify: [ERROR] Failed to check patches on {$this->server->name}");
$mail->view('emails.server-patches-error', [
'name' => $this->server->name,
'error' => $this->patchData['error'],
'osId' => $this->patchData['osId'] ?? 'unknown',
'package_manager' => $this->patchData['package_manager'] ?? 'unknown',
'server_url' => $this->serverUrl,
]);
return $mail;
}
$totalUpdates = $this->patchData['total_updates'] ?? 0;
$mail->subject("Coolify: [ACTION REQUIRED] {$totalUpdates} server patches available on {$this->server->name}");
$mail->view('emails.server-patches', [
'name' => $this->server->name,
'total_updates' => $totalUpdates,
'updates' => $this->patchData['updates'] ?? [],
'osId' => $this->patchData['osId'] ?? 'unknown',
'package_manager' => $this->patchData['package_manager'] ?? 'unknown',
'server_url' => $this->serverUrl,
]);
return $mail;
}
public function toDiscord(): DiscordMessage
{
// Handle error case
if (isset($this->patchData['error'])) {
$osId = $this->patchData['osId'] ?? 'unknown';
$packageManager = $this->patchData['package_manager'] ?? 'unknown';
$error = $this->patchData['error'];
$description = "**Failed to check for updates** on server {$this->server->name}\n\n";
$description .= "**Error Details:**\n";
$description .= '• OS: '.ucfirst($osId)."\n";
$description .= "• Package Manager: {$packageManager}\n";
$description .= "• Error: {$error}\n\n";
$description .= "[Manage Server]($this->serverUrl)";
return new DiscordMessage(
title: ':x: Coolify: [ERROR] Failed to check patches on '.$this->server->name,
description: $description,
color: DiscordMessage::errorColor(),
);
}
$totalUpdates = $this->patchData['total_updates'] ?? 0;
$updates = $this->patchData['updates'] ?? [];
$osId = $this->patchData['osId'] ?? 'unknown';
$packageManager = $this->patchData['package_manager'] ?? 'unknown';
// Check for critical packages
$criticalPackages = collect($updates)->filter(function ($update) {
return str_contains(strtolower($update['package']), 'docker') ||
str_contains(strtolower($update['package']), 'kernel') ||
str_contains(strtolower($update['package']), 'openssh') ||
str_contains(strtolower($update['package']), 'ssl');
});
$hasCriticalPackages = $criticalPackages->count() > 0;
$description = "**{$totalUpdates} package updates** available for server {$this->server->name}\n\n";
$description .= "**Summary:**\n";
$description .= '• OS: '.ucfirst($osId)."\n";
$description .= "• Package Manager: {$packageManager}\n";
$description .= "• Total Updates: {$totalUpdates}\n\n";
// Show first few packages
if (count($updates) > 0) {
$description .= "**Updates:**\n";
$sampleUpdates = array_slice($updates, 0, 5);
foreach ($sampleUpdates as $update) {
$description .= "{$update['package']}: {$update['current_version']}{$update['new_version']}\n";
}
if (count($updates) > 5) {
$description .= '• ... and '.(count($updates) - 5)." more packages\n";
}
if ($hasCriticalPackages) {
$description .= "\n **Critical packages detected** ({$criticalPackages->count()} packages may require restarts)";
}
$description .= "\n [Manage Server Patches]($this->serverUrl)";
}
// Use warning color for critical packages, info color otherwise
$color = $hasCriticalPackages ? DiscordMessage::warningColor() : DiscordMessage::infoColor();
$icon = $hasCriticalPackages ? ':warning:' : ':information_source:';
return new DiscordMessage(
title: "{$icon} Coolify: [ACTION REQUIRED] Server patches available on ".$this->server->name,
description: $description,
color: $color,
);
}
public function toTelegram(): array
{
// Handle error case
if (isset($this->patchData['error'])) {
$osId = $this->patchData['osId'] ?? 'unknown';
$packageManager = $this->patchData['package_manager'] ?? 'unknown';
$error = $this->patchData['error'];
$message = "❌ Coolify: [ERROR] Failed to check patches on {$this->server->name}!\n\n";
$message .= "📊 Error Details:\n";
$message .= '• OS: '.ucfirst($osId)."\n";
$message .= "• Package Manager: {$packageManager}\n";
$message .= "• Error: {$error}\n\n";
return [
'message' => $message,
'buttons' => [
[
'text' => 'Manage Server',
'url' => $this->serverUrl,
],
],
];
}
$totalUpdates = $this->patchData['total_updates'] ?? 0;
$updates = $this->patchData['updates'] ?? [];
$osId = $this->patchData['osId'] ?? 'unknown';
$packageManager = $this->patchData['package_manager'] ?? 'unknown';
// Check for critical packages
$criticalPackages = collect($updates)->filter(function ($update) {
return str_contains(strtolower($update['package']), 'docker') ||
str_contains(strtolower($update['package']), 'kernel') ||
str_contains(strtolower($update['package']), 'openssh') ||
str_contains(strtolower($update['package']), 'ssl');
});
$hasCriticalPackages = $criticalPackages->count() > 0;
// Use warning emoji for critical packages, info emoji otherwise
$icon = $hasCriticalPackages ? '⚠️' : '';
$message = "{$icon} Coolify: [ACTION REQUIRED] {$totalUpdates} server patches available on {$this->server->name}!\n\n";
$message .= "📊 Summary:\n";
$message .= '• OS: '.ucfirst($osId)."\n";
$message .= "• Package Manager: {$packageManager}\n";
$message .= "• Total Updates: {$totalUpdates}\n\n";
if (count($updates) > 0) {
$message .= "📦 Updates:\n";
$sampleUpdates = array_slice($updates, 0, 5);
foreach ($sampleUpdates as $update) {
$message .= "{$update['package']}: {$update['current_version']}{$update['new_version']}\n";
}
if (count($updates) > 5) {
$message .= '• ... and '.(count($updates) - 5)." more packages\n";
}
if ($hasCriticalPackages) {
$message .= "\n⚠️ Critical packages detected: {$criticalPackages->count()} packages may require restarts\n";
foreach ($criticalPackages->take(3) as $package) {
$message .= "{$package['package']}: {$package['current_version']}{$package['new_version']}\n";
}
if ($criticalPackages->count() > 3) {
$message .= '• ... and '.($criticalPackages->count() - 3)." more critical packages\n";
}
}
}
return [
'message' => $message,
'buttons' => [
[
'text' => 'Manage Server Patches',
'url' => $this->serverUrl,
],
],
];
}
public function toPushover(): PushoverMessage
{
// Handle error case
if (isset($this->patchData['error'])) {
$osId = $this->patchData['osId'] ?? 'unknown';
$packageManager = $this->patchData['package_manager'] ?? 'unknown';
$error = $this->patchData['error'];
$message = "[ERROR] Failed to check patches on {$this->server->name}!\n\n";
$message .= "Error Details:\n";
$message .= '• OS: '.ucfirst($osId)."\n";
$message .= "• Package Manager: {$packageManager}\n";
$message .= "• Error: {$error}\n\n";
return new PushoverMessage(
title: 'Server patch check failed',
level: 'error',
message: $message,
buttons: [
[
'text' => 'Manage Server',
'url' => $this->serverUrl,
],
],
);
}
$totalUpdates = $this->patchData['total_updates'] ?? 0;
$updates = $this->patchData['updates'] ?? [];
$osId = $this->patchData['osId'] ?? 'unknown';
$packageManager = $this->patchData['package_manager'] ?? 'unknown';
// Check for critical packages
$criticalPackages = collect($updates)->filter(function ($update) {
return str_contains(strtolower($update['package']), 'docker') ||
str_contains(strtolower($update['package']), 'kernel') ||
str_contains(strtolower($update['package']), 'openssh') ||
str_contains(strtolower($update['package']), 'ssl');
});
$hasCriticalPackages = $criticalPackages->count() > 0;
$message = "[ACTION REQUIRED] {$totalUpdates} server patches available on {$this->server->name}!\n\n";
$message .= "Summary:\n";
$message .= '• OS: '.ucfirst($osId)."\n";
$message .= "• Package Manager: {$packageManager}\n";
$message .= "• Total Updates: {$totalUpdates}\n\n";
if (count($updates) > 0) {
$message .= "Updates:\n";
$sampleUpdates = array_slice($updates, 0, 3);
foreach ($sampleUpdates as $update) {
$message .= "{$update['package']}: {$update['current_version']}{$update['new_version']}\n";
}
if (count($updates) > 3) {
$message .= '• ... and '.(count($updates) - 3)." more packages\n";
}
if ($hasCriticalPackages) {
$message .= "\nCritical packages detected: {$criticalPackages->count()} may require restarts";
}
}
return new PushoverMessage(
title: 'Server patches available',
level: $hasCriticalPackages ? 'warning' : 'info',
message: $message,
buttons: [
[
'text' => 'Manage Server Patches',
'url' => $this->serverUrl,
],
],
);
}
public function toSlack(): SlackMessage
{
// Handle error case
if (isset($this->patchData['error'])) {
$osId = $this->patchData['osId'] ?? 'unknown';
$packageManager = $this->patchData['package_manager'] ?? 'unknown';
$error = $this->patchData['error'];
$description = "Failed to check patches on '{$this->server->name}'!\n\n";
$description .= "*Error Details:*\n";
$description .= '• OS: '.ucfirst($osId)."\n";
$description .= "• Package Manager: {$packageManager}\n";
$description .= "• Error: `{$error}`\n\n";
$description .= "\n:link: <{$this->serverUrl}|Manage Server>";
return new SlackMessage(
title: 'Coolify: [ERROR] Server patch check failed',
description: $description,
color: SlackMessage::errorColor()
);
}
$totalUpdates = $this->patchData['total_updates'] ?? 0;
$updates = $this->patchData['updates'] ?? [];
$osId = $this->patchData['osId'] ?? 'unknown';
$packageManager = $this->patchData['package_manager'] ?? 'unknown';
// Check for critical packages
$criticalPackages = collect($updates)->filter(function ($update) {
return str_contains(strtolower($update['package']), 'docker') ||
str_contains(strtolower($update['package']), 'kernel') ||
str_contains(strtolower($update['package']), 'openssh') ||
str_contains(strtolower($update['package']), 'ssl');
});
$hasCriticalPackages = $criticalPackages->count() > 0;
$description = "{$totalUpdates} server patches available on '{$this->server->name}'!\n\n";
$description .= "*Summary:*\n";
$description .= '• OS: '.ucfirst($osId)."\n";
$description .= "• Package Manager: {$packageManager}\n";
$description .= "• Total Updates: {$totalUpdates}\n\n";
if (count($updates) > 0) {
$description .= "*Updates:*\n";
$sampleUpdates = array_slice($updates, 0, 5);
foreach ($sampleUpdates as $update) {
$description .= "• `{$update['package']}`: {$update['current_version']}{$update['new_version']}\n";
}
if (count($updates) > 5) {
$description .= '• ... and '.(count($updates) - 5)." more packages\n";
}
if ($hasCriticalPackages) {
$description .= "\n:warning: *Critical packages detected:* {$criticalPackages->count()} packages may require restarts\n";
foreach ($criticalPackages->take(3) as $package) {
$description .= "• `{$package['package']}`: {$package['current_version']}{$package['new_version']}\n";
}
if ($criticalPackages->count() > 3) {
$description .= '• ... and '.($criticalPackages->count() - 3)." more critical packages\n";
}
}
}
$description .= "\n:link: <{$this->serverUrl}|Manage Server Patches>";
return new SlackMessage(
title: 'Coolify: [ACTION REQUIRED] Server patches available',
description: $description,
color: $hasCriticalPackages ? SlackMessage::warningColor() : SlackMessage::infoColor()
);
}
public function toWebhook(): array
{
// Handle error case
if (isset($this->patchData['error'])) {
return [
'success' => false,
'message' => 'Failed to check patches',
'event' => 'server_patch_check_error',
'server_name' => $this->server->name,
'server_uuid' => $this->server->uuid,
'os_id' => $this->patchData['osId'] ?? 'unknown',
'package_manager' => $this->patchData['package_manager'] ?? 'unknown',
'error' => $this->patchData['error'],
'url' => $this->serverUrl,
];
}
$totalUpdates = $this->patchData['total_updates'] ?? 0;
$updates = $this->patchData['updates'] ?? [];
// Check for critical packages
$criticalPackages = collect($updates)->filter(function ($update) {
return str_contains(strtolower($update['package']), 'docker') ||
str_contains(strtolower($update['package']), 'kernel') ||
str_contains(strtolower($update['package']), 'openssh') ||
str_contains(strtolower($update['package']), 'ssl');
});
return [
'success' => true,
'message' => 'Server patches available',
'event' => 'server_patch_check',
'server_name' => $this->server->name,
'server_uuid' => $this->server->uuid,
'total_updates' => $totalUpdates,
'os_id' => $this->patchData['osId'] ?? 'unknown',
'package_manager' => $this->patchData['package_manager'] ?? 'unknown',
'updates' => $updates,
'critical_packages_count' => $criticalPackages->count(),
'url' => $this->serverUrl,
];
}
}