Starts Notifications feature. Missing to send email with runtime configs.

This commit is contained in:
Joao Patricio 2023-05-19 18:01:56 +01:00
parent cd5655bd3f
commit 13fda50aac
16 changed files with 334 additions and 35 deletions

6
.gitignore vendored
View File

@ -20,10 +20,8 @@ yarn-error.log
/.npm
/.bash_history
/_data
# Temp while developing Proxy deployment
resources/recipes
_testing_hosts/
_volumes/
.lesshst
psysh_history
.psql_history

View File

@ -0,0 +1,38 @@
<?php
namespace App\Http\Livewire\Settings;
use App\Models\InstanceSettings as ModelsInstanceSettings;
use App\Notifications\TestMessage;
use Illuminate\Support\Facades\Notification;
use Livewire\Component;
class DiscordNotifications extends Component
{
public ModelsInstanceSettings $settings;
protected $rules = [
'settings.extra_attributes.discord_webhook' => 'nullable|url',
];
protected $validationAttributes = [
'settings.extra_attributes.discord_webhook' => 'Discord Webhook',
];
public function mount($settings)
{
//
}
public function submit()
{
$this->resetErrorBag();
$this->validate();
$this->settings->save();
}
public function sentTestMessage()
{
// @TODO figure out how to do it in runtime
}
public function render()
{
return view('livewire.settings.discord-notifications');
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace App\Http\Livewire\Settings;
use App\Models\InstanceSettings as ModelsInstanceSettings;
use App\Notifications\TestMessage;
use Illuminate\Support\Facades\Notification;
use Livewire\Component;
class EmailNotifications extends Component
{
public ModelsInstanceSettings $settings;
protected $rules = [
'settings.extra_attributes.smtp_host' => 'nullable',
'settings.extra_attributes.smtp_port' => 'nullable',
'settings.extra_attributes.smtp_encryption' => 'nullable',
'settings.extra_attributes.smtp_username' => 'nullable',
'settings.extra_attributes.smtp_password' => 'nullable',
'settings.extra_attributes.smtp_timeout' => 'nullable',
];
protected $validationAttributes = [
'settings.extra_attributes.smtp_host' => 'Host',
'settings.extra_attributes.smtp_port' => 'Port',
'settings.extra_attributes.smtp_encryption' => 'Encryption',
'settings.extra_attributes.smtp_username' => 'Username',
'settings.extra_attributes.smtp_password' => 'Password',
'settings.extra_attributes.smtp_timeout' => 'Timeout',
];
public function mount($settings)
{
ray($settings);
//
}
public function submit()
{
$this->resetErrorBag();
$this->validate();
$this->settings->save();
}
public function sentTestMessage()
{
Notification::send(auth()->user(), new TestMessage);
}
public function render()
{
return view('livewire.settings.email-notifications');
}
}

View File

@ -0,0 +1,45 @@
<?php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Http;
class SendMessageToDiscordJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The number of times the job may be attempted.
*
* @var int
*/
public $tries = 5;
/**
* The maximum number of unhandled exceptions to allow before failing.
*/
public int $maxExceptions = 3;
public function __construct(
public string $text,
public string $webhookUrl
) {}
/**
* Execute the job.
*/
public function handle(): void
{
$payload = [
'content' => $this->text,
];
Http::post($this->webhookUrl, $payload);
}
}

View File

@ -2,10 +2,21 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
class InstanceSettings extends Model
{
public $casts = [
'extra_attributes' => SchemalessAttributes::class,
];
public function scopeWithExtraAttributes(): Builder
{
return $this->extra_attributes->modelScope();
}
public static function get()
{
return InstanceSettings::findOrFail(0);

View File

@ -0,0 +1,25 @@
<?php
namespace App\Notifications\Channels;
use App\Jobs\SendMessageToDiscordJob;
use App\Models\InstanceSettings;
use Illuminate\Notifications\Notification;
class DiscordChannel
{
/**
* Send the given notification.
*/
public function send(object $notifiable, Notification $notification): void
{
$message = $notification->toDiscord($notifiable);
$webhookUrl = data_get(
InstanceSettings::get(),
'extra_attributes.discord_webhook'
);
dispatch(new SendMessageToDiscordJob($message, $webhookUrl));
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace App\Notifications;
use App\Notifications\Channels\DiscordChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class TestMessage extends Notification
{
use Queueable;
/**
* Create a new notification instance.
*/
public function __construct()
{
//
}
/**
* Get the notification's delivery channels.
*
* @return array<int, string>
*/
public function via(object $notifiable): array
{
return ['mail', DiscordChannel::class];
}
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->line('Welcome to Coolify!')
->action('Go to dashboard', url('/'))
->line('We need your attention for disk usage.');
}
public function toDiscord(object $notifiable): string
{
return 'Welcome to Coolify! We need your attention for disk usage. [Go to dashboard]('.url('/').')';
}
/**
* Get the array representation of the notification.
*
* @return array<string, mixed>
*/
public function toArray(object $notifiable): array
{
return [
//
];
}
}

View File

@ -3,11 +3,13 @@
namespace App\Providers;
use App\Jobs\CoolifyTask;
use Illuminate\Mail\MailManager;
use Illuminate\Queue\Events\JobProcessed;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Symfony\Component\Mailer\Transport\Smtp\SmtpTransport;
class AppServiceProvider extends ServiceProvider
{
@ -26,6 +28,10 @@ public function register(): void
*/
public function boot(): void
{
if (! $this->app->environment('production')) {
\Illuminate\Support\Facades\Mail::alwaysTo('noone@example.com');
}
Queue::after(function (JobProcessed $event) {
// @TODO: Remove `coolify-builder` container after the remoteProcess job is finishged and remoteProcess->type == `deployment`.
if ($event->job->resolveName() === CoolifyTask::class) {

View File

@ -27,6 +27,9 @@ public function up(): void
// $table->boolean('is_dns_check_enabled')->default(true);
$table->boolean('is_registration_enabled')->default(true);
$table->boolean('is_https_forced')->default(true);
$table->schemalessAttributes('extra_attributes');
$table->timestamps();
});
}

View File

@ -64,3 +64,10 @@ services:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- "./_data/coolify/proxy/testing-host-2:/data/coolify/proxy"
mailpit:
image: 'axllent/mailpit:latest'
ports:
- '${FORWARD_MAILPIT_PORT:-1025}:1025'
- '${FORWARD_MAILPIT_DASHBOARD_PORT:-8025}:8025'
networks:
- coolify

View File

@ -5,26 +5,26 @@
'confirmAction' => null,
])
@if ($type === 'submit')
<button {{ $attributes }} type="submit" @if ($disabled !== null) disabled @endif wire:target="submit"
wire:loading.delay.shorter.class="loading"
@isset($confirm)
x-on:click="toggleConfirmModal('{{ $confirm }}', '{{ explode('(', $confirmAction)[0] }}')"
@endisset
@isset($confirmAction)
x-on:{{ explode('(', $confirmAction)[0] }}.window="$wire.{{ explode('(', $confirmAction)[0] }}"
@endisset>
<button {{ $attributes }} type="submit" @disabled($disabled) wire:target="submit"
wire:loading.delay.shorter.class="loading"
@isset($confirm)
x-on:click="toggleConfirmModal('{{ $confirm }}', '{{ explode('(', $confirmAction)[0] }}')"
@endisset
@isset($confirmAction)
x-on:{{ explode('(', $confirmAction)[0] }}.window="$wire.{{ explode('(', $confirmAction)[0] }}"
@endisset>
{{ $slot }}
</button>
@elseif($type === 'button')
<button {{ $attributes }} @if ($disabled !== null) disabled @endif type="button"
wire:target="{{ explode('(', $attributes->whereStartsWith('wire:click')->first())[0] }}"
wire:loading.delay.shorter.class="loading"
@isset($confirm)
x-on:click="toggleConfirmModal('{{ $confirm }}', '{{ explode('(', $confirmAction)[0] }}')"
@endisset
@isset($confirmAction)
x-on:{{ explode('(', $confirmAction)[0] }}.window="$wire.{{ explode('(', $confirmAction)[0] }}"
@endisset>
<button {{ $attributes }} @disabled($disabled) type="button"
wire:target="{{ explode('(', $attributes->whereStartsWith('wire:click')->first())[0] }}"
wire:loading.delay.shorter.class="loading"
@isset($confirm)
x-on:click="toggleConfirmModal('{{ $confirm }}', '{{ explode('(', $confirmAction)[0] }}')"
@endisset
@isset($confirmAction)
x-on:{{ explode('(', $confirmAction)[0] }}.window="$wire.{{ explode('(', $confirmAction)[0] }}"
@endisset>
{{ $slot }}
</button>
@endif

View File

@ -0,0 +1,15 @@
<div class="">
<form wire:submit.prevent='submit' class="flex flex-col">
<div class="flex flex-col gap-2 xl:flex-row w-96">
<x-inputs.input id="settings.extra_attributes.discord_webhook" label="Discord Webhook" />
</div>
<div>
<x-inputs.button class="w-16 mt-4" type="submit">
Submit
</x-inputs.button>
<x-inputs.button class="mt-4 btn btn-xs no-animation normal-case text-white btn-primary" wire:click="sentTestMessage">
Send test message
</x-inputs.button>
</div>
</form>
</div>

View File

@ -0,0 +1,24 @@
<div class="mt-10">
<form wire:submit.prevent='submit' class="flex flex-col">
<div class="flex flex-col gap-2 xl:flex-row">
<div class="flex flex-col w-96">
<x-inputs.input id="settings.extra_attributes.smtp_host" label="Host" />
<x-inputs.input id="settings.extra_attributes.smtp_port" label="Port" />
<x-inputs.input id="settings.extra_attributes.smtp_encryption" label="Encryption" />
</div>
<div class="flex flex-col w-96">
<x-inputs.input id="settings.extra_attributes.smtp_username" label="Username" />
<x-inputs.input id="settings.extra_attributes.smtp_password" label="Password" />
<x-inputs.input id="settings.extra_attributes.smtp_timeout" label="Timeout" />
</div>
</div>
<div>
<x-inputs.button class="w-16 mt-4" type="submit">
Submit
</x-inputs.button>
<x-inputs.button class="mt-4 btn btn-xs no-animation normal-case text-white btn-primary" wire:click="sentTestMessage">
Send test message
</x-inputs.button>
</div>
</form>
</div>

View File

@ -1,4 +1,12 @@
<x-layout>
<h1>Settings</h1>
<h3>General</h3>
<livewire:settings.form :settings="$settings" />
<div class="my-12"></div>
<h3>Notifications</h3>
<livewire:settings.discord-notifications :settings="$settings" />
<livewire:settings.email-notifications :settings="$settings" />
</x-layout>

View File

@ -1,7 +1,8 @@
<x-layout>
<div>
<h3>Current Team</h3>
<p>Name: {{ session('currentTeam')->name }}</p>
<livewire:switch-team>
<p>Name: {{ session('currentTeam.name') }}</p>
<livewire:switch-team/>
</div>
</x-layout>

View File

@ -2,6 +2,7 @@
use Illuminate\Foundation\Inspiring;
use Illuminate\Support\Facades\Artisan;
use Symfony\Component\Mailer\Mailer;
/*
|--------------------------------------------------------------------------
@ -14,19 +15,27 @@
|
*/
// Artisan::command('inspire', function () {
Artisan::command('inspire', function () {
// $activity = Spatie\Activitylog\Models\Activity::latest()->first();
$smtp = [
"transport" => "smtp",
"host" => "mailpit",
"port" => 1025,
"encryption" => 'tls',
"username" => null,
"password" => null,
"timeout" => null,
"local_domain" => null,
];
config()->set('mail.mailers.smtp', $smtp);
// $this->info(
// collect(
// json_decode(data_get($activity, 'description'), associative: true, flags: JSON_THROW_ON_ERROR)
// )
// ->sortBy('order')
// ->map(fn($i) => $i['output'])
// ->implode("\n")
// );
// \Illuminate\Support\Facades\Mail::mailer('smtp')
// ->to('ask@me.com')
// ->send(new \App\Mail\TestMail);
\Illuminate\Support\Facades\Notification::send(
\App\Models\User::find(1),
new \App\Notifications\TestMessage
);
// })->purpose('Display an inspiring quote');
})->purpose('Display an inspiring quote');