diff --git a/.gitignore b/.gitignore
index d8bc3e6b2..e1b6b679e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/app/Console/Commands/NotifyDemo.php b/app/Console/Commands/NotifyDemo.php
new file mode 100644
index 000000000..1d5906bd0
--- /dev/null
+++ b/app/Console/Commands/NotifyDemo.php
@@ -0,0 +1,75 @@
+argument('channel');
+
+ if (blank($channel)) {
+ $this->showHelp();
+ return;
+ }
+
+ ray($channel);
+ }
+
+ private function showHelp()
+ {
+ style('coolify')->color('#9333EA');
+ style('title-box')->apply('mt-1 px-2 py-1 bg-coolify');
+
+ render(<<<'HTML'
+
+
+ Coolify
+
+
+ Demo Notify => Send a demo notification to a given channel.
+
+
+ php artisan app:demo-notify {channel}
+
+
+
Channels:
+
+ - email
+ - slack
+ - discord
+ - telegram
+
+
+
+ HTML);
+
+ ask(<<<'HTML'
+
+ In which manner you wish a coolified notification?
+
+ HTML, ['email', 'slack', 'discord', 'telegram']);
+ }
+}
diff --git a/app/Http/Livewire/Notifications/DiscordSettings.php b/app/Http/Livewire/Notifications/DiscordSettings.php
new file mode 100644
index 000000000..239d080a6
--- /dev/null
+++ b/app/Http/Livewire/Notifications/DiscordSettings.php
@@ -0,0 +1,43 @@
+ 'nullable|url',
+ 'model.extra_attributes.discord_active' => 'nullable|boolean',
+ ];
+ protected $validationAttributes = [
+ 'model.extra_attributes.discord_webhook' => 'Discord Webhook',
+ ];
+ public function mount($model)
+ {
+ //
+ }
+ public function submit()
+ {
+ $this->resetErrorBag();
+ $this->validate();
+ $this->model->save();
+ if ( is_a($this->model, Team::class)) {
+ session(['currentTeam' => $this->model]);
+ }
+ }
+ public function sendTestNotification()
+ {
+ Notification::send($this->model, new DemoNotification);
+ }
+ public function render()
+ {
+ return view('livewire.notifications.discord-settings');
+ }
+}
diff --git a/app/Http/Livewire/Notifications/EmailSettings.php b/app/Http/Livewire/Notifications/EmailSettings.php
new file mode 100644
index 000000000..26f955925
--- /dev/null
+++ b/app/Http/Livewire/Notifications/EmailSettings.php
@@ -0,0 +1,59 @@
+ 'nullable|boolean',
+ 'model.extra_attributes.from_address' => 'nullable',
+ 'model.extra_attributes.from_name' => 'nullable',
+ 'model.extra_attributes.recipients' => 'nullable',
+ 'model.extra_attributes.smtp_host' => 'nullable',
+ 'model.extra_attributes.smtp_port' => 'nullable',
+ 'model.extra_attributes.smtp_encryption' => 'nullable',
+ 'model.extra_attributes.smtp_username' => 'nullable',
+ 'model.extra_attributes.smtp_password' => 'nullable',
+ 'model.extra_attributes.smtp_timeout' => 'nullable',
+ ];
+ protected $validationAttributes = [
+ 'model.extra_attributes.from_address' => 'From Address',
+ 'model.extra_attributes.from_name' => 'From Name',
+ 'model.extra_attributes.recipients' => 'Recipients',
+ 'model.extra_attributes.smtp_host' => 'Host',
+ 'model.extra_attributes.smtp_port' => 'Port',
+ 'model.extra_attributes.smtp_encryption' => 'Encryption',
+ 'model.extra_attributes.smtp_username' => 'Username',
+ 'model.extra_attributes.smtp_password' => 'Password',
+ 'model.extra_attributes.smtp_timeout' => 'Timeout',
+ ];
+ public function mount($model)
+ {
+ //
+ }
+ public function submit()
+ {
+ $this->resetErrorBag();
+ $this->validate();
+ $this->model->save();
+ if ( is_a($this->model, Team::class)) {
+ session(['currentTeam' => $this->model]);
+ }
+ }
+ public function sendTestNotification()
+ {
+ Notification::send($this->model, new DemoNotification);
+ }
+ public function render()
+ {
+ return view('livewire.notifications.email-settings');
+ }
+}
diff --git a/app/Jobs/SendMessageToDiscordJob.php b/app/Jobs/SendMessageToDiscordJob.php
new file mode 100644
index 000000000..8f1962829
--- /dev/null
+++ b/app/Jobs/SendMessageToDiscordJob.php
@@ -0,0 +1,45 @@
+ $this->text,
+ ];
+
+ Http::post($this->webhookUrl, $payload);
+ }
+}
diff --git a/app/Models/Team.php b/app/Models/Team.php
index aaadcbb5a..fd5a0f06e 100644
--- a/app/Models/Team.php
+++ b/app/Models/Team.php
@@ -2,28 +2,59 @@
namespace App\Models;
-class Team extends BaseModel
+use App\Notifications\Channels\SendsCoolifyEmail;
+use App\Notifications\Channels\SendsDiscord;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Notifications\Notifiable;
+use Spatie\SchemalessAttributes\Casts\SchemalessAttributes;
+
+class Team extends BaseModel implements SendsDiscord, SendsCoolifyEmail
{
+ use Notifiable;
+
protected $casts = [
+ 'extra_attributes' => SchemalessAttributes::class,
'personal_team' => 'boolean',
];
protected $fillable = [
'id',
'name',
- 'personal_team'
+ 'personal_team',
+ 'extra_attributes',
];
+
+ public function routeNotificationForDiscord()
+ {
+ return $this->extra_attributes->get('discord_webhook');
+ }
+
+ public function routeNotificationForCoolifyEmail()
+ {
+ $recipients = $this->extra_attributes->get('recipients', '');
+
+ return explode(PHP_EOL, $recipients);
+ }
+
+ public function scopeWithExtraAttributes(): Builder
+ {
+ return $this->extra_attributes->modelScope();
+ }
+
public function projects()
{
return $this->hasMany(Project::class);
}
+
public function servers()
{
return $this->hasMany(Server::class);
}
+
public function applications()
{
return $this->hasManyThrough(Application::class, Project::class);
}
+
public function privateKeys()
{
return $this->hasMany(PrivateKey::class);
diff --git a/app/Notifications/Channels/CoolifyEmailChannel.php b/app/Notifications/Channels/CoolifyEmailChannel.php
new file mode 100644
index 000000000..3f122070f
--- /dev/null
+++ b/app/Notifications/Channels/CoolifyEmailChannel.php
@@ -0,0 +1,44 @@
+bootConfigs($notifiable);
+ $bcc = $notifiable->routeNotificationForCoolifyEmail();
+ $mailMessage = $notification->toMail($notifiable);
+
+ Mail::send([], [], fn(Message $message) => $message
+ ->from(
+ $notifiable->extra_attributes?->get('from_address'),
+ $notifiable->extra_attributes?->get('from_name')
+ )
+ ->bcc($bcc)
+ ->subject($mailMessage->subject)
+ ->html((string)$mailMessage->render())
+ );
+ }
+
+ private function bootConfigs($notifiable): void
+ {
+ config()->set('mail.mailers.smtp', [
+ "transport" => "smtp",
+ "host" => $notifiable->extra_attributes?->get('smtp_host'),
+ "port" => $notifiable->extra_attributes?->get('smtp_port'),
+ "encryption" => $notifiable->extra_attributes?->get('smtp_encryption'),
+ "username" => $notifiable->extra_attributes?->get('smtp_username'),
+ "password" => $notifiable->extra_attributes?->get('smtp_password'),
+ "timeout" => $notifiable->extra_attributes?->get('smtp_timeout'),
+ "local_domain" => null,
+ ]);
+ }
+}
diff --git a/app/Notifications/Channels/DiscordChannel.php b/app/Notifications/Channels/DiscordChannel.php
new file mode 100644
index 000000000..06c8c0130
--- /dev/null
+++ b/app/Notifications/Channels/DiscordChannel.php
@@ -0,0 +1,21 @@
+toDiscord($notifiable);
+
+ $webhookUrl = $notifiable->routeNotificationForDiscord();
+
+ dispatch(new SendMessageToDiscordJob($message, $webhookUrl));
+ }
+}
diff --git a/app/Notifications/Channels/SendsCoolifyEmail.php b/app/Notifications/Channels/SendsCoolifyEmail.php
new file mode 100644
index 000000000..e1698a1fe
--- /dev/null
+++ b/app/Notifications/Channels/SendsCoolifyEmail.php
@@ -0,0 +1,8 @@
+
+ */
+ public function via(object $notifiable): array
+ {
+ $channels = [];
+ $notifiable->extra_attributes?->get('smtp_active') && $channels[] = CoolifyEmailChannel::class;
+ $notifiable->extra_attributes?->get('discord_active') && $channels[] = DiscordChannel::class;
+ return $channels;
+ }
+
+ /**
+ * Get the mail representation of the notification.
+ */
+ public function toMail(object $notifiable): MailMessage
+ {
+ return (new MailMessage)
+ ->subject('Coolify demo notification')
+ ->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
+ */
+ public function toArray(object $notifiable): array
+ {
+ return [
+ //
+ ];
+ }
+}
diff --git a/database/migrations/2023_03_20_112811_create_teams_table.php b/database/migrations/2023_03_20_112811_create_teams_table.php
index 77e7b840f..4396c8db7 100644
--- a/database/migrations/2023_03_20_112811_create_teams_table.php
+++ b/database/migrations/2023_03_20_112811_create_teams_table.php
@@ -16,6 +16,7 @@ return new class extends Migration
$table->string('uuid')->unique();
$table->string('name');
$table->boolean('personal_team')->default(false);
+ $table->schemalessAttributes('extra_attributes');
$table->timestamps();
});
}
diff --git a/database/migrations/2023_03_20_112813_create_instance_settings_table.php b/database/migrations/2023_03_20_112813_create_instance_settings_table.php
index e8396b036..22c709fb8 100644
--- a/database/migrations/2023_03_20_112813_create_instance_settings_table.php
+++ b/database/migrations/2023_03_20_112813_create_instance_settings_table.php
@@ -21,8 +21,8 @@ return new class extends Migration
$table->boolean('do_not_track')->default(false);
$table->boolean('is_auto_update_enabled')->default(true);
$table->boolean('is_registration_enabled')->default(true);
- // $table->string('preview_domain_separator')->default('.');
// $table->string('custom_dns_servers')->default('1.1.1.1,8.8.8.8');
+ // $table->string('preview_domain_separator')->default('.');
// $table->boolean('is_dns_check_enabled')->default(true);
$table->timestamps();
});
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index 475f4e7c0..07177d96f 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -62,6 +62,11 @@ 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'
buggregator:
image: ghcr.io/buggregator/server:latest
container_name: coolify-debug
diff --git a/resources/views/emails/example.blade.php b/resources/views/emails/example.blade.php
new file mode 100644
index 000000000..bbfbe24b1
--- /dev/null
+++ b/resources/views/emails/example.blade.php
@@ -0,0 +1 @@
+Hello I am an example
diff --git a/resources/views/livewire/notifications/discord-settings.blade.php b/resources/views/livewire/notifications/discord-settings.blade.php
new file mode 100644
index 000000000..9a1980d6f
--- /dev/null
+++ b/resources/views/livewire/notifications/discord-settings.blade.php
@@ -0,0 +1,23 @@
+
diff --git a/resources/views/livewire/notifications/email-settings.blade.php b/resources/views/livewire/notifications/email-settings.blade.php
new file mode 100644
index 000000000..dbcf16c3b
--- /dev/null
+++ b/resources/views/livewire/notifications/email-settings.blade.php
@@ -0,0 +1,45 @@
+
diff --git a/resources/views/settings.blade.php b/resources/views/settings.blade.php
index 761a5417f..389f8be41 100644
--- a/resources/views/settings.blade.php
+++ b/resources/views/settings.blade.php
@@ -1,3 +1,4 @@
+ Settings
diff --git a/resources/views/team.blade.php b/resources/views/team.blade.php
index 01c89f164..d0de056dc 100644
--- a/resources/views/team.blade.php
+++ b/resources/views/team.blade.php
@@ -1,8 +1,13 @@
- Teams
-
-
Currently Active Team:
-
{{ session('currentTeam')->name }}
+
+
Current Team
+
Name: {{ session('currentTeam.name') }}
+
+
+
Notifications
+
+
+
diff --git a/routes/console.php b/routes/console.php
index d2977bec5..e05f4c9a1 100644
--- a/routes/console.php
+++ b/routes/console.php
@@ -14,19 +14,6 @@ use Illuminate\Support\Facades\Artisan;
|
*/
-// Artisan::command('inspire', function () {
-
-// $activity = Spatie\Activitylog\Models\Activity::latest()->first();
-
-// $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")
-// );
-
-
-
-// })->purpose('Display an inspiring quote');
+Artisan::command('inspire', function () {
+ $this->comment(Inspiring::quote());
+})->purpose('Display an inspiring quote');