feat: slack notifications
This commit is contained in:
58
app/Jobs/SendMessageToSlackJob.php
Normal file
58
app/Jobs/SendMessageToSlackJob.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class SendMessageToSlackJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct(
|
||||
private SlackMessage $message,
|
||||
private string $webhookUrl
|
||||
) {
|
||||
}
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
Http::post($this->webhookUrl, [
|
||||
'blocks' => [
|
||||
[
|
||||
'type' => 'section',
|
||||
'text' => [
|
||||
'type' => 'plain_text',
|
||||
'text' => "Coolify Notification",
|
||||
],
|
||||
],
|
||||
],
|
||||
'attachments' => [
|
||||
[
|
||||
'color' => $this->message->color,
|
||||
'blocks' => [
|
||||
[
|
||||
'type' => 'header',
|
||||
'text' => [
|
||||
'type' => 'plain_text',
|
||||
'text' => $this->message->title,
|
||||
],
|
||||
],
|
||||
[
|
||||
'type' => 'section',
|
||||
'text' => [
|
||||
'type' => 'mrkdwn',
|
||||
'text' => $this->message->description
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
130
app/Livewire/Notifications/Slack.php
Normal file
130
app/Livewire/Notifications/Slack.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Notifications;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Livewire\Attributes\Validate;
|
||||
use Livewire\Component;
|
||||
|
||||
class Slack extends Component
|
||||
{
|
||||
public Team $team;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackEnabled = false;
|
||||
|
||||
#[Validate(['url', 'nullable'])]
|
||||
public ?string $slackWebhookUrl = null;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsTest = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsDeployments = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsStatusChanges = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsDatabaseBackups = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsScheduledTasks = false;
|
||||
|
||||
#[Validate(['boolean'])]
|
||||
public bool $slackNotificationsServerDiskUsage = false;
|
||||
|
||||
public function mount()
|
||||
{
|
||||
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->slack_enabled = $this->slackEnabled;
|
||||
$this->team->slack_webhook_url = $this->slackWebhookUrl;
|
||||
$this->team->slack_notifications_test = $this->slackNotificationsTest;
|
||||
$this->team->slack_notifications_deployments = $this->slackNotificationsDeployments;
|
||||
$this->team->slack_notifications_status_changes = $this->slackNotificationsStatusChanges;
|
||||
$this->team->slack_notifications_database_backups = $this->slackNotificationsDatabaseBackups;
|
||||
$this->team->slack_notifications_scheduled_tasks = $this->slackNotificationsScheduledTasks;
|
||||
$this->team->slack_notifications_server_disk_usage = $this->slackNotificationsServerDiskUsage;
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
} else {
|
||||
$this->slackEnabled = $this->team->slack_enabled;
|
||||
$this->slackWebhookUrl = $this->team->slack_webhook_url;
|
||||
$this->slackNotificationsTest = $this->team->slack_notifications_test;
|
||||
$this->slackNotificationsDeployments = $this->team->slack_notifications_deployments;
|
||||
$this->slackNotificationsStatusChanges = $this->team->slack_notifications_status_changes;
|
||||
$this->slackNotificationsDatabaseBackups = $this->team->slack_notifications_database_backups;
|
||||
$this->slackNotificationsScheduledTasks = $this->team->slack_notifications_scheduled_tasks;
|
||||
$this->slackNotificationsServerDiskUsage = $this->team->slack_notifications_server_disk_usage;
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSaveSlackEnabled()
|
||||
{
|
||||
try {
|
||||
$this->validate([
|
||||
'slackWebhookUrl' => 'required',
|
||||
], [
|
||||
'slackWebhookUrl.required' => 'Slack Webhook URL is required.',
|
||||
]);
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
$this->slackEnabled = false;
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->syncData(true);
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
try {
|
||||
$this->resetErrorBag();
|
||||
$this->syncData(true);
|
||||
$this->saveModel();
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function saveModel()
|
||||
{
|
||||
$this->syncData(true);
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
}
|
||||
|
||||
public function sendTestNotification()
|
||||
{
|
||||
try {
|
||||
$this->team->notify(new Test);
|
||||
$this->dispatch('success', 'Test notification sent.');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.notifications.slack');
|
||||
}
|
||||
}
|
@@ -4,6 +4,7 @@ namespace App\Models;
|
||||
|
||||
use App\Notifications\Channels\SendsDiscord;
|
||||
use App\Notifications\Channels\SendsEmail;
|
||||
use App\Notifications\Channels\SendsSlack;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
@@ -70,7 +71,7 @@ use OpenApi\Attributes as OA;
|
||||
),
|
||||
]
|
||||
)]
|
||||
class Team extends Model implements SendsDiscord, SendsEmail
|
||||
class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack
|
||||
{
|
||||
use Notifiable;
|
||||
|
||||
@@ -127,6 +128,11 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
||||
];
|
||||
}
|
||||
|
||||
public function routeNotificationForSlack()
|
||||
{
|
||||
return data_get($this, 'slack_webhook_url', null);
|
||||
}
|
||||
|
||||
public function getRecepients($notification)
|
||||
{
|
||||
$recipients = data_get($notification, 'emails', null);
|
||||
|
@@ -5,6 +5,7 @@ namespace App\Notifications\Application;
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationPreview;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -133,4 +134,31 @@ class DeploymentFailed extends Notification implements ShouldQueue
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
if ($this->preview) {
|
||||
$title = "Pull request #{$this->preview->pull_request_id} deployment failed";
|
||||
$description = "Pull request deployment failed for {$this->application_name}";
|
||||
if ($this->preview->fqdn) {
|
||||
$description .= "\nPreview URL: {$this->preview->fqdn}";
|
||||
}
|
||||
} else {
|
||||
$title = "Deployment failed";
|
||||
$description = "Deployment failed for {$this->application_name}";
|
||||
if ($this->fqdn) {
|
||||
$description .= "\nApplication URL: {$this->fqdn}";
|
||||
}
|
||||
}
|
||||
|
||||
$description .= "\n\n**Project:** " . data_get($this->application, 'environment.project.name');
|
||||
$description .= "\n**Environment:** {$this->environment_name}";
|
||||
$description .= "\n**Deployment Logs:** {$this->deployment_url}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
class DeploymentSuccess extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
@@ -148,4 +148,32 @@ class DeploymentSuccess extends Notification implements ShouldQueue
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
if ($this->preview) {
|
||||
$title = "Pull request #{$this->preview->pull_request_id} successfully deployed";
|
||||
$description = "New version successfully deployed for {$this->application_name}";
|
||||
if ($this->preview->fqdn) {
|
||||
$description .= "\nPreview URL: {$this->preview->fqdn}";
|
||||
}
|
||||
} else {
|
||||
$title = "New version successfully deployed";
|
||||
$description = "New version successfully deployed for {$this->application_name}";
|
||||
if ($this->fqdn) {
|
||||
$description .= "\nApplication URL: {$this->fqdn}";
|
||||
}
|
||||
}
|
||||
|
||||
$description .= "\n\n**Project:** " . data_get($this->application, 'environment.project.name');
|
||||
$description .= "\n**Environment:** {$this->environment_name}";
|
||||
$description .= "\n**Deployment Logs:** {$this->deployment_url}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::successColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
class StatusChanged extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
@@ -80,4 +80,20 @@ class StatusChanged extends Notification implements ShouldQueue
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Application stopped";
|
||||
$description = "{$this->resource_name} has been stopped";
|
||||
|
||||
$description .= "\n\n**Project:** " . data_get($this->resource, 'environment.project.name');
|
||||
$description .= "\n**Environment:** {$this->environment_name}";
|
||||
$description .= "\n**Application URL:** {$this->resource_url}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
8
app/Notifications/Channels/SendsSlack.php
Normal file
8
app/Notifications/Channels/SendsSlack.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications\Channels;
|
||||
|
||||
interface SendsSlack
|
||||
{
|
||||
public function routeNotificationForSlack();
|
||||
}
|
22
app/Notifications/Channels/SlackChannel.php
Normal file
22
app/Notifications/Channels/SlackChannel.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications\Channels;
|
||||
|
||||
use App\Jobs\SendMessageToSlackJob;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class SlackChannel
|
||||
{
|
||||
/**
|
||||
* Send the given notification.
|
||||
*/
|
||||
public function send(SendsSlack $notifiable, Notification $notification): void
|
||||
{
|
||||
$message = $notification->toSlack();
|
||||
$webhookUrl = $notifiable->routeNotificationForSlack();
|
||||
if (!$webhookUrl) {
|
||||
return;
|
||||
}
|
||||
dispatch(new SendMessageToSlackJob($message, $webhookUrl))->onQueue('high');
|
||||
}
|
||||
}
|
@@ -8,14 +8,16 @@ use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
class ContainerRestarted extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct(public string $name, public Server $server, public ?string $url = null) {}
|
||||
public function __construct(public string $name, public Server $server, public ?string $url = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
@@ -69,4 +71,20 @@ class ContainerRestarted extends Notification implements ShouldQueue
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Resource restarted";
|
||||
$description = "A resource ({$this->name}) has been restarted automatically on {$this->server->name}";
|
||||
|
||||
if ($this->url) {
|
||||
$description .= "\n**Resource URL:** {$this->url}";
|
||||
}
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::warningColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -8,14 +8,16 @@ use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
class ContainerStopped extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct(public string $name, public Server $server, public ?string $url = null) {}
|
||||
public function __construct(public string $name, public Server $server, public ?string $url = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
@@ -69,4 +71,20 @@ class ContainerStopped extends Notification implements ShouldQueue
|
||||
|
||||
return $payload;
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Resource stopped";
|
||||
$description = "A resource ({$this->name}) has been stopped unexpectedly on {$this->server->name}";
|
||||
|
||||
if ($this->url) {
|
||||
$description .= "\n**Resource URL:** {$this->url}";
|
||||
}
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
class BackupFailed extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
@@ -69,4 +69,19 @@ class BackupFailed extends Notification implements ShouldQueue
|
||||
'message' => $message,
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Database backup failed";
|
||||
$description = "Database backup for {$this->name} (db:{$this->database_name}) has FAILED.";
|
||||
|
||||
$description .= "\n\n**Frequency:** {$this->frequency}";
|
||||
$description .= "\n\n**Error Output:**\n{$this->output}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
class BackupSuccess extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
@@ -66,4 +66,18 @@ class BackupSuccess extends Notification implements ShouldQueue
|
||||
'message' => $message,
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Database backup successful";
|
||||
$description = "Database backup for {$this->name} (db:{$this->database_name}) was successful.";
|
||||
|
||||
$description .= "\n\n**Frequency:** {$this->frequency}";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::successColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
28
app/Notifications/Dto/SlackMessage.php
Normal file
28
app/Notifications/Dto/SlackMessage.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Notifications\Dto;
|
||||
|
||||
class SlackMessage
|
||||
{
|
||||
public function __construct(
|
||||
public string $title,
|
||||
public string $description,
|
||||
public string $color = '#0099ff'
|
||||
) {
|
||||
}
|
||||
|
||||
public static function infoColor(): string
|
||||
{
|
||||
return '#0099ff';
|
||||
}
|
||||
|
||||
public static function errorColor(): string
|
||||
{
|
||||
return '#ff0000';
|
||||
}
|
||||
|
||||
public static function successColor(): string
|
||||
{
|
||||
return '#00ff00';
|
||||
}
|
||||
}
|
@@ -4,7 +4,9 @@ namespace App\Notifications\Internal;
|
||||
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Notification;
|
||||
@@ -15,13 +17,16 @@ class GeneralNotification extends Notification implements ShouldQueue
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct(public string $message) {}
|
||||
public function __construct(public string $message)
|
||||
{
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
$channels = [];
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
@@ -29,6 +34,9 @@ class GeneralNotification extends Notification implements ShouldQueue
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -48,4 +56,13 @@ class GeneralNotification extends Notification implements ShouldQueue
|
||||
'message' => $this->message,
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
return new SlackMessage(
|
||||
title: 'Coolify: General Notification',
|
||||
description: $this->message,
|
||||
color: SlackMessage::infoColor(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -8,7 +8,7 @@ use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
class TaskFailed extends Notification implements ShouldQueue
|
||||
{
|
||||
use Queueable;
|
||||
@@ -75,4 +75,24 @@ class TaskFailed extends Notification implements ShouldQueue
|
||||
'message' => $message,
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Scheduled task failed";
|
||||
$description = "Scheduled task ({$this->task->name}) failed.";
|
||||
|
||||
if ($this->output) {
|
||||
$description .= "\n\n**Error Output:**\n{$this->output}";
|
||||
}
|
||||
|
||||
if ($this->url) {
|
||||
$description .= "\n\n**Task URL:** {$this->url}";
|
||||
}
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -6,6 +6,7 @@ use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Notification;
|
||||
@@ -16,7 +17,9 @@ class DockerCleanup extends Notification implements ShouldQueue
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct(public Server $server, public string $message) {}
|
||||
public function __construct(public Server $server, public string $message)
|
||||
{
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
@@ -24,7 +27,7 @@ class DockerCleanup extends Notification implements ShouldQueue
|
||||
// $isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
}
|
||||
@@ -34,6 +37,9 @@ class DockerCleanup extends Notification implements ShouldQueue
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -65,4 +71,13 @@ class DockerCleanup extends Notification implements ShouldQueue
|
||||
'message' => "Coolify: Server '{$this->server->name}' cleanup job done!\n\n{$this->message}",
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
return new SlackMessage(
|
||||
title: 'Server cleanup job done',
|
||||
description: "Server '{$this->server->name}' cleanup job done!\n\n{$this->message}",
|
||||
color: SlackMessage::successColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,9 @@ use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -18,7 +20,9 @@ class ForceDisabled extends Notification implements ShouldQueue
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct(public Server $server) {}
|
||||
public function __construct(public Server $server)
|
||||
{
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
@@ -26,7 +30,7 @@ class ForceDisabled extends Notification implements ShouldQueue
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
}
|
||||
@@ -36,6 +40,9 @@ class ForceDisabled extends Notification implements ShouldQueue
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -70,4 +77,19 @@ class ForceDisabled extends Notification implements ShouldQueue
|
||||
'message' => "Coolify: Server ({$this->server->name}) disabled because it is not paid!\n All automations and integrations are stopped.\nPlease update your subscription to enable the server again [here](https://app.coolify.io/subscriptions).",
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$title = "Server disabled";
|
||||
$description = "Server ({$this->server->name}) disabled because it is not paid!\n";
|
||||
$description .= "All automations and integrations are stopped.\n\n";
|
||||
$description .= "Please update your subscription to enable the server again: https://app.coolify.io/subscriptions";
|
||||
|
||||
return new SlackMessage(
|
||||
title: $title,
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,9 @@ use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -18,7 +20,9 @@ class ForceEnabled extends Notification implements ShouldQueue
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct(public Server $server) {}
|
||||
public function __construct(public Server $server)
|
||||
{
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
@@ -26,7 +30,7 @@ class ForceEnabled extends Notification implements ShouldQueue
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
}
|
||||
@@ -36,6 +40,9 @@ class ForceEnabled extends Notification implements ShouldQueue
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -66,4 +73,15 @@ class ForceEnabled extends Notification implements ShouldQueue
|
||||
'message' => "Coolify: Server ({$this->server->name}) enabled again!",
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
return new SlackMessage(
|
||||
title: 'Server enabled',
|
||||
description: "Server '{$this->server->name}' enabled again!",
|
||||
color: SlackMessage::successColor()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ namespace App\Notifications\Server;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -15,7 +16,9 @@ class HighDiskUsage extends Notification implements ShouldQueue
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct(public Server $server, public int $disk_usage, public int $server_disk_usage_notification_threshold) {}
|
||||
public function __construct(public Server $server, public int $disk_usage, public int $server_disk_usage_notification_threshold)
|
||||
{
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
@@ -58,4 +61,22 @@ class HighDiskUsage extends Notification implements ShouldQueue
|
||||
'message' => "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->server_disk_usage_notification_threshold}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup.",
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$description = "Server '{$this->server->name}' high disk usage detected!\n";
|
||||
$description .= "Disk usage: {$this->disk_usage}%\n";
|
||||
$description .= "Threshold: {$this->server_disk_usage_notification_threshold}%\n\n";
|
||||
$description .= "Please cleanup your disk to prevent data-loss.\n";
|
||||
$description .= "Tips for cleanup: https://coolify.io/docs/knowledge-base/server/automated-cleanup\n";
|
||||
$description .= "Change settings:\n";
|
||||
$description .= "- Threshold: " . base_url() . "/server/" . $this->server->uuid . "#advanced\n";
|
||||
$description .= "- Notifications: " . base_url() . "/notifications/discord";
|
||||
|
||||
return new SlackMessage(
|
||||
title: 'High disk usage detected',
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,9 @@ use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -37,7 +39,7 @@ class Reachable extends Notification implements ShouldQueue
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
}
|
||||
@@ -47,6 +49,9 @@ class Reachable extends Notification implements ShouldQueue
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -77,4 +82,16 @@ class Reachable extends Notification implements ShouldQueue
|
||||
'message' => "Coolify: Server '{$this->server->name}' revived. All automations & integrations are turned on again!",
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
return new SlackMessage(
|
||||
title: "Server revived",
|
||||
description: "Server '{$this->server->name}' revived.\nAll automations & integrations are turned on again!",
|
||||
color: SlackMessage::successColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,9 @@ use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -37,6 +39,7 @@ class Unreachable extends Notification implements ShouldQueue
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
@@ -47,6 +50,9 @@ class Unreachable extends Notification implements ShouldQueue
|
||||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
@@ -81,4 +87,18 @@ class Unreachable extends Notification implements ShouldQueue
|
||||
'message' => "Coolify: Your server '{$this->server->name}' is unreachable. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server and turn on all automations & integrations.",
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
$description = "Your server '{$this->server->name}' is unreachable.\n";
|
||||
$description .= "All automations & integrations are turned off!\n\n";
|
||||
$description .= "*IMPORTANT:* We automatically try to revive your server and turn on all automations & integrations.";
|
||||
|
||||
return new SlackMessage(
|
||||
title: 'Server unreachable',
|
||||
description: $description,
|
||||
color: SlackMessage::errorColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@
|
||||
namespace App\Notifications;
|
||||
|
||||
use App\Notifications\Dto\DiscordMessage;
|
||||
use App\Notifications\Dto\SlackMessage;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
@@ -15,7 +16,9 @@ class Test extends Notification implements ShouldQueue
|
||||
|
||||
public $tries = 5;
|
||||
|
||||
public function __construct(public ?string $emails = null) {}
|
||||
public function __construct(public ?string $emails = null)
|
||||
{
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
@@ -64,4 +67,12 @@ class Test extends Notification implements ShouldQueue
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function toSlack(): SlackMessage
|
||||
{
|
||||
return new SlackMessage(
|
||||
title: 'Test Slack Notification',
|
||||
description: 'This is a test Slack notification from Coolify.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@@ -26,6 +26,7 @@ use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\SlackChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Internal\GeneralNotification;
|
||||
use Carbon\CarbonImmutable;
|
||||
@@ -440,11 +441,13 @@ function setNotificationChannels($notifiable, $event)
|
||||
{
|
||||
$channels = [];
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isSlackEnabled = data_get($notifiable, 'slack_enabled');
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isSubscribedToEmailEvent = data_get($notifiable, "smtp_notifications_$event");
|
||||
$isSubscribedToDiscordEvent = data_get($notifiable, "discord_notifications_$event");
|
||||
$isSubscribedToTelegramEvent = data_get($notifiable, "telegram_notifications_$event");
|
||||
$isSubscribedToSlackEvent = data_get($notifiable, "slack_notifications_$event");
|
||||
|
||||
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
@@ -455,6 +458,9 @@ function setNotificationChannels($notifiable, $event)
|
||||
if ($isTelegramEnabled && $isSubscribedToTelegramEvent) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isSlackEnabled && $isSubscribedToSlackEvent) {
|
||||
$channels[] = SlackChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
|
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('teams', function (Blueprint $table) {
|
||||
$table->boolean('slack_enabled')->default(false);
|
||||
$table->string('slack_webhook_url')->nullable();
|
||||
$table->boolean('slack_notifications_test')->default(false);
|
||||
$table->boolean('slack_notifications_deployments')->default(false);
|
||||
$table->boolean('slack_notifications_status_changes')->default(false);
|
||||
$table->boolean('slack_notifications_database_backups')->default(false);
|
||||
$table->boolean('slack_notifications_scheduled_tasks')->default(false);
|
||||
$table->boolean('slack_notifications_server_disk_usage')->default(false);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('teams', function (Blueprint $table) {
|
||||
$table->dropColumn([
|
||||
'slack_enabled',
|
||||
'slack_webhook_url',
|
||||
'slack_notifications_test',
|
||||
'slack_notifications_deployments',
|
||||
'slack_notifications_status_changes',
|
||||
'slack_notifications_database_backups',
|
||||
'slack_notifications_scheduled_tasks',
|
||||
'slack_notifications_server_disk_usage',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
@@ -15,6 +15,10 @@
|
||||
href="{{ route('notifications.discord') }}">
|
||||
<button>Discord</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('notifications.slack') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('notifications.slack') }}">
|
||||
<button>Slack</button>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
43
resources/views/livewire/notifications/slack.blade.php
Normal file
43
resources/views/livewire/notifications/slack.blade.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<div>
|
||||
<x-slot:title>
|
||||
Notifications | Coolify
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Slack</h2>
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
@if ($slackEnabled)
|
||||
<x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
|
||||
wire:click="sendTestNotification">
|
||||
Send Test Notifications
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
<div class="w-32">
|
||||
<x-forms.checkbox instantSave="instantSaveSlackEnabled" id="slackEnabled" label="Enabled" />
|
||||
</div>
|
||||
<x-forms.input type="password"
|
||||
helper="Generate a webhook in Slack.<br>Example: https://hooks.slack.com/services/...." required
|
||||
id="slackWebhookUrl" label="Webhook" />
|
||||
</form>
|
||||
@if ($slackEnabled)
|
||||
<h2 class="mt-4">Subscribe to events</h2>
|
||||
<div class="w-64">
|
||||
@if (isDev())
|
||||
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsTest" label="Test" />
|
||||
@endif
|
||||
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsStatusChanges"
|
||||
label="Container Status Changes" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsDeployments"
|
||||
label="Application Deployments" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsDatabaseBackups" label="Backup Status" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsScheduledTasks"
|
||||
label="Scheduled Tasks Status" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="slackNotificationsServerDiskUsage"
|
||||
label="Server Disk Usage" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
@@ -133,6 +133,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
||||
Route::get('/email', NotificationEmail::class)->name('notifications.email');
|
||||
Route::get('/telegram', NotificationTelegram::class)->name('notifications.telegram');
|
||||
Route::get('/discord', NotificationDiscord::class)->name('notifications.discord');
|
||||
Route::get('/slack', App\Livewire\Notifications\Slack::class)->name('notifications.slack');
|
||||
});
|
||||
|
||||
Route::prefix('storages')->group(function () {
|
||||
|
Reference in New Issue
Block a user