mirror of
https://github.com/coollabsio/coolify.git
synced 2026-03-11 08:55:47 +00:00
add support for ntfy.sh
This commit is contained in:
parent
e9158b7305
commit
b200175df1
27 changed files with 549 additions and 17 deletions
84
app/Jobs/SendMessageToNtfyJob.php
Normal file
84
app/Jobs/SendMessageToNtfyJob.php
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class SendMessageToNtfyJob implements ShouldBeEncrypted, ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* The number of times the job may be attempted.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public $tries = 5;
|
||||
|
||||
public $backoff = 10;
|
||||
|
||||
/**
|
||||
* The maximum number of unhandled exceptions to allow before failing.
|
||||
*/
|
||||
public int $maxExceptions = 5;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param string $text The message to send
|
||||
* @param string|null $buttons The buttons to send. These buttons follow the format as described in the Ntfy documentation: https://docs.ntfy.sh/publish/?h=user#defining-actions
|
||||
* @param string|null $emoji The emoji to use for the message. We use the shortcodes for emojis. A list of them can be found here: https://docs.ntfy.sh/emojis/
|
||||
* @param string|null $title The title of the message
|
||||
* @param string $url The URL of the Ntfy instance
|
||||
* @param string|null $username The username to use for basic authentication
|
||||
* @param string|null $password The password to use for basic authentication
|
||||
* @param string $topic The topic to send the message to
|
||||
*/
|
||||
public function __construct(
|
||||
public string $text,
|
||||
public ?string $buttons,
|
||||
public ?string $emoji,
|
||||
public ?string $title,
|
||||
public string $url,
|
||||
public ?string $username,
|
||||
public ?string $password,
|
||||
public string $topic
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
|
||||
$headers = [];
|
||||
if ($this->username && $this->password) {
|
||||
$headers['Authorization'] = 'Basic '.base64_encode($this->username.':'.$this->password);
|
||||
}
|
||||
|
||||
if ($this->buttons) {
|
||||
$headers['Actions'] = $this->buttons;
|
||||
}
|
||||
|
||||
$headers['Content-Type'] = 'text/markdown';
|
||||
$headers['Tags'] = 'coolify';
|
||||
|
||||
if ($this->emoji) {
|
||||
$headers['Tags'] .= ','.$this->emoji;
|
||||
}
|
||||
|
||||
if ($this->title) {
|
||||
$headers['Title'] = $this->title;
|
||||
}
|
||||
|
||||
$payload = $this->text;
|
||||
Http::withHeaders($headers)->post($this->url.'/'.$this->topic, $payload);
|
||||
}
|
||||
}
|
||||
72
app/Livewire/Notifications/Ntfy.php
Normal file
72
app/Livewire/Notifications/Ntfy.php
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
namespace App\Livewire\Notifications;
|
||||
|
||||
use App\Models\Team;
|
||||
use App\Notifications\Test;
|
||||
use Livewire\Component;
|
||||
|
||||
class Ntfy extends Component
|
||||
{
|
||||
public Team $team;
|
||||
|
||||
protected $rules = [
|
||||
'team.ntfy_enabled' => 'nullable|boolean',
|
||||
'team.ntfy_url' => 'required|url',
|
||||
'team.ntfy_topic' => 'required|string',
|
||||
'team.ntfy_username' => 'nullable|string',
|
||||
'team.ntfy_password' => 'nullable|string',
|
||||
'team.ntfy_notifications_test' => 'nullable|boolean',
|
||||
'team.ntfy_notifications_deployments' => 'nullable|boolean',
|
||||
'team.ntfy_notifications_status_changes' => 'nullable|boolean',
|
||||
'team.ntfy_notifications_database_backups' => 'nullable|boolean',
|
||||
'team.ntfy_notifications_scheduled_tasks' => 'nullable|boolean',
|
||||
];
|
||||
|
||||
/* shelll */
|
||||
protected $validationAttributes = [
|
||||
'team.ntfy_url' => 'Host',
|
||||
'team.ntfy_topic' => 'Topic',
|
||||
];
|
||||
|
||||
public function mount()
|
||||
{
|
||||
$this->team = auth()->user()->currentTeam();
|
||||
}
|
||||
|
||||
public function instantSave()
|
||||
{
|
||||
try {
|
||||
$this->submit();
|
||||
} catch (\Throwable $e) {
|
||||
ray($e->getMessage());
|
||||
$this->team->ntfy_enabled = false;
|
||||
$this->validate();
|
||||
}
|
||||
}
|
||||
|
||||
public function submit()
|
||||
{
|
||||
$this->resetErrorBag();
|
||||
$this->validate();
|
||||
$this->saveModel();
|
||||
}
|
||||
|
||||
public function saveModel()
|
||||
{
|
||||
$this->team->save();
|
||||
refreshSession();
|
||||
$this->dispatch('success', 'Settings saved.');
|
||||
}
|
||||
|
||||
public function sendTestNotification()
|
||||
{
|
||||
$this->team?->notify(new Test());
|
||||
$this->dispatch('success', 'Test notification sent.');
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
return view('livewire.notifications.ntfy');
|
||||
}
|
||||
}
|
||||
|
|
@ -3,12 +3,13 @@
|
|||
namespace App\Models;
|
||||
|
||||
use App\Notifications\Channels\SendsDiscord;
|
||||
use App\Notifications\Channels\SendsNtfy;
|
||||
use App\Notifications\Channels\SendsEmail;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
class Team extends Model implements SendsDiscord, SendsEmail
|
||||
class Team extends Model implements SendsDiscord, SendsEmail, SendsNtfy
|
||||
{
|
||||
use Notifiable;
|
||||
|
||||
|
|
@ -31,27 +32,27 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
|||
static::deleting(function ($team) {
|
||||
$keys = $team->privateKeys;
|
||||
foreach ($keys as $key) {
|
||||
ray('Deleting key: '.$key->name);
|
||||
ray('Deleting key: ' . $key->name);
|
||||
$key->delete();
|
||||
}
|
||||
$sources = $team->sources();
|
||||
foreach ($sources as $source) {
|
||||
ray('Deleting source: '.$source->name);
|
||||
ray('Deleting source: ' . $source->name);
|
||||
$source->delete();
|
||||
}
|
||||
$tags = Tag::whereTeamId($team->id)->get();
|
||||
foreach ($tags as $tag) {
|
||||
ray('Deleting tag: '.$tag->name);
|
||||
ray('Deleting tag: ' . $tag->name);
|
||||
$tag->delete();
|
||||
}
|
||||
$shared_variables = $team->environment_variables();
|
||||
foreach ($shared_variables as $shared_variable) {
|
||||
ray('Deleting team shared variable: '.$shared_variable->name);
|
||||
ray('Deleting team shared variable: ' . $shared_variable->name);
|
||||
$shared_variable->delete();
|
||||
}
|
||||
$s3s = $team->s3s;
|
||||
foreach ($s3s as $s3) {
|
||||
ray('Deleting s3: '.$s3->name);
|
||||
ray('Deleting s3: ' . $s3->name);
|
||||
$s3->delete();
|
||||
}
|
||||
});
|
||||
|
|
@ -70,6 +71,16 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
|||
];
|
||||
}
|
||||
|
||||
public function routeNotificationForNtfy()
|
||||
{
|
||||
return [
|
||||
'url' => data_get($this, 'ntfy_url', null),
|
||||
'username' => data_get($this, 'ntfy_username', null),
|
||||
'password' => data_get($this, 'ntfy_password', null),
|
||||
'topic' => data_get($this, 'ntfy_topic', null),
|
||||
];
|
||||
}
|
||||
|
||||
public function getRecepients($notification)
|
||||
{
|
||||
$recipients = data_get($notification, 'emails', null);
|
||||
|
|
@ -225,7 +236,7 @@ class Team extends Model implements SendsDiscord, SendsEmail
|
|||
if (isCloud()) {
|
||||
return true;
|
||||
}
|
||||
if ($this->smtp_enabled || $this->resend_enabled || $this->discord_enabled || $this->telegram_enabled || $this->use_instance_email_settings) {
|
||||
if ($this->smtp_enabled || $this->resend_enabled || $this->discord_enabled || $this->telegram_enabled || $this->ntfy_enabled || $this->use_instance_email_settings) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,6 +85,16 @@ class DeploymentFailed extends Notification implements ShouldQueue
|
|||
return $message;
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'Coolify: Deployment Failed',
|
||||
'message' => 'Deployment failed of '.$this->application_name.' ('.$this->fqdn.'): ',
|
||||
'buttons' => 'view, View Deployment Logs, '.$this->deployment_url.';',
|
||||
'emoji' => 'warning',
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
if ($this->preview) {
|
||||
|
|
|
|||
|
|
@ -101,6 +101,23 @@ class DeploymentSuccess extends Notification implements ShouldQueue
|
|||
return $message;
|
||||
}
|
||||
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
if ($this->preview) {
|
||||
$message = 'Coolify: New PR'.$this->preview->pull_request_id.' version successfully deployed of '.$this->application_name.'';
|
||||
} else {
|
||||
$message = 'Coolify: New version successfully deployed of '.$this->application_name.'';
|
||||
}
|
||||
|
||||
return [
|
||||
'title' => 'Coolify: New version deployed',
|
||||
'message' => $message,
|
||||
'buttons' => 'view, Open Application, '.$this->fqdn.';view, Deployment logs, '.$this->deployment_url.';',
|
||||
'emoji' => 'checkmark',
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
if ($this->preview) {
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class StatusChanged extends Notification implements ShouldQueue
|
|||
if (str($this->fqdn)->explode(',')->count() > 1) {
|
||||
$this->fqdn = str($this->fqdn)->explode(',')->first();
|
||||
}
|
||||
$this->resource_url = base_url()."/project/{$this->project_uuid}/".urlencode($this->environment_name)."/application/{$this->resource->uuid}";
|
||||
$this->resource_url = base_url() . "/project/{$this->project_uuid}/" . urlencode($this->environment_name) . "/application/{$this->resource->uuid}";
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
|
|
@ -57,17 +57,29 @@ class StatusChanged extends Notification implements ShouldQueue
|
|||
|
||||
public function toDiscord(): string
|
||||
{
|
||||
$message = 'Coolify: '.$this->resource_name.' has been stopped.
|
||||
$message = 'Coolify: ' . $this->resource_name . ' has been stopped.
|
||||
|
||||
';
|
||||
$message .= '[Open Application in Coolify]('.$this->resource_url.')';
|
||||
$message .= '[Open Application in Coolify](' . $this->resource_url . ')';
|
||||
|
||||
return $message;
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
$message = 'Coolify: ' . $this->resource_name . ' has been stopped.';
|
||||
|
||||
return [
|
||||
'title' => 'Coolify: Application Status Changed',
|
||||
'message' => $message,
|
||||
'buttons' => 'view, Go to your dashboard, ' . base_url() . ';',
|
||||
'emoji' => 'stop_sign',
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
$message = 'Coolify: '.$this->resource_name.' has been stopped.';
|
||||
$message = 'Coolify: ' . $this->resource_name . ' has been stopped.';
|
||||
|
||||
return [
|
||||
'message' => $message,
|
||||
|
|
|
|||
42
app/Notifications/Channels/NtfyChannel.php
Normal file
42
app/Notifications/Channels/NtfyChannel.php
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
namespace App\Notifications\Channels;
|
||||
|
||||
use App\Jobs\SendMessageToDiscordJob;
|
||||
use App\Jobs\SendMessageToNtfyJob;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
||||
class NtfyChannel
|
||||
{
|
||||
/**
|
||||
* Send the given notification.
|
||||
*/
|
||||
public function send(SendsNtfy $notifiable, Notification $notification): void
|
||||
{
|
||||
$content = $notification->toNtfy($notifiable);
|
||||
$message = $content['message'] ?? null;
|
||||
$buttons = $content['buttons'] ?? null;
|
||||
$emoji = $content['emoji'] ?? null;
|
||||
$title = $content['title'] ?? null;
|
||||
|
||||
$url_info = $notifiable->routeNotificationForNtfy();
|
||||
$topic = $url_info['topic'];
|
||||
$url = $url_info['url'];
|
||||
$username = $url_info['username'];
|
||||
$password = $url_info['password'];
|
||||
if (! $url_info) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(new SendMessageToNtfyJob(
|
||||
$message,
|
||||
$buttons,
|
||||
$emoji,
|
||||
$title,
|
||||
$url,
|
||||
$username,
|
||||
$password,
|
||||
$topic
|
||||
));
|
||||
}
|
||||
}
|
||||
8
app/Notifications/Channels/SendsNtfy.php
Normal file
8
app/Notifications/Channels/SendsNtfy.php
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace App\Notifications\Channels;
|
||||
|
||||
interface SendsNtfy
|
||||
{
|
||||
public function routeNotificationForNtfy();
|
||||
}
|
||||
|
|
@ -41,6 +41,16 @@ class ContainerRestarted extends Notification implements ShouldQueue
|
|||
return $message;
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'title' => "Coolify: A resource ({$this->name}) has been restarted",
|
||||
'message' => "Coolify: A resource ({$this->name}) has been restarted automatically on {$this->server->name}",
|
||||
'buttons' => 'view, Check Proxy in Coolify, '.$this->url.';',
|
||||
'emoji' => 'arrows_counterclockwise',
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
$message = "Coolify: A resource ({$this->name}) has been restarted automatically on {$this->server->name}";
|
||||
|
|
|
|||
|
|
@ -41,6 +41,16 @@ class ContainerStopped extends Notification implements ShouldQueue
|
|||
return $message;
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'title' => "Coolify: A resource ($this->name) has been stopped",
|
||||
'message' => "Coolify: A resource ($this->name) has been stopped unexpectedly on {$this->server->name}",
|
||||
'buttons' => 'view, Check Proxy in Coolify, '.$this->url.';',
|
||||
'emoji' => 'stop_sign',
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
$message = "Coolify: A resource ($this->name) has been stopped unexpectedly on {$this->server->name}";
|
||||
|
|
|
|||
|
|
@ -50,6 +50,14 @@ class BackupFailed extends Notification implements ShouldQueue
|
|||
return "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason:\n{$this->output}";
|
||||
}
|
||||
|
||||
public function toNtfy()
|
||||
{
|
||||
return [
|
||||
'title' => 'Coolify: Database backup has FAILED',
|
||||
'message' => "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason:\n{$this->output}",
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
$message = "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was FAILED.\n\nReason:\n{$this->output}";
|
||||
|
|
|
|||
|
|
@ -49,6 +49,14 @@ class BackupSuccess extends Notification implements ShouldQueue
|
|||
return "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was successful.";
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'Coolify: Database backup was successful',
|
||||
'message' => "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was successful.",
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
$message = "Coolify: Database backup for {$this->name} (db:{$this->database_name}) with frequency of {$this->frequency} was successful.";
|
||||
|
|
|
|||
|
|
@ -39,6 +39,14 @@ class DailyBackup extends Notification implements ShouldQueue
|
|||
return 'Coolify: Daily backup statuses';
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'Coolify: Daily backup statuses',
|
||||
'message' => 'Coolify: Daily backup statuses',
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
$message = 'Coolify: Daily backup statuses';
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace App\Notifications\Internal;
|
|||
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\NtfyChannel;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
|
@ -14,13 +15,16 @@ class GeneralNotification extends Notification implements ShouldQueue
|
|||
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct(public string $message) {}
|
||||
public function __construct(public string $message)
|
||||
{
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
$channels = [];
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isNtfyEnabled = data_get($notifiable, 'ntfy_enabled');
|
||||
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
|
|
@ -28,6 +32,9 @@ class GeneralNotification extends Notification implements ShouldQueue
|
|||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isNtfyEnabled) {
|
||||
$channels[] = NtfyChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
|
|
@ -37,6 +44,14 @@ class GeneralNotification extends Notification implements ShouldQueue
|
|||
return $this->message;
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'message' => $this->message,
|
||||
'buttons' => 'view, Go to your dashboard, '.base_url().';',
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -51,6 +51,16 @@ class TaskFailed extends Notification implements ShouldQueue
|
|||
return "Coolify: Scheduled task ({$this->task->name}, [link]({$this->url})) failed with output: {$this->output}";
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'Coolify: Scheduled task failed',
|
||||
'message' => "Coolify: Scheduled task ({$this->task->name}) failed with output: {$this->output}",
|
||||
'buttons' => 'view, Open task in Coolify, '.$this->url.';',
|
||||
'emoji' => 'warning',
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
$message = "Coolify: Scheduled task ({$this->task->name}) failed with output: {$this->output}";
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Notifications\Server;
|
|||
use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\NtfyChannel;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Notification;
|
||||
|
|
@ -23,6 +24,7 @@ class DockerCleanup extends Notification implements ShouldQueue
|
|||
// $isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isNtfyEnabled = data_get($notifiable, 'ntfy_enabled');
|
||||
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
|
|
@ -34,6 +36,10 @@ class DockerCleanup extends Notification implements ShouldQueue
|
|||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
|
||||
if ($isNtfyEnabled) {
|
||||
$channels[] = NtfyChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
|
||||
|
|
@ -49,6 +55,15 @@ class DockerCleanup extends Notification implements ShouldQueue
|
|||
// return $mail;
|
||||
// }
|
||||
|
||||
public function toNtfy()
|
||||
{
|
||||
return [
|
||||
'title' => "Coolify: Server '{$this->server->name}' cleanup job done!",
|
||||
'message' => "Coolify: Server '{$this->server->name}' cleanup job done!\n\n{$this->message}",
|
||||
'emoji' => 'wastebasket',
|
||||
];
|
||||
}
|
||||
|
||||
public function toDiscord(): string
|
||||
{
|
||||
$message = "Coolify: Server '{$this->server->name}' cleanup job done!\n\n{$this->message}";
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use App\Models\Server;
|
|||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\NtfyChannel;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
|
@ -17,7 +18,9 @@ class ForceDisabled extends Notification implements ShouldQueue
|
|||
|
||||
public $tries = 1;
|
||||
|
||||
public function __construct(public Server $server) {}
|
||||
public function __construct(public Server $server)
|
||||
{
|
||||
}
|
||||
|
||||
public function via(object $notifiable): array
|
||||
{
|
||||
|
|
@ -25,6 +28,7 @@ class ForceDisabled extends Notification implements ShouldQueue
|
|||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isNtfyEnabled = data_get($notifiable, 'ntfy_enabled');
|
||||
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
|
|
@ -35,6 +39,9 @@ class ForceDisabled extends Notification implements ShouldQueue
|
|||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isNtfyEnabled) {
|
||||
$channels[] = NtfyChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
|
|
@ -50,6 +57,16 @@ class ForceDisabled extends Notification implements ShouldQueue
|
|||
return $mail;
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'title' => "Coolify: Server ({$this->server->name}) disabled because it is not paid!",
|
||||
'message' => "All automations and integrations are stopped.\nPlease update your subscription to enable the server again [here](https://app.coolify.io/subsciprtions",
|
||||
'buttons' => 'view, Update subscription, '.base_url().'/subscriptions;',
|
||||
'emoji' => 'stop_sign',
|
||||
];
|
||||
}
|
||||
|
||||
public function toDiscord(): string
|
||||
{
|
||||
$message = "Coolify: Server ({$this->server->name}) disabled because it is not paid!\n All automations and integrations are stopped.\nPlease update your subscription to enable the server again [here](https://app.coolify.io/subsciprtions).";
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Notifications\Server;
|
|||
use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\NtfyChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
|
@ -25,6 +26,7 @@ class ForceEnabled extends Notification implements ShouldQueue
|
|||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isNtfyEnabled = data_get($notifiable, 'ntfy_enabled');
|
||||
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
|
|
@ -36,6 +38,10 @@ class ForceEnabled extends Notification implements ShouldQueue
|
|||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
|
||||
if ($isNtfyEnabled) {
|
||||
$channels[] = NtfyChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
|
||||
|
|
@ -57,6 +63,14 @@ class ForceEnabled extends Notification implements ShouldQueue
|
|||
return $message;
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'title' => "Coolify: Server ({$this->server->name}) enabled again!",
|
||||
'message' => "Coolify: Server ({$this->server->name}) enabled again!",
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Notifications\Server;
|
|||
use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\NtfyChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
|
@ -25,6 +26,7 @@ class HighDiskUsage extends Notification implements ShouldQueue
|
|||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isNtfyEnabled = data_get($notifiable, 'ntfy_enabled');
|
||||
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
|
|
@ -35,6 +37,9 @@ class HighDiskUsage extends Notification implements ShouldQueue
|
|||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isNtfyEnabled) {
|
||||
$channels[] = NtfyChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
|
|
@ -52,6 +57,15 @@ class HighDiskUsage extends Notification implements ShouldQueue
|
|||
return $mail;
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'title' => "Coolify: Server '{$this->server->name}' high disk usage detected!",
|
||||
'message' => "Disk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup.",
|
||||
'buttons' => 'view, Go to your dashboard, '.base_url().';',
|
||||
];
|
||||
}
|
||||
|
||||
public function toDiscord(): string
|
||||
{
|
||||
$message = "Coolify: Server '{$this->server->name}' high disk usage detected!\nDisk usage: {$this->disk_usage}%. Threshold: {$this->cleanup_after_percentage}%.\nPlease cleanup your disk to prevent data-loss.\nHere are some tips: https://coolify.io/docs/knowledge-base/server/automated-cleanup.";
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use App\Models\Server;
|
|||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Channels\NtfyChannel;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Notifications\Messages\MailMessage;
|
||||
|
|
@ -33,6 +34,7 @@ class Revived extends Notification implements ShouldQueue
|
|||
$channels = [];
|
||||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isNtfyEnabled = data_get($notifiable, 'ntfy_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
|
||||
if ($isDiscordEnabled) {
|
||||
|
|
@ -44,6 +46,9 @@ class Revived extends Notification implements ShouldQueue
|
|||
if ($isTelegramEnabled) {
|
||||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
if ($isNtfyEnabled) {
|
||||
$channels[] = NtfyChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
|
|
@ -66,6 +71,16 @@ class Revived extends Notification implements ShouldQueue
|
|||
return $message;
|
||||
}
|
||||
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'title' => "Coolify: Server '{$this->server->name}' revived.",
|
||||
'message' => "All automations & integrations are turned on again!",
|
||||
'emoji' => 'white_check_mark',
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Notifications\Server;
|
|||
use App\Models\Server;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\NtfyChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
|
|
@ -25,6 +26,7 @@ class Unreachable extends Notification implements ShouldQueue
|
|||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isNtfyEnabled = data_get($notifiable, 'ntfy_enabled');
|
||||
|
||||
if ($isDiscordEnabled) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
|
|
@ -36,6 +38,10 @@ class Unreachable extends Notification implements ShouldQueue
|
|||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
|
||||
if ($isNtfyEnabled) {
|
||||
$channels[] = NtfyChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
|
||||
|
|
@ -50,6 +56,14 @@ class Unreachable extends Notification implements ShouldQueue
|
|||
return $mail;
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'title' => "Coolify: Your server '{$this->server->name}' is unreachable.",
|
||||
'message' => 'All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server and turn on all automations & integrations.',
|
||||
];
|
||||
}
|
||||
|
||||
public function toDiscord(): string
|
||||
{
|
||||
$message = "Coolify: Your server '{$this->server->name}' is unreachable. All automations & integrations are turned off! Please check your server! IMPORTANT: We automatically try to revive your server and turn on all automations & integrations.";
|
||||
|
|
|
|||
|
|
@ -38,6 +38,16 @@ class Test extends Notification implements ShouldQueue
|
|||
return $message;
|
||||
}
|
||||
|
||||
public function toNtfy(): array
|
||||
{
|
||||
return [
|
||||
'title' => 'Coolify: Test Ntfy Notification',
|
||||
'message' => 'Coolify: This is a test Ntfy notification from Coolify.',
|
||||
'buttons' => 'view, Go to your dashboard, '.base_url().';',
|
||||
'emoji' => 'rocket',
|
||||
];
|
||||
}
|
||||
|
||||
public function toTelegram(): array
|
||||
{
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ use App\Models\Team;
|
|||
use App\Models\User;
|
||||
use App\Notifications\Channels\DiscordChannel;
|
||||
use App\Notifications\Channels\EmailChannel;
|
||||
use App\Notifications\Channels\NtfyChannel;
|
||||
use App\Notifications\Channels\TelegramChannel;
|
||||
use App\Notifications\Internal\GeneralNotification;
|
||||
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
|
||||
|
|
@ -425,9 +426,11 @@ function setNotificationChannels($notifiable, $event)
|
|||
$isEmailEnabled = isEmailEnabled($notifiable);
|
||||
$isDiscordEnabled = data_get($notifiable, 'discord_enabled');
|
||||
$isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
|
||||
$isNtfyEnabled = data_get($notifiable, 'ntfy_enabled');
|
||||
$isSubscribedToEmailEvent = data_get($notifiable, "smtp_notifications_$event");
|
||||
$isSubscribedToDiscordEvent = data_get($notifiable, "discord_notifications_$event");
|
||||
$isSubscribedToTelegramEvent = data_get($notifiable, "telegram_notifications_$event");
|
||||
$isSubscribedToNtfyEvent = data_get($notifiable, "ntfy_notifications_$event");
|
||||
|
||||
if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
|
||||
$channels[] = DiscordChannel::class;
|
||||
|
|
@ -439,6 +442,10 @@ function setNotificationChannels($notifiable, $event)
|
|||
$channels[] = TelegramChannel::class;
|
||||
}
|
||||
|
||||
if ($isNtfyEnabled && $isSubscribedToNtfyEvent) {
|
||||
$channels[] = NtfyChannel::class;
|
||||
}
|
||||
|
||||
return $channels;
|
||||
}
|
||||
function parseEnvFormatToArray($env_file_contents)
|
||||
|
|
@ -1322,7 +1329,6 @@ function parseDockerComposeFile(Service|Application $resource, bool $isNew = fal
|
|||
updateCompose($savedService);
|
||||
|
||||
return $service;
|
||||
|
||||
});
|
||||
|
||||
$envs_from_coolify = $resource->environment_variables()->get();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('teams', function (Blueprint $table) {
|
||||
$table->boolean('ntfy_enabled')->default(false);
|
||||
$table->text('ntfy_url')->nullable();
|
||||
$table->text('ntfy_topic')->nullable();
|
||||
$table->text('ntfy_username')->nullable();
|
||||
$table->text('ntfy_password')->nullable();
|
||||
$table->boolean('ntfy_notifications_test')->default(true);
|
||||
$table->boolean('ntfy_notifications_deployments')->default(true);
|
||||
$table->boolean('ntfy_notifications_status_changes')->default(true);
|
||||
$table->boolean('ntfy_notifications_database_backups')->default(true);
|
||||
$table->boolean('ntfy_notifications_scheduled_tasks')->default(true);
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('teams', function (Blueprint $table) {
|
||||
$table->dropColumn('ntfy_enabled');
|
||||
$table->dropColumn('ntfy_url');
|
||||
$table->dropColumn('ntfy_topic');
|
||||
$table->dropColumn('ntfy_username');
|
||||
$table->dropColumn('ntfy_password');
|
||||
$table->dropColumn('ntfy_notifications_test');
|
||||
$table->dropColumn('ntfy_notifications_deployments');
|
||||
$table->dropColumn('ntfy_notifications_status_changes');
|
||||
$table->dropColumn('ntfy_notifications_database_backups');
|
||||
$table->dropColumn('ntfy_notifications_scheduled_tasks');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -15,6 +15,10 @@
|
|||
href="{{ route('notifications.discord') }}">
|
||||
<button>Discord</button>
|
||||
</a>
|
||||
<a class="{{ request()->routeIs('notifications.ntfy') ? 'dark:text-white' : '' }}"
|
||||
href="{{ route('notifications.ntfy') }}">
|
||||
<button>Ntfy.sh</button>
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
56
resources/views/livewire/notifications/ntfy.blade.php
Normal file
56
resources/views/livewire/notifications/ntfy.blade.php
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
<div>
|
||||
<x-slot:title>
|
||||
Notifications | Coolify
|
||||
</x-slot>
|
||||
<x-notification.navbar />
|
||||
<form wire:submit='submit' class="flex flex-col gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<h2>Ntfy.sh</h2>
|
||||
<x-forms.button type="submit">
|
||||
Save
|
||||
</x-forms.button>
|
||||
@if ($team->ntfy_enabled)
|
||||
<x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
|
||||
wire:click="sendTestNotification">
|
||||
Send Test Notifications
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
<div class="w-32">
|
||||
<x-forms.checkbox instantSave id="team.ntfy_enabled" label="Enabled" />
|
||||
</div>
|
||||
|
||||
<x-forms.input type="text"
|
||||
helper="Enter your preferred ntfy host<br>Example: https://ntfy.sh" required
|
||||
id="team.ntfy_url" label="Host" />
|
||||
|
||||
<x-forms.input type="text"
|
||||
helper="Ntfy topic you want to subscribe to" required
|
||||
id="team.ntfy_topic" label="Topic" />
|
||||
|
||||
<div class="flex gap-2">
|
||||
<x-forms.input helper="If you have set up a user please enter its username"
|
||||
id="team.ntfy_username" label="Username" />
|
||||
<x-forms.input type="password"
|
||||
helper="If you have set up a user please enter its password"
|
||||
id="team.ntfy_password" label="Password" />
|
||||
</div>
|
||||
</form>
|
||||
@if (data_get($team, 'ntfy_enabled'))
|
||||
<h2 class="mt-4">Subscribe to events</h2>
|
||||
<div class="w-64">
|
||||
@if (isDev())
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.ntfy_notifications_test" label="Test" />
|
||||
@endif
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.ntfy_notifications_status_changes"
|
||||
label="Container Status Changes" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.ntfy_notifications_deployments"
|
||||
label="Application Deployments" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.ntfy_notifications_database_backups"
|
||||
label="Backup Status" />
|
||||
<x-forms.checkbox instantSave="saveModel" id="team.ntfy_notifications_scheduled_tasks"
|
||||
label="Scheduled Tasks Status" />
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
|
|
@ -13,6 +13,7 @@ use App\Livewire\ForcePasswordReset;
|
|||
use App\Livewire\Notifications\Discord as NotificationDiscord;
|
||||
use App\Livewire\Notifications\Email as NotificationEmail;
|
||||
use App\Livewire\Notifications\Telegram as NotificationTelegram;
|
||||
use App\Livewire\Notifications\Ntfy as NotificationNtfy;
|
||||
use App\Livewire\Profile\Index as ProfileIndex;
|
||||
use App\Livewire\Project\Application\Configuration as ApplicationConfiguration;
|
||||
use App\Livewire\Project\Application\Deployment\Index as DeploymentIndex;
|
||||
|
|
@ -126,6 +127,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
|
|||
Route::get('/email', NotificationEmail::class)->name('notifications.email');
|
||||
Route::get('/telegram', NotificationTelegram::class)->name('notifications.telegram');
|
||||
Route::get('/discord', NotificationDiscord::class)->name('notifications.discord');
|
||||
Route::get('/ntfy', NotificationNtfy::class)->name('notifications.ntfy');
|
||||
});
|
||||
|
||||
Route::prefix('storages')->group(function () {
|
||||
|
|
@ -274,7 +276,7 @@ Route::middleware(['auth'])->group(function () {
|
|||
if ($stream === false) {
|
||||
abort(500, 'Failed to open stream for the requested file.');
|
||||
}
|
||||
while (! feof($stream)) {
|
||||
while (!feof($stream)) {
|
||||
echo fread($stream, 2048);
|
||||
flush();
|
||||
}
|
||||
|
|
@ -282,7 +284,7 @@ Route::middleware(['auth'])->group(function () {
|
|||
fclose($stream);
|
||||
}, 200, [
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
'Content-Disposition' => 'attachment; filename="'.basename($filename).'"',
|
||||
'Content-Disposition' => 'attachment; filename="' . basename($filename) . '"',
|
||||
]);
|
||||
} catch (\Throwable $e) {
|
||||
return response()->json(['message' => $e->getMessage()], 500);
|
||||
|
|
@ -325,7 +327,7 @@ Route::middleware(['auth'])->group(function () {
|
|||
Route::get('/destination/{destination_uuid}', function () {
|
||||
$standalone_dockers = StandaloneDocker::where('uuid', request()->destination_uuid)->first();
|
||||
$swarm_dockers = SwarmDocker::where('uuid', request()->destination_uuid)->first();
|
||||
if (! $standalone_dockers && ! $swarm_dockers) {
|
||||
if (!$standalone_dockers && !$swarm_dockers) {
|
||||
abort(404);
|
||||
}
|
||||
$destination = $standalone_dockers ? $standalone_dockers : $swarm_dockers;
|
||||
|
|
|
|||
Loading…
Reference in a new issue