mirror of
https://github.com/coollabsio/coolify.git
synced 2026-03-11 08:55:47 +00:00
403 lines
17 KiB
PHP
403 lines
17 KiB
PHP
<?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,
|
||
];
|
||
}
|
||
}
|