diff --git a/README.md b/README.md index dac48d127..56edffd31 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,8 @@ # Donations ### Special Sponsors -![image](https://github.com/user-attachments/assets/726fb63e-c3b8-4260-b3ac-06780605ec5d) +![image](https://github.com/user-attachments/assets/6022bc9c-8435-4d14-9497-8be230ed8cb1) + * [CCCareers](https://cccareers.org/) - A career development platform connecting coding bootcamp graduates with job opportunities in the tech industry. * [Hetzner](http://htznr.li/CoolifyXHetzner) - A German web hosting company offering affordable dedicated servers, cloud services, and web hosting solutions. @@ -52,7 +53,9 @@ ### Special Sponsors * [SupaGuide](https://supa.guide/?ref=coolify.io) - A comprehensive resource hub offering guides and tutorials for web development using Supabase. * [GoldenVM](https://billing.goldenvm.com/?ref=coolify.io) - A cloud hosting provider offering scalable infrastructure solutions for businesses of all sizes. * [Tigris](https://tigrisdata.com/?ref=coolify.io) - A fully managed serverless object storage service compatible with Amazon S3 API. Offers high performance, scalability, and built-in search capabilities for efficient data management. -* [Advin](https://coolify.ad.vin/?ref=coolify.io) - A digital advertising agency specializing in programmatic advertising and data-driven marketing strategies. +* [Cloudify.ro](https://cloudify.ro/?ref=coolify.io) - A cloud hosting provider offering scalable infrastructure solutions for businesses of all sizes. +* [Syntaxfm](https://syntax.fm/?ref=coolify.io) - Podcast for web developers. +* [PFGlabs](https://pfglabs.com/?ref=coolify.io) - Build real project with Golang. * [Treive](https://trieve.ai/?ref=coolify.io) - An AI-powered search and discovery platform for enhancing information retrieval in large datasets. * [Blacksmith](https://blacksmith.sh/?ref=coolify.io) - A cloud-native platform for automating infrastructure provisioning and management across multiple cloud providers. * [Brand Dev](https://brand.dev/?ref=coolify.io) - A web development agency specializing in creating custom digital experiences and brand identities. @@ -92,6 +95,9 @@ ## Github Sponsors ($40+) Michael Mazurczak Formbricks StartupFame +jyc.dev +BitLaunch +Internet Garden Jonas Jaeger JP Evercam diff --git a/app/Console/Commands/NotifyDemo.php b/app/Console/Commands/NotifyDemo.php index f0131b7b2..990a03869 100644 --- a/app/Console/Commands/NotifyDemo.php +++ b/app/Console/Commands/NotifyDemo.php @@ -59,9 +59,10 @@ private function showHelp()
Channels:
@@ -72,6 +73,6 @@ private function showHelp()
In which manner you wish a coolified notification?
- HTML, ['email', 'slack', 'discord', 'telegram']); + HTML, ['email', 'discord', 'telegram', 'slack', 'pushover']); } } diff --git a/app/Console/Commands/Seeder.php b/app/Console/Commands/Seeder.php new file mode 100644 index 000000000..e37b6a9d2 --- /dev/null +++ b/app/Console/Commands/Seeder.php @@ -0,0 +1,24 @@ +info('Seeder is enabled on this server.'); + $this->call('db:seed', ['--class' => 'ProductionSeeder', '--force' => true]); + exit(0); + } else { + $this->info('Seeder is disabled on this server.'); + exit(0); + } + } +} 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/Jobs/SendMessageToTelegramJob.php b/app/Jobs/SendMessageToTelegramJob.php index 85f4fc934..6b0a64ae3 100644 --- a/app/Jobs/SendMessageToTelegramJob.php +++ b/app/Jobs/SendMessageToTelegramJob.php @@ -32,7 +32,7 @@ public function __construct( public array $buttons, public string $token, public string $chatId, - public ?string $topicId = null, + public ?string $threadId = null, ) { $this->onQueue('high'); } @@ -67,8 +67,8 @@ public function handle(): void 'chat_id' => $this->chatId, 'text' => $this->text, ]; - if ($this->topicId) { - $payload['message_thread_id'] = $this->topicId; + if ($this->threadId) { + $payload['message_thread_id'] = $this->threadId; } $response = Http::post($url, $payload); if ($response->failed()) { diff --git a/app/Livewire/Notifications/Email.php b/app/Livewire/Notifications/Email.php index dc2a95e84..eac48cf77 100644 --- a/app/Livewire/Notifications/Email.php +++ b/app/Livewire/Notifications/Email.php @@ -14,8 +14,10 @@ class Email extends Component { protected $listeners = ['refresh' => '$refresh']; + #[Locked] public Team $team; + #[Locked] public EmailNotificationSettings $settings; #[Locked] diff --git a/app/Livewire/Notifications/Pushover.php b/app/Livewire/Notifications/Pushover.php new file mode 100644 index 000000000..f1e4c464d --- /dev/null +++ b/app/Livewire/Notifications/Pushover.php @@ -0,0 +1,184 @@ + '$refresh']; + + #[Locked] + public Team $team; + + #[Locked] + public PushoverNotificationSettings $settings; + + #[Validate(['boolean'])] + public bool $pushoverEnabled = false; + + #[Validate(['nullable', 'string'])] + public ?string $pushoverUserKey = null; + + #[Validate(['nullable', 'string'])] + public ?string $pushoverApiToken = null; + + #[Validate(['boolean'])] + public bool $deploymentSuccessPushoverNotifications = false; + + #[Validate(['boolean'])] + public bool $deploymentFailurePushoverNotifications = true; + + #[Validate(['boolean'])] + public bool $statusChangePushoverNotifications = false; + + #[Validate(['boolean'])] + public bool $backupSuccessPushoverNotifications = false; + + #[Validate(['boolean'])] + public bool $backupFailurePushoverNotifications = true; + + #[Validate(['boolean'])] + public bool $scheduledTaskSuccessPushoverNotifications = false; + + #[Validate(['boolean'])] + public bool $scheduledTaskFailurePushoverNotifications = true; + + #[Validate(['boolean'])] + public bool $dockerCleanupSuccessPushoverNotifications = false; + + #[Validate(['boolean'])] + public bool $dockerCleanupFailurePushoverNotifications = true; + + #[Validate(['boolean'])] + public bool $serverDiskUsagePushoverNotifications = true; + + #[Validate(['boolean'])] + public bool $serverReachablePushoverNotifications = false; + + #[Validate(['boolean'])] + public bool $serverUnreachablePushoverNotifications = true; + + public function mount() + { + try { + $this->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_key = $this->pushoverUserKey; + $this->settings->pushover_api_token = $this->pushoverApiToken; + + $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->pushoverUserKey = $this->settings->pushover_user_key; + $this->pushoverApiToken = $this->settings->pushover_api_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([ + 'pushoverUserKey' => 'required', + 'pushoverApiToken' => 'required', + ], [ + 'pushoverUserKey.required' => 'Pushover User Key is required.', + 'pushoverApiToken.required' => 'Pushover API Token is required.', + ]); + $this->saveModel(); + } catch (\Throwable $e) { + $this->pushoverEnabled = false; + + return handleError($e, $this); + } finally { + $this->dispatch('refresh'); + } + } + + public function instantSave() + { + try { + $this->syncData(true); + } catch (\Throwable $e) { + return handleError($e, $this); + } finally { + $this->dispatch('refresh'); + } + } + + 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/Livewire/Notifications/Slack.php b/app/Livewire/Notifications/Slack.php index 97464fa1c..f47ad8a97 100644 --- a/app/Livewire/Notifications/Slack.php +++ b/app/Livewire/Notifications/Slack.php @@ -5,13 +5,18 @@ use App\Models\SlackNotificationSettings; use App\Models\Team; use App\Notifications\Test; +use Livewire\Attributes\Locked; use Livewire\Attributes\Validate; use Livewire\Component; class Slack extends Component { + protected $listeners = ['refresh' => '$refresh']; + + #[Locked] public Team $team; + #[Locked] public SlackNotificationSettings $settings; #[Validate(['boolean'])] @@ -121,6 +126,8 @@ public function instantSaveSlackEnabled() $this->slackEnabled = false; return handleError($e, $this); + } finally { + $this->dispatch('refresh'); } } @@ -130,6 +137,8 @@ public function instantSave() $this->syncData(true); } catch (\Throwable $e) { return handleError($e, $this); + } finally { + $this->dispatch('refresh'); } } diff --git a/app/Livewire/Notifications/Telegram.php b/app/Livewire/Notifications/Telegram.php index de2fa9cdc..01afb4ac6 100644 --- a/app/Livewire/Notifications/Telegram.php +++ b/app/Livewire/Notifications/Telegram.php @@ -5,13 +5,18 @@ use App\Models\Team; use App\Models\TelegramNotificationSettings; use App\Notifications\Test; +use Livewire\Attributes\Locked; use Livewire\Attributes\Validate; use Livewire\Component; class Telegram extends Component { + protected $listeners = ['refresh' => '$refresh']; + + #[Locked] public Team $team; + #[Locked] public TelegramNotificationSettings $settings; #[Validate(['boolean'])] @@ -60,40 +65,40 @@ class Telegram extends Component public bool $serverUnreachableTelegramNotifications = true; #[Validate(['nullable', 'string'])] - public ?string $telegramNotificationsDeploymentSuccessTopicId = null; + public ?string $telegramNotificationsDeploymentSuccessThreadId = null; #[Validate(['nullable', 'string'])] - public ?string $telegramNotificationsDeploymentFailureTopicId = null; + public ?string $telegramNotificationsDeploymentFailureThreadId = null; #[Validate(['nullable', 'string'])] - public ?string $telegramNotificationsStatusChangeTopicId = null; + public ?string $telegramNotificationsStatusChangeThreadId = null; #[Validate(['nullable', 'string'])] - public ?string $telegramNotificationsBackupSuccessTopicId = null; + public ?string $telegramNotificationsBackupSuccessThreadId = null; #[Validate(['nullable', 'string'])] - public ?string $telegramNotificationsBackupFailureTopicId = null; + public ?string $telegramNotificationsBackupFailureThreadId = null; #[Validate(['nullable', 'string'])] - public ?string $telegramNotificationsScheduledTaskSuccessTopicId = null; + public ?string $telegramNotificationsScheduledTaskSuccessThreadId = null; #[Validate(['nullable', 'string'])] - public ?string $telegramNotificationsScheduledTaskFailureTopicId = null; + public ?string $telegramNotificationsScheduledTaskFailureThreadId = null; #[Validate(['nullable', 'string'])] - public ?string $telegramNotificationsDockerCleanupSuccessTopicId = null; + public ?string $telegramNotificationsDockerCleanupSuccessThreadId = null; #[Validate(['nullable', 'string'])] - public ?string $telegramNotificationsDockerCleanupFailureTopicId = null; + public ?string $telegramNotificationsDockerCleanupFailureThreadId = null; #[Validate(['nullable', 'string'])] - public ?string $telegramNotificationsServerDiskUsageTopicId = null; + public ?string $telegramNotificationsServerDiskUsageThreadId = null; #[Validate(['nullable', 'string'])] - public ?string $telegramNotificationsServerReachableTopicId = null; + public ?string $telegramNotificationsServerReachableThreadId = null; #[Validate(['nullable', 'string'])] - public ?string $telegramNotificationsServerUnreachableTopicId = null; + public ?string $telegramNotificationsServerUnreachableThreadId = null; public function mount() { @@ -127,21 +132,20 @@ public function syncData(bool $toModel = false) $this->settings->server_reachable_telegram_notifications = $this->serverReachableTelegramNotifications; $this->settings->server_unreachable_telegram_notifications = $this->serverUnreachableTelegramNotifications; - $this->settings->telegram_notifications_deployment_success_topic_id = $this->telegramNotificationsDeploymentSuccessTopicId; - $this->settings->telegram_notifications_deployment_failure_topic_id = $this->telegramNotificationsDeploymentFailureTopicId; - $this->settings->telegram_notifications_status_change_topic_id = $this->telegramNotificationsStatusChangeTopicId; - $this->settings->telegram_notifications_backup_success_topic_id = $this->telegramNotificationsBackupSuccessTopicId; - $this->settings->telegram_notifications_backup_failure_topic_id = $this->telegramNotificationsBackupFailureTopicId; - $this->settings->telegram_notifications_scheduled_task_success_topic_id = $this->telegramNotificationsScheduledTaskSuccessTopicId; - $this->settings->telegram_notifications_scheduled_task_failure_topic_id = $this->telegramNotificationsScheduledTaskFailureTopicId; - $this->settings->telegram_notifications_docker_cleanup_success_topic_id = $this->telegramNotificationsDockerCleanupSuccessTopicId; - $this->settings->telegram_notifications_docker_cleanup_failure_topic_id = $this->telegramNotificationsDockerCleanupFailureTopicId; - $this->settings->telegram_notifications_server_disk_usage_topic_id = $this->telegramNotificationsServerDiskUsageTopicId; - $this->settings->telegram_notifications_server_reachable_topic_id = $this->telegramNotificationsServerReachableTopicId; - $this->settings->telegram_notifications_server_unreachable_topic_id = $this->telegramNotificationsServerUnreachableTopicId; + $this->settings->telegram_notifications_deployment_success_thread_id = $this->telegramNotificationsDeploymentSuccessThreadId; + $this->settings->telegram_notifications_deployment_failure_thread_id = $this->telegramNotificationsDeploymentFailureThreadId; + $this->settings->telegram_notifications_status_change_thread_id = $this->telegramNotificationsStatusChangeThreadId; + $this->settings->telegram_notifications_backup_success_thread_id = $this->telegramNotificationsBackupSuccessThreadId; + $this->settings->telegram_notifications_backup_failure_thread_id = $this->telegramNotificationsBackupFailureThreadId; + $this->settings->telegram_notifications_scheduled_task_success_thread_id = $this->telegramNotificationsScheduledTaskSuccessThreadId; + $this->settings->telegram_notifications_scheduled_task_failure_thread_id = $this->telegramNotificationsScheduledTaskFailureThreadId; + $this->settings->telegram_notifications_docker_cleanup_success_thread_id = $this->telegramNotificationsDockerCleanupSuccessThreadId; + $this->settings->telegram_notifications_docker_cleanup_failure_thread_id = $this->telegramNotificationsDockerCleanupFailureThreadId; + $this->settings->telegram_notifications_server_disk_usage_thread_id = $this->telegramNotificationsServerDiskUsageThreadId; + $this->settings->telegram_notifications_server_reachable_thread_id = $this->telegramNotificationsServerReachableThreadId; + $this->settings->telegram_notifications_server_unreachable_thread_id = $this->telegramNotificationsServerUnreachableThreadId; $this->settings->save(); - refreshSession(); } else { $this->telegramEnabled = $this->settings->telegram_enabled; $this->telegramToken = $this->settings->telegram_token; @@ -160,18 +164,18 @@ public function syncData(bool $toModel = false) $this->serverReachableTelegramNotifications = $this->settings->server_reachable_telegram_notifications; $this->serverUnreachableTelegramNotifications = $this->settings->server_unreachable_telegram_notifications; - $this->telegramNotificationsDeploymentSuccessTopicId = $this->settings->telegram_notifications_deployment_success_topic_id; - $this->telegramNotificationsDeploymentFailureTopicId = $this->settings->telegram_notifications_deployment_failure_topic_id; - $this->telegramNotificationsStatusChangeTopicId = $this->settings->telegram_notifications_status_change_topic_id; - $this->telegramNotificationsBackupSuccessTopicId = $this->settings->telegram_notifications_backup_success_topic_id; - $this->telegramNotificationsBackupFailureTopicId = $this->settings->telegram_notifications_backup_failure_topic_id; - $this->telegramNotificationsScheduledTaskSuccessTopicId = $this->settings->telegram_notifications_scheduled_task_success_topic_id; - $this->telegramNotificationsScheduledTaskFailureTopicId = $this->settings->telegram_notifications_scheduled_task_failure_topic_id; - $this->telegramNotificationsDockerCleanupSuccessTopicId = $this->settings->telegram_notifications_docker_cleanup_success_topic_id; - $this->telegramNotificationsDockerCleanupFailureTopicId = $this->settings->telegram_notifications_docker_cleanup_failure_topic_id; - $this->telegramNotificationsServerDiskUsageTopicId = $this->settings->telegram_notifications_server_disk_usage_topic_id; - $this->telegramNotificationsServerReachableTopicId = $this->settings->telegram_notifications_server_reachable_topic_id; - $this->telegramNotificationsServerUnreachableTopicId = $this->settings->telegram_notifications_server_unreachable_topic_id; + $this->telegramNotificationsDeploymentSuccessThreadId = $this->settings->telegram_notifications_deployment_success_thread_id; + $this->telegramNotificationsDeploymentFailureThreadId = $this->settings->telegram_notifications_deployment_failure_thread_id; + $this->telegramNotificationsStatusChangeThreadId = $this->settings->telegram_notifications_status_change_thread_id; + $this->telegramNotificationsBackupSuccessThreadId = $this->settings->telegram_notifications_backup_success_thread_id; + $this->telegramNotificationsBackupFailureThreadId = $this->settings->telegram_notifications_backup_failure_thread_id; + $this->telegramNotificationsScheduledTaskSuccessThreadId = $this->settings->telegram_notifications_scheduled_task_success_thread_id; + $this->telegramNotificationsScheduledTaskFailureThreadId = $this->settings->telegram_notifications_scheduled_task_failure_thread_id; + $this->telegramNotificationsDockerCleanupSuccessThreadId = $this->settings->telegram_notifications_docker_cleanup_success_thread_id; + $this->telegramNotificationsDockerCleanupFailureThreadId = $this->settings->telegram_notifications_docker_cleanup_failure_thread_id; + $this->telegramNotificationsServerDiskUsageThreadId = $this->settings->telegram_notifications_server_disk_usage_thread_id; + $this->telegramNotificationsServerReachableThreadId = $this->settings->telegram_notifications_server_reachable_thread_id; + $this->telegramNotificationsServerUnreachableThreadId = $this->settings->telegram_notifications_server_unreachable_thread_id; } } @@ -181,6 +185,8 @@ public function instantSave() $this->syncData(true); } catch (\Throwable $e) { return handleError($e, $this); + } finally { + $this->dispatch('refresh'); } } @@ -210,6 +216,8 @@ public function instantSaveTelegramEnabled() $this->telegramEnabled = false; return handleError($e, $this); + } finally { + $this->dispatch('refresh'); } } diff --git a/app/Models/PushoverNotificationSettings.php b/app/Models/PushoverNotificationSettings.php new file mode 100644 index 000000000..e3191dcc3 --- /dev/null +++ b/app/Models/PushoverNotificationSettings.php @@ -0,0 +1,61 @@ + 'boolean', + 'pushover_user_key' => 'encrypted', + 'pushover_api_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..e55cb0d19 100644 --- a/app/Models/Team.php +++ b/app/Models/Team.php @@ -4,6 +4,7 @@ use App\Notifications\Channels\SendsDiscord; use App\Notifications\Channels\SendsEmail; +use App\Notifications\Channels\SendsPushover; use App\Notifications\Channels\SendsSlack; use App\Traits\HasNotificationSettings; use Illuminate\Database\Eloquent\Casts\Attribute; @@ -32,7 +33,7 @@ ] )] -class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack +class Team extends Model implements SendsDiscord, SendsEmail, SendsPushover, SendsSlack { use HasNotificationSettings, Notifiable; @@ -49,6 +50,7 @@ protected static function booted() $team->discordNotificationSettings()->create(); $team->slackNotificationSettings()->create(); $team->telegramNotificationSettings()->create(); + $team->pushoverNotificationSettings()->create(); }); static::saving(function ($team) { @@ -155,6 +157,14 @@ public function routeNotificationForSlack() return data_get($this, 'slack_webhook_url', null); } + public function routeNotificationForPushover() + { + return [ + 'user' => data_get($this, 'pushover_user_key', null), + 'token' => data_get($this, 'pushover_api_token', null), + ]; + } + public function getRecipients($notification) { $recipients = data_get($notification, 'emails', null); @@ -174,7 +184,8 @@ public function isAnyNotificationEnabled() 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 @@ public function slackNotificationSettings() { return $this->hasOne(SlackNotificationSettings::class); } + + public function pushoverNotificationSettings() + { + return $this->hasOne(PushoverNotificationSettings::class); + } } diff --git a/app/Models/TelegramNotificationSettings.php b/app/Models/TelegramNotificationSettings.php index 2edca14ff..78bd841bd 100644 --- a/app/Models/TelegramNotificationSettings.php +++ b/app/Models/TelegramNotificationSettings.php @@ -30,17 +30,17 @@ class TelegramNotificationSettings extends Model 'server_reachable_telegram_notifications', 'server_unreachable_telegram_notifications', - 'telegram_notifications_deployment_success_topic_id', - 'telegram_notifications_deployment_failure_topic_id', - 'telegram_notifications_status_change_topic_id', - 'telegram_notifications_backup_success_topic_id', - 'telegram_notifications_backup_failure_topic_id', - 'telegram_notifications_scheduled_task_success_topic_id', - 'telegram_notifications_scheduled_task_failure_topic_id', - 'telegram_notifications_docker_cleanup_topic_id', - 'telegram_notifications_server_disk_usage_topic_id', - 'telegram_notifications_server_reachable_topic_id', - 'telegram_notifications_server_unreachable_topic_id', + 'telegram_notifications_deployment_success_thread_id', + 'telegram_notifications_deployment_failure_thread_id', + 'telegram_notifications_status_change_thread_id', + 'telegram_notifications_backup_success_thread_id', + 'telegram_notifications_backup_failure_thread_id', + 'telegram_notifications_scheduled_task_success_thread_id', + 'telegram_notifications_scheduled_task_failure_thread_id', + 'telegram_notifications_docker_cleanup_thread_id', + 'telegram_notifications_server_disk_usage_thread_id', + 'telegram_notifications_server_reachable_thread_id', + 'telegram_notifications_server_unreachable_thread_id', ]; protected $casts = [ @@ -60,17 +60,17 @@ class TelegramNotificationSettings extends Model 'server_reachable_telegram_notifications' => 'boolean', 'server_unreachable_telegram_notifications' => 'boolean', - 'telegram_notifications_deployment_success_topic_id' => 'encrypted', - 'telegram_notifications_deployment_failure_topic_id' => 'encrypted', - 'telegram_notifications_status_change_topic_id' => 'encrypted', - 'telegram_notifications_backup_success_topic_id' => 'encrypted', - 'telegram_notifications_backup_failure_topic_id' => 'encrypted', - 'telegram_notifications_scheduled_task_success_topic_id' => 'encrypted', - 'telegram_notifications_scheduled_task_failure_topic_id' => 'encrypted', - 'telegram_notifications_docker_cleanup_topic_id' => 'encrypted', - 'telegram_notifications_server_disk_usage_topic_id' => 'encrypted', - 'telegram_notifications_server_reachable_topic_id' => 'encrypted', - 'telegram_notifications_server_unreachable_topic_id' => 'encrypted', + 'telegram_notifications_deployment_success_thread_id' => 'encrypted', + 'telegram_notifications_deployment_failure_thread_id' => 'encrypted', + 'telegram_notifications_status_change_thread_id' => 'encrypted', + 'telegram_notifications_backup_success_thread_id' => 'encrypted', + 'telegram_notifications_backup_failure_thread_id' => 'encrypted', + 'telegram_notifications_scheduled_task_success_thread_id' => 'encrypted', + 'telegram_notifications_scheduled_task_failure_thread_id' => 'encrypted', + 'telegram_notifications_docker_cleanup_thread_id' => 'encrypted', + 'telegram_notifications_server_disk_usage_thread_id' => 'encrypted', + 'telegram_notifications_server_reachable_thread_id' => 'encrypted', + 'telegram_notifications_server_unreachable_thread_id' => 'encrypted', ]; public function team() 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\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 @@ public function toTelegram(): array ]; } + 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\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 @@ public function toTelegram(): array ]; } + 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 @@ 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 @@ public function toTelegram(): array ]; } + 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..3d3728d01 --- /dev/null +++ b/app/Notifications/Channels/PushoverChannel.php @@ -0,0 +1,21 @@ +toPushover(); + $pushoverSettings = $notifiable->pushoverNotificationSettings; + + if (! $pushoverSettings || ! $pushoverSettings->isEnabled() || ! $pushoverSettings->pushover_user_key || ! $pushoverSettings->pushover_api_token) { + return; + } + + SendMessageToPushoverJob::dispatch($message, $pushoverSettings->pushover_api_token, $pushoverSettings->pushover_user_key); + } +} 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 @@ +telegram_token; $chatId = $settings->telegram_chat_id; - $topicId = match (get_class($notification)) { - \App\Notifications\Test::class => $settings->telegram_notifications_test_topic_id, + $threadId = match (get_class($notification)) { + \App\Notifications\Application\DeploymentSuccess::class => $settings->telegram_notifications_deployment_success_thread_id, + \App\Notifications\Application\DeploymentFailed::class => $settings->telegram_notifications_deployment_failure_thread_id, \App\Notifications\Application\StatusChanged::class, \App\Notifications\Container\ContainerRestarted::class, - \App\Notifications\Container\ContainerStopped::class => $settings->telegram_notifications_status_change_topic_id, - \App\Notifications\Application\DeploymentSuccess::class => $settings->telegram_notifications_deployment_success_topic_id, - \App\Notifications\Application\DeploymentFailed::class => $settings->telegram_notifications_deployment_failure_topic_id, - \App\Notifications\Database\BackupSuccess::class => $settings->telegram_notifications_backup_success_topic_id, - \App\Notifications\Database\BackupFailed::class => $settings->telegram_notifications_backup_failure_topic_id, - \App\Notifications\ScheduledTask\TaskFailed::class => $settings->telegram_notifications_scheduled_task_failure_topic_id, - \App\Notifications\Server\Unreachable::class => $settings->telegram_notifications_server_unreachable_topic_id, - \App\Notifications\Server\Reachable::class => $settings->telegram_notifications_server_reachable_topic_id, + \App\Notifications\Container\ContainerStopped::class => $settings->telegram_notifications_status_change_thread_id, + + \App\Notifications\Database\BackupSuccess::class => $settings->telegram_notifications_backup_success_thread_id, + \App\Notifications\Database\BackupFailed::class => $settings->telegram_notifications_backup_failure_thread_id, + + \App\Notifications\ScheduledTask\TaskSuccess::class => $settings->telegram_notifications_scheduled_task_success_thread_id, + \App\Notifications\ScheduledTask\TaskFailed::class => $settings->telegram_notifications_scheduled_task_failure_thread_id, + + \App\Notifications\Server\DockerCleanupSuccess::class => $settings->telegram_notifications_docker_cleanup_success_thread_id, + \App\Notifications\Server\DockerCleanupFailed::class => $settings->telegram_notifications_docker_cleanup_failure_thread_id, + \App\Notifications\Server\HighDiskUsage::class => $settings->telegram_notifications_server_disk_usage_thread_id, + \App\Notifications\Server\Unreachable::class => $settings->telegram_notifications_server_unreachable_thread_id, + \App\Notifications\Server\Reachable::class => $settings->telegram_notifications_server_reachable_thread_id, + default => null, }; @@ -35,6 +42,6 @@ public function send($notifiable, $notification): void return; } - SendMessageToTelegramJob::dispatch($message, $buttons, $telegramToken, $chatId, $topicId); + SendMessageToTelegramJob::dispatch($message, $buttons, $telegramToken, $chatId, $threadId); } } diff --git a/app/Notifications/Container/ContainerRestarted.php b/app/Notifications/Container/ContainerRestarted.php index c25072ecf..68fc6b019 100644 --- a/app/Notifications/Container/ContainerRestarted.php +++ b/app/Notifications/Container/ContainerRestarted.php @@ -5,6 +5,7 @@ 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,24 @@ public function toTelegram(): array return $payload; } + public function toPushover(): PushoverMessage + { + $buttons = []; + if ($this->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 @@ 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 @@ public function toTelegram(): array 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 @@ 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 @@ public function toTelegram(): array ]; } + 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 @@ 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 @@ public function toTelegram(): array ]; } + + 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 @@ public function toTelegram(): array ]; } + 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 @@ 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 @@ public function toTelegram(): array ]; } + 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 @@ 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 @@ public function toTelegram(): array ]; } + 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 @@ 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 @@ public function toTelegram(): array ]; } + 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 @@ 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 @@ public function toTelegram(): array ]; } + 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 @@ 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 @@ public function toTelegram(): array ]; } + 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 @@ 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 @@ public function toTelegram(): array ]; } + 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 @@ 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 @@ public function toTelegram(): array ]; } + 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 @@ 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 @@ public function toDiscord(): DiscordMessage ); } + 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 @@ 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 @@ public function toTelegram(): ?array ]; } + 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\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 @@ public function via(object $notifiable): array 'discord' => [DiscordChannel::class], 'telegram' => [TelegramChannel::class], 'slack' => [SlackChannel::class], + 'pushover' => [PushoverChannel::class], default => [], }; } else { @@ -85,6 +88,20 @@ public function toTelegram(): array ]; } + 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\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 @@ public function getNotificationSettings(string $channel): ?Model 'discord' => $this->discordNotificationSettings, 'telegram' => $this->telegramNotificationSettings, 'slack' => $this->slackNotificationSettings, + 'pushover' => $this->pushoverNotificationSettings, default => null, }; } @@ -73,6 +75,7 @@ public function getEnabledChannels(string $event): array 'discord' => DiscordChannel::class, 'telegram' => TelegramChannel::class, 'slack' => SlackChannel::class, + 'pushover' => PushoverChannel::class, ]; if ($event === 'general') { diff --git a/config/constants.php b/config/constants.php index 3054df1f0..a02d6616a 100644 --- a/config/constants.php +++ b/config/constants.php @@ -37,6 +37,10 @@ 'is_migration_enabled' => env('MIGRATION_ENABLED', true), ], + 'seeder' => [ + 'is_seeder_enabled' => env('SEEDER_ENABLED', true), + ], + 'horizon' => [ 'is_horizon_enabled' => env('HORIZON_ENABLED', true), 'is_scheduler_enabled' => env('SCHEDULER_ENABLED', true), diff --git a/database/migrations/2024_10_30_074601_rename_token_permissions.php b/database/migrations/2024_10_30_074601_rename_token_permissions.php index 2ca98d090..69d774d7f 100644 --- a/database/migrations/2024_10_30_074601_rename_token_permissions.php +++ b/database/migrations/2024_10_30_074601_rename_token_permissions.php @@ -40,7 +40,7 @@ public function down(): void $tokens = PersonalAccessToken::all(); foreach ($tokens as $token) { $abilities = collect(); - if (in_array('write', $token->abilities)) { + if (in_array('root', $token->abilities)) { $abilities->push('*'); } else { if (in_array('read', $token->abilities)) { diff --git a/database/migrations/2024_12_05_212440_create_telegram_notification_settings_table.php b/database/migrations/2024_12_05_212440_create_telegram_notification_settings_table.php index 190d148fc..db4d998e3 100644 --- a/database/migrations/2024_12_05_212440_create_telegram_notification_settings_table.php +++ b/database/migrations/2024_12_05_212440_create_telegram_notification_settings_table.php @@ -32,18 +32,18 @@ public function up(): void $table->boolean('server_reachable_telegram_notifications')->default(false); $table->boolean('server_unreachable_telegram_notifications')->default(true); - $table->text('telegram_notifications_deployment_success_topic_id')->nullable(); - $table->text('telegram_notifications_deployment_failure_topic_id')->nullable(); - $table->text('telegram_notifications_status_change_topic_id')->nullable(); - $table->text('telegram_notifications_backup_success_topic_id')->nullable(); - $table->text('telegram_notifications_backup_failure_topic_id')->nullable(); - $table->text('telegram_notifications_scheduled_task_success_topic_id')->nullable(); - $table->text('telegram_notifications_scheduled_task_failure_topic_id')->nullable(); - $table->text('telegram_notifications_docker_cleanup_success_topic_id')->nullable(); - $table->text('telegram_notifications_docker_cleanup_failure_topic_id')->nullable(); - $table->text('telegram_notifications_server_disk_usage_topic_id')->nullable(); - $table->text('telegram_notifications_server_reachable_topic_id')->nullable(); - $table->text('telegram_notifications_server_unreachable_topic_id')->nullable(); + $table->text('telegram_notifications_deployment_success_thread_id')->nullable(); + $table->text('telegram_notifications_deployment_failure_thread_id')->nullable(); + $table->text('telegram_notifications_status_change_thread_id')->nullable(); + $table->text('telegram_notifications_backup_success_thread_id')->nullable(); + $table->text('telegram_notifications_backup_failure_thread_id')->nullable(); + $table->text('telegram_notifications_scheduled_task_success_thread_id')->nullable(); + $table->text('telegram_notifications_scheduled_task_failure_thread_id')->nullable(); + $table->text('telegram_notifications_docker_cleanup_success_thread_id')->nullable(); + $table->text('telegram_notifications_docker_cleanup_failure_thread_id')->nullable(); + $table->text('telegram_notifications_server_disk_usage_thread_id')->nullable(); + $table->text('telegram_notifications_server_reachable_thread_id')->nullable(); + $table->text('telegram_notifications_server_unreachable_thread_id')->nullable(); $table->unique(['team_id']); }); diff --git a/database/migrations/2024_12_05_212705_migrate_telegram_notification_settings_from_teams_table.php b/database/migrations/2024_12_05_212705_migrate_telegram_notification_settings_from_teams_table.php index 0c10646b9..df120e273 100644 --- a/database/migrations/2024_12_05_212705_migrate_telegram_notification_settings_from_teams_table.php +++ b/database/migrations/2024_12_05_212705_migrate_telegram_notification_settings_from_teams_table.php @@ -4,6 +4,7 @@ use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Schema; return new class extends Migration @@ -30,17 +31,17 @@ public function up(): void 'status_change_telegram_notifications' => $team->telegram_notifications_status_changes ?? false, 'server_disk_usage_telegram_notifications' => $team->telegram_notifications_server_disk_usage ?? true, - 'telegram_notifications_deployment_success_topic_id' => $team->telegram_notifications_deployments_message_thread_id ? Crypt::encryptString($team->telegram_notifications_deployments_message_thread_id) : null, - 'telegram_notifications_deployment_failure_topic_id' => $team->telegram_notifications_deployments_message_thread_id ? Crypt::encryptString($team->telegram_notifications_deployments_message_thread_id) : null, - 'telegram_notifications_backup_success_topic_id' => $team->telegram_notifications_database_backups_message_thread_id ? Crypt::encryptString($team->telegram_notifications_database_backups_message_thread_id) : null, - 'telegram_notifications_backup_failure_topic_id' => $team->telegram_notifications_database_backups_message_thread_id ? Crypt::encryptString($team->telegram_notifications_database_backups_message_thread_id) : null, - 'telegram_notifications_scheduled_task_success_topic_id' => $team->telegram_notifications_scheduled_tasks_thread_id ? Crypt::encryptString($team->telegram_notifications_scheduled_tasks_thread_id) : null, - 'telegram_notifications_scheduled_task_failure_topic_id' => $team->telegram_notifications_scheduled_tasks_thread_id ? Crypt::encryptString($team->telegram_notifications_scheduled_tasks_thread_id) : null, - 'telegram_notifications_status_change_topic_id' => $team->telegram_notifications_status_changes_message_thread_id ? Crypt::encryptString($team->telegram_notifications_status_changes_message_thread_id) : null, + 'telegram_notifications_deployment_success_thread_id' => $team->telegram_notifications_deployments_message_thread_id ? Crypt::encryptString($team->telegram_notifications_deployments_message_thread_id) : null, + 'telegram_notifications_deployment_failure_thread_id' => $team->telegram_notifications_deployments_message_thread_id ? Crypt::encryptString($team->telegram_notifications_deployments_message_thread_id) : null, + 'telegram_notifications_backup_success_thread_id' => $team->telegram_notifications_database_backups_message_thread_id ? Crypt::encryptString($team->telegram_notifications_database_backups_message_thread_id) : null, + 'telegram_notifications_backup_failure_thread_id' => $team->telegram_notifications_database_backups_message_thread_id ? Crypt::encryptString($team->telegram_notifications_database_backups_message_thread_id) : null, + 'telegram_notifications_scheduled_task_success_thread_id' => $team->telegram_notifications_scheduled_tasks_thread_id ? Crypt::encryptString($team->telegram_notifications_scheduled_tasks_thread_id) : null, + 'telegram_notifications_scheduled_task_failure_thread_id' => $team->telegram_notifications_scheduled_tasks_thread_id ? Crypt::encryptString($team->telegram_notifications_scheduled_tasks_thread_id) : null, + 'telegram_notifications_status_change_thread_id' => $team->telegram_notifications_status_changes_message_thread_id ? Crypt::encryptString($team->telegram_notifications_status_changes_message_thread_id) : null, ] ); } catch (Exception $e) { - \Log::error('Error migrating telegram notification settings from teams table: '.$e->getMessage()); + Log::error('Error migrating telegram notification settings from teams table: '.$e->getMessage()); } } @@ -101,13 +102,13 @@ public function down(): void 'telegram_notifications_scheduled_tasks' => $setting->scheduled_task_success_telegram_notifications || $setting->scheduled_task_failure_telegram_notifications, 'telegram_notifications_server_disk_usage' => $setting->server_disk_usage_telegram_notifications, - 'telegram_notifications_deployments_message_thread_id' => $setting->telegram_notifications_deployment_success_topic_id ? Crypt::decryptString($setting->telegram_notifications_deployment_success_topic_id) : null, - 'telegram_notifications_status_changes_message_thread_id' => $setting->telegram_notifications_status_change_topic_id ? Crypt::decryptString($setting->telegram_notifications_status_change_topic_id) : null, - 'telegram_notifications_database_backups_message_thread_id' => $setting->telegram_notifications_backup_success_topic_id ? Crypt::decryptString($setting->telegram_notifications_backup_success_topic_id) : null, - 'telegram_notifications_scheduled_tasks_thread_id' => $setting->telegram_notifications_scheduled_task_success_topic_id ? Crypt::decryptString($setting->telegram_notifications_scheduled_task_success_topic_id) : null, + 'telegram_notifications_deployments_message_thread_id' => $setting->telegram_notifications_deployment_success_thread_id ? Crypt::decryptString($setting->telegram_notifications_deployment_success_thread_id) : null, + 'telegram_notifications_status_changes_message_thread_id' => $setting->telegram_notifications_status_change_thread_id ? Crypt::decryptString($setting->telegram_notifications_status_change_thread_id) : null, + 'telegram_notifications_database_backups_message_thread_id' => $setting->telegram_notifications_backup_success_thread_id ? Crypt::decryptString($setting->telegram_notifications_backup_success_thread_id) : null, + 'telegram_notifications_scheduled_tasks_thread_id' => $setting->telegram_notifications_scheduled_task_success_thread_id ? Crypt::decryptString($setting->telegram_notifications_scheduled_task_success_thread_id) : null, ]); } catch (Exception $e) { - \Log::error('Error migrating telegram notification settings from teams table: '.$e->getMessage()); + Log::error('Error migrating telegram notification settings from teams table: '.$e->getMessage()); } } } diff --git a/database/migrations/2024_12_06_142014_create_slack_notification_settings_table.php b/database/migrations/2024_12_06_142014_create_slack_notification_settings_table.php index 790e0f667..8aee40d87 100644 --- a/database/migrations/2024_12_06_142014_create_slack_notification_settings_table.php +++ b/database/migrations/2024_12_06_142014_create_slack_notification_settings_table.php @@ -3,6 +3,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Schema; return new class extends Migration @@ -42,7 +43,7 @@ public function up(): void 'team_id' => $team->id, ]); } catch (\Throwable $e) { - \Log::error('Error migrating slack notification settings from teams table: '.$e->getMessage()); + Log::error('Error creating slack notification settings for existing teams: '.$e->getMessage()); } } } 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..ad4215a07 --- /dev/null +++ b/database/migrations/2024_12_11_135026_create_pushover_notification_settings_table.php @@ -0,0 +1,59 @@ +id(); + $table->foreignId('team_id')->constrained()->cascadeOnDelete(); + + $table->boolean('pushover_enabled')->default(false); + $table->text('pushover_user_key')->nullable(); + $table->text('pushover_api_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']); + }); + $teams = DB::table('teams')->get(); + + foreach ($teams as $team) { + try { + DB::table('pushover_notification_settings')->insert([ + 'team_id' => $team->id, + ]); + } catch (\Throwable $e) { + Log::error('Error creating pushover notification settings for existing teams: '.$e->getMessage()); + } + } + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('pushover_notification_settings'); + } +}; diff --git a/docker/production/etc/s6-overlay/s6-rc.d/init-seeder/up b/docker/production/etc/s6-overlay/s6-rc.d/init-seeder/up index 20d17c58b..007f4233e 100644 --- a/docker/production/etc/s6-overlay/s6-rc.d/init-seeder/up +++ b/docker/production/etc/s6-overlay/s6-rc.d/init-seeder/up @@ -6,10 +6,7 @@ cd /var/www/html foreground { php artisan - db:seed - --class - ProductionSeeder - --force + start:seeder } diff --git a/other/logos/internetgarden.ico b/other/logos/internetgarden.ico new file mode 100644 index 000000000..e555984be Binary files /dev/null and b/other/logos/internetgarden.ico differ diff --git a/resources/views/components/notification/navbar.blade.php b/resources/views/components/notification/navbar.blade.php index c4dbd25af..447a385c5 100644 --- a/resources/views/components/notification/navbar.blade.php +++ b/resources/views/components/notification/navbar.blade.php @@ -7,18 +7,22 @@ href="{{ route('notifications.email') }}"> - - - + + + + + + - \ No newline at end of file + diff --git a/resources/views/livewire/notifications/discord.blade.php b/resources/views/livewire/notifications/discord.blade.php index 1f83ed061..6caf31176 100644 --- a/resources/views/livewire/notifications/discord.blade.php +++ b/resources/views/livewire/notifications/discord.blade.php @@ -24,7 +24,7 @@

Notification Settings

diff --git a/resources/views/livewire/notifications/pushover.blade.php b/resources/views/livewire/notifications/pushover.blade.php new file mode 100644 index 000000000..c8dd777d8 --- /dev/null +++ b/resources/views/livewire/notifications/pushover.blade.php @@ -0,0 +1,86 @@ +
+ + Notifications | Coolify + + +
+
+

Pushover

+ + Save + + @if ($pushoverEnabled) + + Send Test Notification + + @else + + Send Test Notification + + @endif +
+
+ +
+
+ + +
+
+

Notification Settings

+

+ Select events for which you would like to receive Pushover notifications. +

+
+
+

Deployments

+
+ + + +
+
+
+

Backups

+
+ + +
+
+
+

Scheduled Tasks

+
+ + +
+
+
+

Server

+
+ + + + + +
+
+
+
diff --git a/resources/views/livewire/notifications/slack.blade.php b/resources/views/livewire/notifications/slack.blade.php index 774f8e7e5..28bfe509f 100644 --- a/resources/views/livewire/notifications/slack.blade.php +++ b/resources/views/livewire/notifications/slack.blade.php @@ -24,7 +24,7 @@

Notification Settings

diff --git a/resources/views/livewire/notifications/telegram.blade.php b/resources/views/livewire/notifications/telegram.blade.php index 5dd859e9e..ecc9360fa 100644 --- a/resources/views/livewire/notifications/telegram.blade.php +++ b/resources/views/livewire/notifications/telegram.blade.php @@ -26,8 +26,8 @@
- +
@@ -44,16 +44,16 @@ - +
- +
@@ -61,8 +61,8 @@ label="Container Status Changes" helper="Send a notification when a container status changes. It will send a notification for Stopped and Restarted events of a container." />
- +
@@ -74,8 +74,8 @@ - +
@@ -83,8 +83,8 @@
- + @@ -97,8 +97,8 @@ - +
@@ -106,8 +106,8 @@
- + @@ -120,8 +120,8 @@ - +
@@ -129,8 +129,8 @@
- +
@@ -138,8 +138,8 @@
- +
@@ -147,8 +147,8 @@
- + @@ -157,8 +157,8 @@ - + diff --git a/routes/web.php b/routes/web.php index 3570fe0ed..3d12b9b46 100644 --- a/routes/web.php +++ b/routes/web.php @@ -12,6 +12,7 @@ use App\Livewire\ForcePasswordReset; use App\Livewire\Notifications\Discord as NotificationDiscord; use App\Livewire\Notifications\Email as NotificationEmail; +use App\Livewire\Notifications\Pushover as NotificationPushover; use App\Livewire\Notifications\Slack as NotificationSlack; use App\Livewire\Notifications\Telegram as NotificationTelegram; use App\Livewire\Profile\Index as ProfileIndex; @@ -133,6 +134,7 @@ 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 () {