diff --git a/app/Console/Commands/NotifyDemo.php b/app/Console/Commands/NotifyDemo.php
index f0131b7b2..b6e5015f8 100644
--- a/app/Console/Commands/NotifyDemo.php
+++ b/app/Console/Commands/NotifyDemo.php
@@ -62,6 +62,7 @@ class NotifyDemo extends Command
slack
discord
telegram
+
pushover
@@ -72,6 +73,6 @@ class NotifyDemo extends Command
In which manner you wish a coolified notification?
- HTML, ['email', 'slack', 'discord', 'telegram']);
+ HTML, ['email', 'slack', 'discord', 'telegram', 'pushover']);
}
}
diff --git a/app/Jobs/SendMessageToPushoverJob.php b/app/Jobs/SendMessageToPushoverJob.php
new file mode 100644
index 000000000..834a32b07
--- /dev/null
+++ b/app/Jobs/SendMessageToPushoverJob.php
@@ -0,0 +1,50 @@
+onQueue('high');
+ }
+
+ /**
+ * Execute the job.
+ */
+ public function handle(): void
+ {
+ $response = Http::post('https://api.pushover.net/1/messages.json', $this->message->toPayload($this->token, $this->user));
+ if ($response->failed()) {
+ throw new \RuntimeException('Pushover notification failed with ' . $response->status() . ' status code.' . $response->body());
+ }
+ }
+}
diff --git a/app/Livewire/Notifications/Pushover.php b/app/Livewire/Notifications/Pushover.php
new file mode 100644
index 000000000..7a3b294a2
--- /dev/null
+++ b/app/Livewire/Notifications/Pushover.php
@@ -0,0 +1,176 @@
+team = auth()->user()->currentTeam();
+ $this->settings = $this->team->pushoverNotificationSettings;
+ $this->syncData();
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function syncData(bool $toModel = false)
+ {
+ if ($toModel) {
+ $this->validate();
+ $this->settings->pushover_enabled = $this->pushoverEnabled;
+ $this->settings->pushover_user = $this->pushoverUser;
+ $this->settings->pushover_token = $this->pushoverToken;
+
+ $this->settings->deployment_success_pushover_notifications = $this->deploymentSuccessPushoverNotifications;
+ $this->settings->deployment_failure_pushover_notifications = $this->deploymentFailurePushoverNotifications;
+ $this->settings->status_change_pushover_notifications = $this->statusChangePushoverNotifications;
+ $this->settings->backup_success_pushover_notifications = $this->backupSuccessPushoverNotifications;
+ $this->settings->backup_failure_pushover_notifications = $this->backupFailurePushoverNotifications;
+ $this->settings->scheduled_task_success_pushover_notifications = $this->scheduledTaskSuccessPushoverNotifications;
+ $this->settings->scheduled_task_failure_pushover_notifications = $this->scheduledTaskFailurePushoverNotifications;
+ $this->settings->docker_cleanup_success_pushover_notifications = $this->dockerCleanupSuccessPushoverNotifications;
+ $this->settings->docker_cleanup_failure_pushover_notifications = $this->dockerCleanupFailurePushoverNotifications;
+ $this->settings->server_disk_usage_pushover_notifications = $this->serverDiskUsagePushoverNotifications;
+ $this->settings->server_reachable_pushover_notifications = $this->serverReachablePushoverNotifications;
+ $this->settings->server_unreachable_pushover_notifications = $this->serverUnreachablePushoverNotifications;
+
+ $this->settings->save();
+ refreshSession();
+ } else {
+ $this->pushoverEnabled = $this->settings->pushover_enabled;
+ $this->pushoverUser = $this->settings->pushover_user;
+ $this->pushoverToken = $this->settings->pushover_token;
+
+ $this->deploymentSuccessPushoverNotifications = $this->settings->deployment_success_pushover_notifications;
+ $this->deploymentFailurePushoverNotifications = $this->settings->deployment_failure_pushover_notifications;
+ $this->statusChangePushoverNotifications = $this->settings->status_change_pushover_notifications;
+ $this->backupSuccessPushoverNotifications = $this->settings->backup_success_pushover_notifications;
+ $this->backupFailurePushoverNotifications = $this->settings->backup_failure_pushover_notifications;
+ $this->scheduledTaskSuccessPushoverNotifications = $this->settings->scheduled_task_success_pushover_notifications;
+ $this->scheduledTaskFailurePushoverNotifications = $this->settings->scheduled_task_failure_pushover_notifications;
+ $this->dockerCleanupSuccessPushoverNotifications = $this->settings->docker_cleanup_success_pushover_notifications;
+ $this->dockerCleanupFailurePushoverNotifications = $this->settings->docker_cleanup_failure_pushover_notifications;
+ $this->serverDiskUsagePushoverNotifications = $this->settings->server_disk_usage_pushover_notifications;
+ $this->serverReachablePushoverNotifications = $this->settings->server_reachable_pushover_notifications;
+ $this->serverUnreachablePushoverNotifications = $this->settings->server_unreachable_pushover_notifications;
+ }
+ }
+
+ public function instantSavePushoverEnabled()
+ {
+ try {
+ $this->validate([
+ 'pushoverUser' => 'required',
+ 'pushoverToken' => 'required',
+ ], [
+ 'pushoverUser.required' => 'Pushover User is required.',
+ 'pushoverToken.required' => 'Pushover Token is required.',
+ ]);
+ $this->saveModel();
+ } catch (\Throwable $e) {
+ $this->pushoverEnabled = 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(channel: 'pushover'));
+ $this->dispatch('success', 'Test notification sent.');
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function render()
+ {
+ return view('livewire.notifications.pushover');
+ }
+}
diff --git a/app/Models/PushoverNotificationSettings.php b/app/Models/PushoverNotificationSettings.php
new file mode 100644
index 000000000..7576a6040
--- /dev/null
+++ b/app/Models/PushoverNotificationSettings.php
@@ -0,0 +1,61 @@
+ 'boolean',
+ 'pushover_user' => 'encrypted',
+ 'pushover_token' => 'encrypted',
+
+ 'deployment_success_pushover_notifications' => 'boolean',
+ 'deployment_failure_pushover_notifications' => 'boolean',
+ 'status_change_pushover_notifications' => 'boolean',
+ 'backup_success_pushover_notifications' => 'boolean',
+ 'backup_failure_pushover_notifications' => 'boolean',
+ 'scheduled_task_success_pushover_notifications' => 'boolean',
+ 'scheduled_task_failure_pushover_notifications' => 'boolean',
+ 'docker_cleanup_pushover_notifications' => 'boolean',
+ 'server_disk_usage_pushover_notifications' => 'boolean',
+ 'server_reachable_pushover_notifications' => 'boolean',
+ 'server_unreachable_pushover_notifications' => 'boolean',
+ ];
+
+ public function team()
+ {
+ return $this->belongsTo(Team::class);
+ }
+
+ public function isEnabled()
+ {
+ return $this->pushover_enabled;
+ }
+}
diff --git a/app/Models/Team.php b/app/Models/Team.php
index 07424a55f..fc10892a4 100644
--- a/app/Models/Team.php
+++ b/app/Models/Team.php
@@ -5,6 +5,7 @@ namespace App\Models;
use App\Notifications\Channels\SendsDiscord;
use App\Notifications\Channels\SendsEmail;
use App\Notifications\Channels\SendsSlack;
+use App\Notifications\Channels\SendsPushover;
use App\Traits\HasNotificationSettings;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
@@ -32,7 +33,7 @@ use OpenApi\Attributes as OA;
]
)]
-class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack
+class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack, SendsPushover
{
use HasNotificationSettings, Notifiable;
@@ -49,6 +50,7 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack
$team->discordNotificationSettings()->create();
$team->slackNotificationSettings()->create();
$team->telegramNotificationSettings()->create();
+ $team->pushoverNotificationSettings()->create();
});
static::saving(function ($team) {
@@ -154,6 +156,14 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack
{
return data_get($this, 'slack_webhook_url', null);
}
+
+ public function routeNotificationForPushover()
+ {
+ return [
+ 'user' => data_get($this, 'pushover_user', null),
+ 'token' => data_get($this, 'pushover_token', null),
+ ];
+ }
public function getRecipients($notification)
{
@@ -174,7 +184,8 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack
return $this->getNotificationSettings('email')?->isEnabled() ||
$this->getNotificationSettings('discord')?->isEnabled() ||
$this->getNotificationSettings('slack')?->isEnabled() ||
- $this->getNotificationSettings('telegram')?->isEnabled();
+ $this->getNotificationSettings('telegram')?->isEnabled() ||
+ $this->getNotificationSettings('pushover')?->isEnabled();
}
public function subscriptionEnded()
@@ -276,4 +287,9 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack
{
return $this->hasOne(SlackNotificationSettings::class);
}
+
+ public function pushoverNotificationSettings()
+ {
+ return $this->hasOne(PushoverNotificationSettings::class);
+ }
}
diff --git a/app/Notifications/Application/DeploymentFailed.php b/app/Notifications/Application/DeploymentFailed.php
index 06a2b48e3..80c1c421c 100644
--- a/app/Notifications/Application/DeploymentFailed.php
+++ b/app/Notifications/Application/DeploymentFailed.php
@@ -6,6 +6,7 @@ use App\Models\Application;
use App\Models\ApplicationPreview;
use App\Notifications\CustomEmailNotification;
use App\Notifications\Dto\DiscordMessage;
+use App\Notifications\Dto\PushoverMessage;
use App\Notifications\Dto\SlackMessage;
use Illuminate\Notifications\Messages\MailMessage;
@@ -130,6 +131,31 @@ class DeploymentFailed extends CustomEmailNotification
];
}
+ public function toPushover(): PushoverMessage
+ {
+ if ($this->preview) {
+ $title = "Pull request #{$this->preview->pull_request_id} deployment failed";
+ $message = "Pull request deployment failed for {$this->application_name}";
+ } else {
+ $title = 'Deployment failed';
+ $message = "Deployment failed for {$this->application_name}";
+ }
+
+ $buttons[] = [
+ 'text' => 'Deployment logs',
+ 'url' => $this->deployment_url,
+ ];
+
+ return new PushoverMessage(
+ title: $title,
+ level: 'error',
+ message: $message,
+ buttons: [
+ ...$buttons,
+ ],
+ );
+ }
+
public function toSlack(): SlackMessage
{
if ($this->preview) {
diff --git a/app/Notifications/Application/DeploymentSuccess.php b/app/Notifications/Application/DeploymentSuccess.php
index 548f5c6e5..b1a3d5225 100644
--- a/app/Notifications/Application/DeploymentSuccess.php
+++ b/app/Notifications/Application/DeploymentSuccess.php
@@ -6,6 +6,7 @@ use App\Models\Application;
use App\Models\ApplicationPreview;
use App\Notifications\CustomEmailNotification;
use App\Notifications\Dto\DiscordMessage;
+use App\Notifications\Dto\PushoverMessage;
use App\Notifications\Dto\SlackMessage;
use Illuminate\Notifications\Messages\MailMessage;
@@ -139,6 +140,42 @@ class DeploymentSuccess extends CustomEmailNotification
];
}
+ public function toPushover(): PushoverMessage
+ {
+ if ($this->preview) {
+ $title = "Pull request #{$this->preview->pull_request_id} successfully deployed";
+ $message = 'New PR' . $this->preview->pull_request_id . ' version successfully deployed of ' . $this->application_name . '';
+ if ($this->preview->fqdn) {
+ $buttons[] = [
+ 'text' => 'Open Application',
+ 'url' => $this->preview->fqdn,
+ ];
+ }
+ } else {
+ $title = 'New version successfully deployed';
+ $message = 'New version successfully deployed of ' . $this->application_name . '';
+ if ($this->fqdn) {
+ $buttons[] = [
+ 'text' => 'Open Application',
+ 'url' => $this->fqdn,
+ ];
+ }
+ }
+ $buttons[] = [
+ 'text' => 'Deployment logs',
+ 'url' => $this->deployment_url,
+ ];
+
+ return new PushoverMessage(
+ title: $title,
+ level: 'success',
+ message: $message,
+ buttons: [
+ ...$buttons,
+ ],
+ );
+ }
+
public function toSlack(): SlackMessage
{
if ($this->preview) {
diff --git a/app/Notifications/Application/StatusChanged.php b/app/Notifications/Application/StatusChanged.php
index 32a6a659a..c9c7344c4 100644
--- a/app/Notifications/Application/StatusChanged.php
+++ b/app/Notifications/Application/StatusChanged.php
@@ -5,6 +5,7 @@ namespace App\Notifications\Application;
use App\Models\Application;
use App\Notifications\CustomEmailNotification;
use App\Notifications\Dto\DiscordMessage;
+use App\Notifications\Dto\PushoverMessage;
use App\Notifications\Dto\SlackMessage;
use Illuminate\Notifications\Messages\MailMessage;
@@ -77,6 +78,23 @@ class StatusChanged extends CustomEmailNotification
];
}
+ public function toPushover(): PushoverMessage
+ {
+ $message = $this->resource_name . ' has been stopped.';
+
+ return new PushoverMessage(
+ title: 'Application stopped',
+ level: 'error',
+ message: $message,
+ buttons: [
+ [
+ 'text' => 'Open Application in Coolify',
+ 'url' => $this->resource_url,
+ ],
+ ],
+ );
+ }
+
public function toSlack(): SlackMessage
{
$title = 'Application stopped';
diff --git a/app/Notifications/Channels/PushoverChannel.php b/app/Notifications/Channels/PushoverChannel.php
new file mode 100644
index 000000000..56d3cb03d
--- /dev/null
+++ b/app/Notifications/Channels/PushoverChannel.php
@@ -0,0 +1,21 @@
+toPushover();
+ $pushoverSettings = $notifiable->pushoverNotificationSettings;
+
+ if (! $pushoverSettings || ! $pushoverSettings->isEnabled() || ! $pushoverSettings->pushover_user || ! $pushoverSettings->pushover_token) {
+ return;
+ }
+
+ SendMessageToPushoverJob::dispatch($message, $pushoverSettings->pushover_token, $pushoverSettings->pushover_user);
+ }
+}
diff --git a/app/Notifications/Channels/SendsPushover.php b/app/Notifications/Channels/SendsPushover.php
new file mode 100644
index 000000000..7922eefb4
--- /dev/null
+++ b/app/Notifications/Channels/SendsPushover.php
@@ -0,0 +1,8 @@
+url) {
+ $buttons[] = [
+ 'text' => 'Check Proxy in Coolify',
+ 'url' => $this->url,
+ ];
+ }
+
+ return new PushoverMessage(
+ title: 'Resource restarted',
+ level: 'warning',
+ message: "A resource ({$this->name}) has been restarted automatically on {$this->server->name}",
+ buttons: $buttons,
+ );
+ }
+
public function toSlack(): SlackMessage
{
$title = 'Resource restarted';
diff --git a/app/Notifications/Container/ContainerStopped.php b/app/Notifications/Container/ContainerStopped.php
index bc6e52b6d..49aea196d 100644
--- a/app/Notifications/Container/ContainerStopped.php
+++ b/app/Notifications/Container/ContainerStopped.php
@@ -5,6 +5,7 @@ namespace App\Notifications\Container;
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;
@@ -68,6 +69,25 @@ class ContainerStopped extends CustomEmailNotification
return $payload;
}
+ public function toPushover(): PushoverMessage
+ {
+ $buttons = [];
+ if ($this->url) {
+ $buttons[] = [
+ 'text' => 'Open Application in Coolify',
+ 'url' => $this->url,
+ ];
+ }
+
+ return new PushoverMessage(
+ title: 'Resource stopped',
+ level: 'error',
+ message: "A resource ({$this->name}) has been stopped unexpectedly on {$this->server->name}",
+ buttons: $buttons,
+ );
+ }
+
+
public function toSlack(): SlackMessage
{
$title = 'Resource stopped';
diff --git a/app/Notifications/Database/BackupFailed.php b/app/Notifications/Database/BackupFailed.php
index 2208f7b1d..6dcb70583 100644
--- a/app/Notifications/Database/BackupFailed.php
+++ b/app/Notifications/Database/BackupFailed.php
@@ -5,6 +5,7 @@ namespace App\Notifications\Database;
use App\Models\ScheduledDatabaseBackup;
use App\Notifications\CustomEmailNotification;
use App\Notifications\Dto\DiscordMessage;
+use App\Notifications\Dto\PushoverMessage;
use App\Notifications\Dto\SlackMessage;
use Illuminate\Notifications\Messages\MailMessage;
@@ -64,6 +65,15 @@ class BackupFailed extends CustomEmailNotification
];
}
+ public function toPushover(): PushoverMessage
+ {
+ return new PushoverMessage(
+ title: 'Database backup failed',
+ level: 'error',
+ message: "Database backup for {$this->name} (db:{$this->database_name}) was FAILED
Frequency: {$this->frequency} . Reason: {$this->output}",
+ );
+ }
+
public function toSlack(): SlackMessage
{
$title = 'Database backup failed';
diff --git a/app/Notifications/Database/BackupSuccess.php b/app/Notifications/Database/BackupSuccess.php
index 10b4ff3df..4c3e8e060 100644
--- a/app/Notifications/Database/BackupSuccess.php
+++ b/app/Notifications/Database/BackupSuccess.php
@@ -5,6 +5,7 @@ namespace App\Notifications\Database;
use App\Models\ScheduledDatabaseBackup;
use App\Notifications\CustomEmailNotification;
use App\Notifications\Dto\DiscordMessage;
+use App\Notifications\Dto\PushoverMessage;
use App\Notifications\Dto\SlackMessage;
use Illuminate\Notifications\Messages\MailMessage;
@@ -62,6 +63,17 @@ class BackupSuccess extends CustomEmailNotification
];
}
+
+ public function toPushover(): PushoverMessage
+ {
+ return new PushoverMessage(
+ title: 'Database backup successful',
+ level: 'success',
+ message: "Database backup for {$this->name} (db:{$this->database_name}) was successful.
Frequency: {$this->frequency}.",
+ );
+ }
+
+
public function toSlack(): SlackMessage
{
$title = 'Database backup successful';
diff --git a/app/Notifications/Dto/PushoverMessage.php b/app/Notifications/Dto/PushoverMessage.php
new file mode 100644
index 000000000..0efd1d526
--- /dev/null
+++ b/app/Notifications/Dto/PushoverMessage.php
@@ -0,0 +1,50 @@
+level) {
+ 'info' => 'ℹ️',
+ 'error' => '❌',
+ 'success' => '✅ ',
+ 'warning' => '⚠️',
+ };
+ }
+
+ public function toPayload(string $token, string $user): array
+ {
+ $levelIcon = $this->getLevelIcon();
+ $payload = [
+ 'token' => $token,
+ 'user' => $user,
+ 'title' => "{$levelIcon} {$this->title}",
+ 'message' => $this->message,
+ 'html' => 1,
+ ];
+
+ foreach ($this->buttons as $button) {
+ $buttonUrl = data_get($button, 'url');
+ $text = data_get($button, 'text', 'Click here');
+ if ($buttonUrl && str_contains($buttonUrl, 'http://localhost')) {
+ $buttonUrl = str_replace('http://localhost', config('app.url'), $buttonUrl);
+ }
+ $payload['message'] .= " " . $text . '';
+ }
+
+ Log::info('Pushover message', $payload);
+
+ return $payload;
+ }
+}
diff --git a/app/Notifications/Internal/GeneralNotification.php b/app/Notifications/Internal/GeneralNotification.php
index 7049e3055..1d2367210 100644
--- a/app/Notifications/Internal/GeneralNotification.php
+++ b/app/Notifications/Internal/GeneralNotification.php
@@ -3,6 +3,7 @@
namespace App\Notifications\Internal;
use App\Notifications\Dto\DiscordMessage;
+use App\Notifications\Dto\PushoverMessage;
use App\Notifications\Dto\SlackMessage;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
@@ -40,6 +41,15 @@ class GeneralNotification extends Notification implements ShouldQueue
];
}
+ public function toPushover(): PushoverMessage
+ {
+ return new PushoverMessage(
+ title: 'General Notification',
+ level: 'info',
+ message: $this->message,
+ );
+ }
+
public function toSlack(): SlackMessage
{
return new SlackMessage(
diff --git a/app/Notifications/ScheduledTask/TaskFailed.php b/app/Notifications/ScheduledTask/TaskFailed.php
index 56c410ecb..c4d92f213 100644
--- a/app/Notifications/ScheduledTask/TaskFailed.php
+++ b/app/Notifications/ScheduledTask/TaskFailed.php
@@ -5,6 +5,7 @@ namespace App\Notifications\ScheduledTask;
use App\Models\ScheduledTask;
use App\Notifications\CustomEmailNotification;
use App\Notifications\Dto\DiscordMessage;
+use App\Notifications\Dto\PushoverMessage;
use App\Notifications\Dto\SlackMessage;
use Illuminate\Notifications\Messages\MailMessage;
@@ -70,6 +71,30 @@ class TaskFailed extends CustomEmailNotification
];
}
+ public function toPushover(): PushoverMessage
+ {
+ $message = "Scheduled task ({$this->task->name}) failed ";
+
+ if ($this->output) {
+ $message .= " Error Output:{$this->output}";
+ }
+
+ $buttons = [];
+ if ($this->url) {
+ $buttons[] = [
+ 'text' => 'Open task in Coolify',
+ 'url' => (string) $this->url,
+ ];
+ }
+
+ return new PushoverMessage(
+ title: 'Scheduled task failed',
+ level: 'error',
+ message: $message,
+ buttons: $buttons,
+ );
+ }
+
public function toSlack(): SlackMessage
{
$title = 'Scheduled task failed';
diff --git a/app/Notifications/ScheduledTask/TaskSuccess.php b/app/Notifications/ScheduledTask/TaskSuccess.php
index fc79aea37..5d4154e7a 100644
--- a/app/Notifications/ScheduledTask/TaskSuccess.php
+++ b/app/Notifications/ScheduledTask/TaskSuccess.php
@@ -5,6 +5,7 @@ namespace App\Notifications\ScheduledTask;
use App\Models\ScheduledTask;
use App\Notifications\CustomEmailNotification;
use App\Notifications\Dto\DiscordMessage;
+use App\Notifications\Dto\PushoverMessage;
use App\Notifications\Dto\SlackMessage;
use Illuminate\Notifications\Messages\MailMessage;
@@ -70,6 +71,25 @@ class TaskSuccess extends CustomEmailNotification
];
}
+ public function toPushover(): PushoverMessage
+ {
+ $message = "Coolify: Scheduled task ({$this->task->name}) succeeded.";
+ $buttons = [];
+ if ($this->url) {
+ $buttons[] = [
+ 'text' => 'Open task in Coolify',
+ 'url' => (string) $this->url,
+ ];
+ }
+
+ return new PushoverMessage(
+ title: 'Scheduled task succeeded',
+ level: 'success',
+ message: $message,
+ buttons: $buttons,
+ );
+ }
+
public function toSlack(): SlackMessage
{
$title = 'Scheduled task succeeded';
diff --git a/app/Notifications/Server/DockerCleanupFailed.php b/app/Notifications/Server/DockerCleanupFailed.php
index 53714925c..0291eed19 100644
--- a/app/Notifications/Server/DockerCleanupFailed.php
+++ b/app/Notifications/Server/DockerCleanupFailed.php
@@ -5,6 +5,7 @@ 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;
@@ -48,6 +49,15 @@ class DockerCleanupFailed extends CustomEmailNotification
];
}
+ public function toPushover(): PushoverMessage
+ {
+ return new PushoverMessage(
+ title: 'Docker cleanup job failed',
+ level: 'error',
+ message: "[ACTION REQUIRED] Docker cleanup job failed on {$this->server->name}!\n\n{$this->message}",
+ );
+ }
+
public function toSlack(): SlackMessage
{
return new SlackMessage(
diff --git a/app/Notifications/Server/DockerCleanupSuccess.php b/app/Notifications/Server/DockerCleanupSuccess.php
index 85a819da2..1a652d189 100644
--- a/app/Notifications/Server/DockerCleanupSuccess.php
+++ b/app/Notifications/Server/DockerCleanupSuccess.php
@@ -5,6 +5,7 @@ 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;
@@ -48,6 +49,15 @@ class DockerCleanupSuccess extends CustomEmailNotification
];
}
+ public function toPushover(): PushoverMessage
+ {
+ return new PushoverMessage(
+ title: 'Docker cleanup job succeeded',
+ level: 'success',
+ message: "Docker cleanup job succeeded on {$this->server->name}!\n\n{$this->message}",
+ );
+ }
+
public function toSlack(): SlackMessage
{
return new SlackMessage(
diff --git a/app/Notifications/Server/ForceDisabled.php b/app/Notifications/Server/ForceDisabled.php
index e122849da..7a1f7bcbf 100644
--- a/app/Notifications/Server/ForceDisabled.php
+++ b/app/Notifications/Server/ForceDisabled.php
@@ -5,6 +5,7 @@ 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;
@@ -51,6 +52,15 @@ class ForceDisabled extends CustomEmailNotification
];
}
+ public function toPushover(): PushoverMessage
+ {
+ return new PushoverMessage(
+ title: 'Server disabled',
+ level: 'error',
+ message: "Server ({$this->server->name}) disabled because it is not paid!\n All automations and integrations are stopped. Please update your subscription to enable the server again [here](https://app.coolify.io/subscriptions).",
+ );
+ }
+
public function toSlack(): SlackMessage
{
$title = 'Server disabled';
diff --git a/app/Notifications/Server/ForceEnabled.php b/app/Notifications/Server/ForceEnabled.php
index a0e0a8b59..36dad3c60 100644
--- a/app/Notifications/Server/ForceEnabled.php
+++ b/app/Notifications/Server/ForceEnabled.php
@@ -5,6 +5,7 @@ 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;
@@ -47,6 +48,15 @@ class ForceEnabled extends CustomEmailNotification
];
}
+ public function toPushover(): PushoverMessage
+ {
+ return new PushoverMessage(
+ title: 'Server enabled',
+ level: 'success',
+ message: "Server ({$this->server->name}) enabled again!",
+ );
+ }
+
public function toSlack(): SlackMessage
{
return new SlackMessage(
diff --git a/app/Notifications/Server/HighDiskUsage.php b/app/Notifications/Server/HighDiskUsage.php
index 9f4826689..aea9abd03 100644
--- a/app/Notifications/Server/HighDiskUsage.php
+++ b/app/Notifications/Server/HighDiskUsage.php
@@ -5,6 +5,7 @@ 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;
@@ -57,6 +58,19 @@ class HighDiskUsage extends CustomEmailNotification
];
}
+ public function toPushover(): PushoverMessage
+ {
+ return new PushoverMessage(
+ title: 'High disk usage detected',
+ level: 'warning',
+ message: "Server '{$this->server->name}' high disk usage detected!
Disk usage: {$this->disk_usage}%. Threshold: {$this->server_disk_usage_notification_threshold}%. Please cleanup your disk to prevent data-loss.",
+ buttons: [
+ 'Change settings' => base_url().'/server/'.$this->server->uuid."#advanced",
+ 'Tips for cleanup' => "https://coolify.io/docs/knowledge-base/server/automated-cleanup",
+ ],
+ );
+ }
+
public function toSlack(): SlackMessage
{
$description = "Server '{$this->server->name}' high disk usage detected!\n";
diff --git a/app/Notifications/Server/Reachable.php b/app/Notifications/Server/Reachable.php
index 4917e04f8..e03aef6b7 100644
--- a/app/Notifications/Server/Reachable.php
+++ b/app/Notifications/Server/Reachable.php
@@ -5,6 +5,7 @@ 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;
@@ -49,6 +50,15 @@ class Reachable extends CustomEmailNotification
);
}
+ public function toPushover(): PushoverMessage
+ {
+ return new PushoverMessage(
+ title: 'Server revived',
+ message: "Server '{$this->server->name}' revived. All automations & integrations are turned on again!",
+ level: 'success',
+ );
+ }
+
public function toTelegram(): array
{
return [
diff --git a/app/Notifications/Server/Unreachable.php b/app/Notifications/Server/Unreachable.php
index 43f176d49..fe90cc610 100644
--- a/app/Notifications/Server/Unreachable.php
+++ b/app/Notifications/Server/Unreachable.php
@@ -5,6 +5,7 @@ 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;
@@ -60,6 +61,15 @@ class Unreachable extends CustomEmailNotification
];
}
+ public function toPushover(): PushoverMessage
+ {
+ return new PushoverMessage(
+ title: 'Server unreachable',
+ level: 'error',
+ message: "Your server '{$this->server->name}' is unreachable. All automations & integrations are turned off!
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";
diff --git a/app/Notifications/Test.php b/app/Notifications/Test.php
index da9098500..65971a0ee 100644
--- a/app/Notifications/Test.php
+++ b/app/Notifications/Test.php
@@ -6,7 +6,9 @@ use App\Notifications\Channels\DiscordChannel;
use App\Notifications\Channels\EmailChannel;
use App\Notifications\Channels\SlackChannel;
use App\Notifications\Channels\TelegramChannel;
+use App\Notifications\Channels\PushoverChannel;
use App\Notifications\Dto\DiscordMessage;
+use App\Notifications\Dto\PushoverMessage;
use App\Notifications\Dto\SlackMessage;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
@@ -33,6 +35,7 @@ class Test extends Notification implements ShouldQueue
'discord' => [DiscordChannel::class],
'telegram' => [TelegramChannel::class],
'slack' => [SlackChannel::class],
+ 'pushover' => [PushoverChannel::class],
default => [],
};
} else {
@@ -85,6 +88,20 @@ class Test extends Notification implements ShouldQueue
];
}
+ public function toPushover(): PushoverMessage
+ {
+ return new PushoverMessage(
+ title: 'Test Pushover Notification',
+ message: 'This is a test Pushover notification from Coolify.',
+ buttons: [
+ [
+ 'text' => 'Go to your dashboard',
+ 'url' => base_url(),
+ ],
+ ],
+ );
+ }
+
public function toSlack(): SlackMessage
{
return new SlackMessage(
diff --git a/app/Traits/HasNotificationSettings.php b/app/Traits/HasNotificationSettings.php
index 82cbda6ad..ef858d0b6 100644
--- a/app/Traits/HasNotificationSettings.php
+++ b/app/Traits/HasNotificationSettings.php
@@ -6,6 +6,7 @@ use App\Notifications\Channels\DiscordChannel;
use App\Notifications\Channels\EmailChannel;
use App\Notifications\Channels\SlackChannel;
use App\Notifications\Channels\TelegramChannel;
+use App\Notifications\Channels\PushoverChannel;
use Illuminate\Database\Eloquent\Model;
trait HasNotificationSettings
@@ -27,6 +28,7 @@ trait HasNotificationSettings
'discord' => $this->discordNotificationSettings,
'telegram' => $this->telegramNotificationSettings,
'slack' => $this->slackNotificationSettings,
+ 'pushover' => $this->pushoverNotificationSettings,
default => null,
};
}
@@ -73,6 +75,7 @@ trait HasNotificationSettings
'discord' => DiscordChannel::class,
'telegram' => TelegramChannel::class,
'slack' => SlackChannel::class,
+ 'pushover' => PushoverChannel::class,
];
if ($event === 'general') {
diff --git a/database/migrations/2024_12_11_135026_create_pushover_notification_settings_table.php b/database/migrations/2024_12_11_135026_create_pushover_notification_settings_table.php
new file mode 100644
index 000000000..907f7a052
--- /dev/null
+++ b/database/migrations/2024_12_11_135026_create_pushover_notification_settings_table.php
@@ -0,0 +1,47 @@
+id();
+ $table->foreignId('team_id')->constrained()->cascadeOnDelete();
+
+ $table->boolean('pushover_enabled')->default(false);
+ $table->text('pushover_user')->nullable();
+ $table->text('pushover_token')->nullable();
+
+ $table->boolean('deployment_success_pushover_notifications')->default(false);
+ $table->boolean('deployment_failure_pushover_notifications')->default(true);
+ $table->boolean('status_change_pushover_notifications')->default(false);
+ $table->boolean('backup_success_pushover_notifications')->default(false);
+ $table->boolean('backup_failure_pushover_notifications')->default(true);
+ $table->boolean('scheduled_task_success_pushover_notifications')->default(false);
+ $table->boolean('scheduled_task_failure_pushover_notifications')->default(true);
+ $table->boolean('docker_cleanup_success_pushover_notifications')->default(false);
+ $table->boolean('docker_cleanup_failure_pushover_notifications')->default(true);
+ $table->boolean('server_disk_usage_pushover_notifications')->default(true);
+ $table->boolean('server_reachable_pushover_notifications')->default(false);
+ $table->boolean('server_unreachable_pushover_notifications')->default(true);
+
+ $table->unique(['team_id']);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('pushover_notification_settings');
+ }
+};
+
diff --git a/resources/views/components/notification/navbar.blade.php b/resources/views/components/notification/navbar.blade.php
index c4dbd25af..0193cf3cd 100644
--- a/resources/views/components/notification/navbar.blade.php
+++ b/resources/views/components/notification/navbar.blade.php
@@ -19,6 +19,10 @@
href="{{ route('notifications.slack') }}">
+
+
+
-
\ No newline at end of file
+
diff --git a/resources/views/livewire/notifications/pushover.blade.php b/resources/views/livewire/notifications/pushover.blade.php
new file mode 100644
index 000000000..28a55516d
--- /dev/null
+++ b/resources/views/livewire/notifications/pushover.blade.php
@@ -0,0 +1,84 @@
+
+
+ Notifications | Coolify
+
+
+
+
Notification Settings
+
+ Select events for which you would like to receive Pushover notifications.
+
+
+
+
Deployments
+
+
+
+
+
+
+
+
Backups
+
+
+
+
+
+
+
Scheduled Tasks
+
+
+
+
+
+
+
Server
+
+
+
+
+
+
+
+
+
+
diff --git a/routes/web.php b/routes/web.php
index 3570fe0ed..38ce7f15c 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -14,6 +14,7 @@ use App\Livewire\Notifications\Discord as NotificationDiscord;
use App\Livewire\Notifications\Email as NotificationEmail;
use App\Livewire\Notifications\Slack as NotificationSlack;
use App\Livewire\Notifications\Telegram as NotificationTelegram;
+use App\Livewire\Notifications\Pushover as NotificationPushover;
use App\Livewire\Profile\Index as ProfileIndex;
use App\Livewire\Project\Application\Configuration as ApplicationConfiguration;
use App\Livewire\Project\Application\Deployment\Index as DeploymentIndex;
@@ -133,6 +134,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
Route::get('/telegram', NotificationTelegram::class)->name('notifications.telegram');
Route::get('/discord', NotificationDiscord::class)->name('notifications.discord');
Route::get('/slack', NotificationSlack::class)->name('notifications.slack');
+ Route::get('/pushover', NotificationPushover::class)->name('notifications.pushover');
});
Route::prefix('storages')->group(function () {