diff --git a/app/Console/Commands/CleanupUnreachableServers.php b/app/Console/Commands/CleanupUnreachableServers.php
index df0c6b81b..def01b265 100644
--- a/app/Console/Commands/CleanupUnreachableServers.php
+++ b/app/Console/Commands/CleanupUnreachableServers.php
@@ -18,7 +18,6 @@ class CleanupUnreachableServers extends Command
if ($servers->count() > 0) {
foreach ($servers as $server) {
echo "Cleanup unreachable server ($server->id) with name $server->name";
- // send_internal_notification("Server $server->name is unreachable for 7 days. Cleaning up...");
$server->update([
'ip' => '1.2.3.4',
]);
diff --git a/app/Console/Commands/Emails.php b/app/Console/Commands/Emails.php
index f0e0e7fa0..33ddf3019 100644
--- a/app/Console/Commands/Emails.php
+++ b/app/Console/Commands/Emails.php
@@ -2,14 +2,12 @@
namespace App\Console\Commands;
-use App\Jobs\SendConfirmationForWaitlistJob;
use App\Models\Application;
use App\Models\ApplicationPreview;
use App\Models\ScheduledDatabaseBackup;
use App\Models\Server;
use App\Models\StandalonePostgresql;
use App\Models\Team;
-use App\Models\Waitlist;
use App\Notifications\Application\DeploymentFailed;
use App\Notifications\Application\DeploymentSuccess;
use App\Notifications\Application\StatusChanged;
@@ -64,8 +62,6 @@ class Emails extends Command
'backup-success' => 'Database - Backup Success',
'backup-failed' => 'Database - Backup Failed',
// 'invitation-link' => 'Invitation Link',
- 'waitlist-invitation-link' => 'Waitlist Invitation Link',
- 'waitlist-confirmation' => 'Waitlist Confirmation',
'realusers-before-trial' => 'REAL - Registered Users Before Trial without Subscription',
'realusers-server-lost-connection' => 'REAL - Server Lost Connection',
],
@@ -187,7 +183,7 @@ class Emails extends Command
'team_id' => 0,
]);
}
- // $this->mail = (new BackupSuccess($backup->frequency, $db->name))->toMail();
+ //$this->mail = (new BackupSuccess($backup->frequency, $db->name))->toMail();
$this->sendEmail();
break;
// case 'invitation-link':
@@ -204,23 +200,6 @@ class Emails extends Command
// $this->mail = (new InvitationLink($user))->toMail();
// $this->sendEmail();
// break;
- case 'waitlist-invitation-link':
- $this->mail = new MailMessage;
- $this->mail->view('emails.waitlist-invitation', [
- 'loginLink' => 'https://coolify.io',
- ]);
- $this->mail->subject('Congratulations! You are invited to join Coolify Cloud.');
- $this->sendEmail();
- break;
- case 'waitlist-confirmation':
- $found = Waitlist::where('email', $this->email)->first();
- if ($found) {
- SendConfirmationForWaitlistJob::dispatch($this->email, $found->uuid);
- } else {
- throw new Exception('Waitlist not found');
- }
-
- break;
case 'realusers-before-trial':
$this->mail = new MailMessage;
$this->mail->view('emails.before-trial-conversion');
diff --git a/app/Console/Commands/WaitlistInvite.php b/app/Console/Commands/WaitlistInvite.php
deleted file mode 100644
index 2e330068c..000000000
--- a/app/Console/Commands/WaitlistInvite.php
+++ /dev/null
@@ -1,114 +0,0 @@
-option('people');
- for ($i = 0; $i < $people; $i++) {
- $this->main();
- }
- }
-
- private function main()
- {
- if ($this->argument('email')) {
- if ($this->option('only-email')) {
- $this->next_patient = User::whereEmail($this->argument('email'))->first();
- $this->password = Str::password();
- $this->next_patient->update([
- 'password' => Hash::make($this->password),
- 'force_password_reset' => true,
- ]);
- } else {
- $this->next_patient = Waitlist::where('email', $this->argument('email'))->first();
- }
- if (! $this->next_patient) {
- $this->error("{$this->argument('email')} not found in the waitlist.");
-
- return;
- }
- } else {
- $this->next_patient = Waitlist::orderBy('created_at', 'asc')->where('verified', true)->first();
- }
- if ($this->next_patient) {
- if ($this->option('only-email')) {
- $this->send_email();
-
- return;
- }
- $this->register_user();
- $this->remove_from_waitlist();
- $this->send_email();
- } else {
- $this->info('No verified user found in the waitlist. 👀');
- }
- }
-
- private function register_user()
- {
- $already_registered = User::whereEmail($this->next_patient->email)->first();
- if (! $already_registered) {
- $this->password = Str::password();
- User::create([
- 'name' => str($this->next_patient->email)->before('@'),
- 'email' => $this->next_patient->email,
- 'password' => Hash::make($this->password),
- 'force_password_reset' => true,
- ]);
- $this->info("User registered ({$this->next_patient->email}) successfully. 🎉");
- } else {
- throw new \Exception('User already registered');
- }
- }
-
- private function remove_from_waitlist()
- {
- $this->next_patient->delete();
- $this->info('User removed from waitlist successfully.');
- }
-
- private function send_email()
- {
- $token = Crypt::encryptString("{$this->next_patient->email}@@@$this->password");
- $loginLink = route('auth.link', ['token' => $token]);
- $mail = new MailMessage;
- $mail->view('emails.waitlist-invitation', [
- 'loginLink' => $loginLink,
- ]);
- $mail->subject('Congratulations! You are invited to join Coolify Cloud.');
- send_user_an_email($mail, $this->next_patient->email);
- $this->info('Email sent successfully. 📧');
- }
-}
diff --git a/app/Console/Commands/Weird.php b/app/Console/Commands/Weird.php
deleted file mode 100644
index e471a5f96..000000000
--- a/app/Console/Commands/Weird.php
+++ /dev/null
@@ -1,58 +0,0 @@
-error('This command can only be run in development mode');
-
- return;
- }
- $run = $this->option('run');
- if ($run) {
- $servers = Server::all();
- foreach ($servers as $server) {
- ServerCheck::dispatch($server);
- }
-
- return;
- }
- $number = $this->option('number');
- for ($i = 0; $i < $number; $i++) {
- $uuid = Str::uuid();
- $server = Server::create([
- 'name' => 'localhost-'.$uuid,
- 'description' => 'This is a test docker container in development mode',
- 'ip' => 'coolify-testing-host',
- 'team_id' => 0,
- 'private_key_id' => 1,
- 'proxy' => [
- 'type' => ProxyTypes::NONE->value,
- 'status' => ProxyStatus::EXITED->value,
- ],
- ]);
- $server->settings->update([
- 'is_usable' => true,
- 'is_reachable' => true,
- ]);
- }
- } catch (\Exception $e) {
- $this->error($e->getMessage());
- }
- }
-}
diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php
index 9f1e4eeb8..522683efa 100644
--- a/app/Http/Controllers/Controller.php
+++ b/app/Http/Controllers/Controller.php
@@ -42,15 +42,13 @@ class Controller extends BaseController
public function email_verify(EmailVerificationRequest $request)
{
$request->fulfill();
- $name = request()->user()?->name;
- // send_internal_notification("User {$name} verified their email address.");
return redirect(RouteServiceProvider::HOME);
}
public function forgot_password(Request $request)
{
- if (is_transactional_emails_active()) {
+ if (is_transactional_emails_enabled()) {
$arrayOfRequest = $request->only(Fortify::email());
$request->merge([
'email' => Str::lower($arrayOfRequest['email']),
diff --git a/app/Http/Controllers/Webhook/Waitlist.php b/app/Http/Controllers/Webhook/Waitlist.php
deleted file mode 100644
index dec8ca72d..000000000
--- a/app/Http/Controllers/Webhook/Waitlist.php
+++ /dev/null
@@ -1,63 +0,0 @@
-get('email');
- $confirmation_code = request()->get('confirmation_code');
- try {
- $found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
- if ($found) {
- if (! $found->verified) {
- if ($found->created_at > now()->subMinutes(config('constants.waitlist.expiration'))) {
- $found->verified = true;
- $found->save();
- send_internal_notification('Waitlist confirmed: '.$email);
-
- return 'Thank you for confirming your email address. We will notify you when you are next in line.';
- } else {
- $found->delete();
- send_internal_notification('Waitlist expired: '.$email);
-
- return 'Your confirmation code has expired. Please sign up again.';
- }
- }
- }
-
- return redirect()->route('dashboard');
- } catch (Exception $e) {
- send_internal_notification('Waitlist confirmation failed: '.$e->getMessage());
-
- return redirect()->route('dashboard');
- }
- }
-
- public function cancel(Request $request)
- {
- $email = request()->get('email');
- $confirmation_code = request()->get('confirmation_code');
- try {
- $found = ModelsWaitlist::where('uuid', $confirmation_code)->where('email', $email)->first();
- if ($found && ! $found->verified) {
- $found->delete();
- send_internal_notification('Waitlist cancelled: '.$email);
-
- return 'Your email address has been removed from the waitlist.';
- }
-
- return redirect()->route('dashboard');
- } catch (Exception $e) {
- send_internal_notification('Waitlist cancellation failed: '.$e->getMessage());
-
- return redirect()->route('dashboard');
- }
- }
-}
diff --git a/app/Jobs/ApplicationDeploymentJob.php b/app/Jobs/ApplicationDeploymentJob.php
index 04e71c4e3..6b677fa0e 100644
--- a/app/Jobs/ApplicationDeploymentJob.php
+++ b/app/Jobs/ApplicationDeploymentJob.php
@@ -2409,7 +2409,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
if (! $this->only_this_server) {
$this->deploy_to_additional_destinations();
}
- //$this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
+ $this->application->environment->project->team?->notify(new DeploymentSuccess($this->application, $this->deployment_uuid, $this->preview));
}
}
diff --git a/app/Jobs/CheckResaleLicenseJob.php b/app/Jobs/CheckResaleLicenseJob.php
deleted file mode 100644
index 7479867b6..000000000
--- a/app/Jobs/CheckResaleLicenseJob.php
+++ /dev/null
@@ -1,28 +0,0 @@
-getMessage());
- throw $e;
- }
- }
-}
diff --git a/app/Jobs/DatabaseBackupJob.php b/app/Jobs/DatabaseBackupJob.php
index ee702202f..06aec5e49 100644
--- a/app/Jobs/DatabaseBackupJob.php
+++ b/app/Jobs/DatabaseBackupJob.php
@@ -306,7 +306,9 @@ class DatabaseBackupJob implements ShouldBeEncrypted, ShouldQueue
if ($this->backup->save_s3) {
$this->upload_to_s3();
}
- //$this->team?->notify(new BackupSuccess($this->backup, $this->database, $database));
+
+ $this->team->notify(new BackupSuccess($this->backup, $this->database, $database));
+
$this->backup_log->update([
'status' => 'success',
'message' => $this->backup_output,
diff --git a/app/Jobs/DockerCleanupJob.php b/app/Jobs/DockerCleanupJob.php
index 80542e03b..103c137b9 100644
--- a/app/Jobs/DockerCleanupJob.php
+++ b/app/Jobs/DockerCleanupJob.php
@@ -4,7 +4,8 @@ namespace App\Jobs;
use App\Actions\Server\CleanupDocker;
use App\Models\Server;
-use App\Notifications\Server\DockerCleanup;
+use App\Notifications\Server\DockerCleanupFailed;
+use App\Notifications\Server\DockerCleanupSuccess;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
use Illuminate\Contracts\Queue\ShouldQueue;
@@ -12,7 +13,6 @@ use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
-use Illuminate\Support\Facades\Log;
class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
{
@@ -38,35 +38,36 @@ class DockerCleanupJob implements ShouldBeEncrypted, ShouldQueue
return;
}
- if ($this->manualCleanup || $this->server->settings->force_docker_cleanup) {
- Log::info('DockerCleanupJob '.($this->manualCleanup ? 'manual' : 'force').' cleanup on '.$this->server->name);
- CleanupDocker::run(server: $this->server);
-
- return;
- }
-
$this->usageBefore = $this->server->getDiskUsage();
- if (str($this->usageBefore)->isEmpty() || $this->usageBefore === null || $this->usageBefore === 0) {
- Log::info('DockerCleanupJob force cleanup on '.$this->server->name);
+
+ if ($this->manualCleanup || $this->server->settings->force_docker_cleanup) {
CleanupDocker::run(server: $this->server);
+ $usageAfter = $this->server->getDiskUsage();
+ $this->server->team?->notify(new DockerCleanupSuccess($this->server, ($this->manualCleanup ? 'Manual' : 'Forced').' Docker cleanup job executed successfully. Disk usage before: '.$this->usageBefore.'%, Disk usage after: '.$usageAfter.'%.'));
return;
}
+
+ if (str($this->usageBefore)->isEmpty() || $this->usageBefore === null || $this->usageBefore === 0) {
+ CleanupDocker::run(server: $this->server);
+ $this->server->team?->notify(new DockerCleanupSuccess($this->server, 'Docker cleanup job executed successfully, but no disk usage could be determined.'));
+ }
+
if ($this->usageBefore >= $this->server->settings->docker_cleanup_threshold) {
CleanupDocker::run(server: $this->server);
$usageAfter = $this->server->getDiskUsage();
- if ($usageAfter < $this->usageBefore) {
- $this->server->team?->notify(new DockerCleanup($this->server, 'Saved '.($this->usageBefore - $usageAfter).'% disk space.'));
- Log::info('DockerCleanupJob done: Saved '.($this->usageBefore - $usageAfter).'% disk space on '.$this->server->name);
+ $diskSaved = $this->usageBefore - $usageAfter;
+
+ if ($diskSaved > 0) {
+ $this->server->team?->notify(new DockerCleanupSuccess($this->server, 'Saved '.$diskSaved.'% disk space. Disk usage before: '.$this->usageBefore.'%, Disk usage after: '.$usageAfter.'%.'));
} else {
- Log::info('DockerCleanupJob failed to save disk space on '.$this->server->name);
+ $this->server->team?->notify(new DockerCleanupSuccess($this->server, 'Docker cleanup job executed successfully, but no disk space was saved. Disk usage before: '.$this->usageBefore.'%, Disk usage after: '.$usageAfter.'%.'));
}
} else {
- Log::info('No need to clean up '.$this->server->name);
+ $this->server->team?->notify(new DockerCleanupSuccess($this->server, 'No cleanup needed for '.$this->server->name));
}
} catch (\Throwable $e) {
- CleanupDocker::run(server: $this->server);
- Log::error('DockerCleanupJob failed: '.$e->getMessage());
+ $this->server->team?->notify(new DockerCleanupFailed($this->server, 'Docker cleanup job failed with the following error: '.$e->getMessage()));
throw $e;
}
}
diff --git a/app/Jobs/ScheduledTaskJob.php b/app/Jobs/ScheduledTaskJob.php
index 00575e187..90a10f3e9 100644
--- a/app/Jobs/ScheduledTaskJob.php
+++ b/app/Jobs/ScheduledTaskJob.php
@@ -10,6 +10,7 @@ use App\Models\Server;
use App\Models\Service;
use App\Models\Team;
use App\Notifications\ScheduledTask\TaskFailed;
+use App\Notifications\ScheduledTask\TaskSuccess;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
@@ -111,6 +112,8 @@ class ScheduledTaskJob implements ShouldQueue
'message' => $this->task_output,
]);
+ $this->team?->notify(new TaskSuccess($this->task, $this->task_output));
+
return;
}
}
@@ -125,7 +128,6 @@ class ScheduledTaskJob implements ShouldQueue
]);
}
$this->team?->notify(new TaskFailed($this->task, $e->getMessage()));
- // send_internal_notification('ScheduledTaskJob failed with: ' . $e->getMessage());
throw $e;
} finally {
ScheduledTaskDone::dispatch($this->team->id);
diff --git a/app/Jobs/SendConfirmationForWaitlistJob.php b/app/Jobs/SendConfirmationForWaitlistJob.php
deleted file mode 100755
index 7af8205fc..000000000
--- a/app/Jobs/SendConfirmationForWaitlistJob.php
+++ /dev/null
@@ -1,37 +0,0 @@
-email.'&confirmation_code='.$this->uuid;
- $cancel_url = base_url().'/webhooks/waitlist/cancel?email='.$this->email.'&confirmation_code='.$this->uuid;
- $mail->view('emails.waitlist-confirmation',
- [
- 'confirmation_url' => $confirmation_url,
- 'cancel_url' => $cancel_url,
- ]);
- $mail->subject('You are on the waitlist!');
- send_user_an_email($mail, $this->email);
- } catch (\Throwable $e) {
- send_internal_notification("SendConfirmationForWaitlistJob failed for {$this->email} with error: ".$e->getMessage());
- throw $e;
- }
- }
-}
diff --git a/app/Livewire/Notifications/Discord.php b/app/Livewire/Notifications/Discord.php
index 7a177a227..57007813e 100644
--- a/app/Livewire/Notifications/Discord.php
+++ b/app/Livewire/Notifications/Discord.php
@@ -2,6 +2,7 @@
namespace App\Livewire\Notifications;
+use App\Models\DiscordNotificationSettings;
use App\Models\Team;
use App\Notifications\Test;
use Livewire\Attributes\Validate;
@@ -11,6 +12,8 @@ class Discord extends Component
{
public Team $team;
+ public DiscordNotificationSettings $settings;
+
#[Validate(['boolean'])]
public bool $discordEnabled = false;
@@ -18,27 +21,46 @@ class Discord extends Component
public ?string $discordWebhookUrl = null;
#[Validate(['boolean'])]
- public bool $discordNotificationsTest = false;
+ public bool $deploymentSuccessDiscordNotifications = false;
#[Validate(['boolean'])]
- public bool $discordNotificationsDeployments = false;
+ public bool $deploymentFailureDiscordNotifications = true;
#[Validate(['boolean'])]
- public bool $discordNotificationsStatusChanges = false;
+ public bool $statusChangeDiscordNotifications = false;
#[Validate(['boolean'])]
- public bool $discordNotificationsDatabaseBackups = false;
+ public bool $backupSuccessDiscordNotifications = false;
#[Validate(['boolean'])]
- public bool $discordNotificationsScheduledTasks = false;
+ public bool $backupFailureDiscordNotifications = true;
#[Validate(['boolean'])]
- public bool $discordNotificationsServerDiskUsage = false;
+ public bool $scheduledTaskSuccessDiscordNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $scheduledTaskFailureDiscordNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $dockerCleanupSuccessDiscordNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $dockerCleanupFailureDiscordNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $serverDiskUsageDiscordNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $serverReachableDiscordNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $serverUnreachableDiscordNotifications = true;
public function mount()
{
try {
$this->team = auth()->user()->currentTeam();
+ $this->settings = $this->team->discordNotificationSettings;
$this->syncData();
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -49,25 +71,40 @@ class Discord extends Component
{
if ($toModel) {
$this->validate();
- $this->team->discord_enabled = $this->discordEnabled;
- $this->team->discord_webhook_url = $this->discordWebhookUrl;
- $this->team->discord_notifications_test = $this->discordNotificationsTest;
- $this->team->discord_notifications_deployments = $this->discordNotificationsDeployments;
- $this->team->discord_notifications_status_changes = $this->discordNotificationsStatusChanges;
- $this->team->discord_notifications_database_backups = $this->discordNotificationsDatabaseBackups;
- $this->team->discord_notifications_scheduled_tasks = $this->discordNotificationsScheduledTasks;
- $this->team->discord_notifications_server_disk_usage = $this->discordNotificationsServerDiskUsage;
- $this->team->save();
+ $this->settings->discord_enabled = $this->discordEnabled;
+ $this->settings->discord_webhook_url = $this->discordWebhookUrl;
+
+ $this->settings->deployment_success_discord_notifications = $this->deploymentSuccessDiscordNotifications;
+ $this->settings->deployment_failure_discord_notifications = $this->deploymentFailureDiscordNotifications;
+ $this->settings->status_change_discord_notifications = $this->statusChangeDiscordNotifications;
+ $this->settings->backup_success_discord_notifications = $this->backupSuccessDiscordNotifications;
+ $this->settings->backup_failure_discord_notifications = $this->backupFailureDiscordNotifications;
+ $this->settings->scheduled_task_success_discord_notifications = $this->scheduledTaskSuccessDiscordNotifications;
+ $this->settings->scheduled_task_failure_discord_notifications = $this->scheduledTaskFailureDiscordNotifications;
+ $this->settings->docker_cleanup_success_discord_notifications = $this->dockerCleanupSuccessDiscordNotifications;
+ $this->settings->docker_cleanup_failure_discord_notifications = $this->dockerCleanupFailureDiscordNotifications;
+ $this->settings->server_disk_usage_discord_notifications = $this->serverDiskUsageDiscordNotifications;
+ $this->settings->server_reachable_discord_notifications = $this->serverReachableDiscordNotifications;
+ $this->settings->server_unreachable_discord_notifications = $this->serverUnreachableDiscordNotifications;
+
+ $this->settings->save();
refreshSession();
} else {
- $this->discordEnabled = $this->team->discord_enabled;
- $this->discordWebhookUrl = $this->team->discord_webhook_url;
- $this->discordNotificationsTest = $this->team->discord_notifications_test;
- $this->discordNotificationsDeployments = $this->team->discord_notifications_deployments;
- $this->discordNotificationsStatusChanges = $this->team->discord_notifications_status_changes;
- $this->discordNotificationsDatabaseBackups = $this->team->discord_notifications_database_backups;
- $this->discordNotificationsScheduledTasks = $this->team->discord_notifications_scheduled_tasks;
- $this->discordNotificationsServerDiskUsage = $this->team->discord_notifications_server_disk_usage;
+ $this->discordEnabled = $this->settings->discord_enabled;
+ $this->discordWebhookUrl = $this->settings->discord_webhook_url;
+
+ $this->deploymentSuccessDiscordNotifications = $this->settings->deployment_success_discord_notifications;
+ $this->deploymentFailureDiscordNotifications = $this->settings->deployment_failure_discord_notifications;
+ $this->statusChangeDiscordNotifications = $this->settings->status_change_discord_notifications;
+ $this->backupSuccessDiscordNotifications = $this->settings->backup_success_discord_notifications;
+ $this->backupFailureDiscordNotifications = $this->settings->backup_failure_discord_notifications;
+ $this->scheduledTaskSuccessDiscordNotifications = $this->settings->scheduled_task_success_discord_notifications;
+ $this->scheduledTaskFailureDiscordNotifications = $this->settings->scheduled_task_failure_discord_notifications;
+ $this->dockerCleanupSuccessDiscordNotifications = $this->settings->docker_cleanup_success_discord_notifications;
+ $this->dockerCleanupFailureDiscordNotifications = $this->settings->docker_cleanup_failure_discord_notifications;
+ $this->serverDiskUsageDiscordNotifications = $this->settings->server_disk_usage_discord_notifications;
+ $this->serverReachableDiscordNotifications = $this->settings->server_reachable_discord_notifications;
+ $this->serverUnreachableDiscordNotifications = $this->settings->server_unreachable_discord_notifications;
}
}
@@ -117,7 +154,7 @@ class Discord extends Component
public function sendTestNotification()
{
try {
- $this->team->notify(new Test);
+ $this->team->notify(new Test(channel: 'discord'));
$this->dispatch('success', 'Test notification sent.');
} catch (\Throwable $e) {
return handleError($e, $this);
diff --git a/app/Livewire/Notifications/Email.php b/app/Livewire/Notifications/Email.php
index ab3768643..dc2a95e84 100644
--- a/app/Livewire/Notifications/Email.php
+++ b/app/Livewire/Notifications/Email.php
@@ -2,6 +2,7 @@
namespace App\Livewire\Notifications;
+use App\Models\EmailNotificationSettings;
use App\Models\Team;
use App\Notifications\Test;
use Illuminate\Support\Facades\RateLimiter;
@@ -11,17 +12,18 @@ use Livewire\Component;
class Email extends Component
{
+ protected $listeners = ['refresh' => '$refresh'];
+
public Team $team;
+ public EmailNotificationSettings $settings;
+
#[Locked]
public string $emails;
#[Validate(['boolean'])]
public bool $smtpEnabled = false;
- #[Validate(['boolean'])]
- public bool $useInstanceEmailSettings = false;
-
#[Validate(['nullable', 'email'])]
public ?string $smtpFromAddress = null;
@@ -34,11 +36,11 @@ class Email extends Component
#[Validate(['nullable', 'string'])]
public ?string $smtpHost = null;
- #[Validate(['nullable', 'numeric'])]
+ #[Validate(['nullable', 'numeric', 'min:1', 'max:65535'])]
public ?int $smtpPort = null;
#[Validate(['nullable', 'string', 'in:tls,ssl,none'])]
- public ?string $smtpEncryption = null;
+ public ?string $smtpEncryption = 'tls';
#[Validate(['nullable', 'string'])]
public ?string $smtpUsername = null;
@@ -50,29 +52,50 @@ class Email extends Component
public ?int $smtpTimeout = null;
#[Validate(['boolean'])]
- public bool $smtpNotificationsTest = false;
-
- #[Validate(['boolean'])]
- public bool $smtpNotificationsDeployments = false;
-
- #[Validate(['boolean'])]
- public bool $smtpNotificationsStatusChanges = false;
-
- #[Validate(['boolean'])]
- public bool $smtpNotificationsDatabaseBackups = false;
-
- #[Validate(['boolean'])]
- public bool $smtpNotificationsScheduledTasks = false;
-
- #[Validate(['boolean'])]
- public bool $smtpNotificationsServerDiskUsage = false;
-
- #[Validate(['boolean'])]
- public bool $resendEnabled;
+ public bool $resendEnabled = false;
#[Validate(['nullable', 'string'])]
public ?string $resendApiKey = null;
+ #[Validate(['boolean'])]
+ public bool $useInstanceEmailSettings = false;
+
+ #[Validate(['boolean'])]
+ public bool $deploymentSuccessEmailNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $deploymentFailureEmailNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $statusChangeEmailNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $backupSuccessEmailNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $backupFailureEmailNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $scheduledTaskSuccessEmailNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $scheduledTaskFailureEmailNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $dockerCleanupSuccessEmailNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $dockerCleanupFailureEmailNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $serverDiskUsageEmailNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $serverReachableEmailNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $serverUnreachableEmailNotifications = true;
+
#[Validate(['nullable', 'email'])]
public ?string $testEmailAddress = null;
@@ -81,7 +104,9 @@ class Email extends Component
try {
$this->team = auth()->user()->currentTeam();
$this->emails = auth()->user()->email;
+ $this->settings = $this->team->emailNotificationSettings;
$this->syncData();
+ $this->testEmailAddress = auth()->user()->email;
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -91,47 +116,191 @@ class Email extends Component
{
if ($toModel) {
$this->validate();
- $this->team->smtp_enabled = $this->smtpEnabled;
- $this->team->smtp_from_address = $this->smtpFromAddress;
- $this->team->smtp_from_name = $this->smtpFromName;
- $this->team->smtp_host = $this->smtpHost;
- $this->team->smtp_port = $this->smtpPort;
- $this->team->smtp_encryption = $this->smtpEncryption;
- $this->team->smtp_username = $this->smtpUsername;
- $this->team->smtp_password = $this->smtpPassword;
- $this->team->smtp_timeout = $this->smtpTimeout;
- $this->team->smtp_recipients = $this->smtpRecipients;
- $this->team->smtp_notifications_test = $this->smtpNotificationsTest;
- $this->team->smtp_notifications_deployments = $this->smtpNotificationsDeployments;
- $this->team->smtp_notifications_status_changes = $this->smtpNotificationsStatusChanges;
- $this->team->smtp_notifications_database_backups = $this->smtpNotificationsDatabaseBackups;
- $this->team->smtp_notifications_scheduled_tasks = $this->smtpNotificationsScheduledTasks;
- $this->team->smtp_notifications_server_disk_usage = $this->smtpNotificationsServerDiskUsage;
- $this->team->use_instance_email_settings = $this->useInstanceEmailSettings;
- $this->team->resend_enabled = $this->resendEnabled;
- $this->team->resend_api_key = $this->resendApiKey;
- $this->team->save();
- refreshSession();
+ $this->settings->smtp_enabled = $this->smtpEnabled;
+ $this->settings->smtp_from_address = $this->smtpFromAddress;
+ $this->settings->smtp_from_name = $this->smtpFromName;
+ $this->settings->smtp_recipients = $this->smtpRecipients;
+ $this->settings->smtp_host = $this->smtpHost;
+ $this->settings->smtp_port = $this->smtpPort;
+ $this->settings->smtp_encryption = $this->smtpEncryption;
+ $this->settings->smtp_username = $this->smtpUsername;
+ $this->settings->smtp_password = $this->smtpPassword;
+ $this->settings->smtp_timeout = $this->smtpTimeout;
+
+ $this->settings->resend_enabled = $this->resendEnabled;
+ $this->settings->resend_api_key = $this->resendApiKey;
+
+ $this->settings->use_instance_email_settings = $this->useInstanceEmailSettings;
+
+ $this->settings->deployment_success_email_notifications = $this->deploymentSuccessEmailNotifications;
+ $this->settings->deployment_failure_email_notifications = $this->deploymentFailureEmailNotifications;
+ $this->settings->status_change_email_notifications = $this->statusChangeEmailNotifications;
+ $this->settings->backup_success_email_notifications = $this->backupSuccessEmailNotifications;
+ $this->settings->backup_failure_email_notifications = $this->backupFailureEmailNotifications;
+ $this->settings->scheduled_task_success_email_notifications = $this->scheduledTaskSuccessEmailNotifications;
+ $this->settings->scheduled_task_failure_email_notifications = $this->scheduledTaskFailureEmailNotifications;
+ $this->settings->docker_cleanup_success_email_notifications = $this->dockerCleanupSuccessEmailNotifications;
+ $this->settings->docker_cleanup_failure_email_notifications = $this->dockerCleanupFailureEmailNotifications;
+ $this->settings->server_disk_usage_email_notifications = $this->serverDiskUsageEmailNotifications;
+ $this->settings->server_reachable_email_notifications = $this->serverReachableEmailNotifications;
+ $this->settings->server_unreachable_email_notifications = $this->serverUnreachableEmailNotifications;
+ $this->settings->save();
+
} else {
- $this->smtpEnabled = $this->team->smtp_enabled;
- $this->smtpFromAddress = $this->team->smtp_from_address;
- $this->smtpFromName = $this->team->smtp_from_name;
- $this->smtpHost = $this->team->smtp_host;
- $this->smtpPort = $this->team->smtp_port;
- $this->smtpEncryption = $this->team->smtp_encryption;
- $this->smtpUsername = $this->team->smtp_username;
- $this->smtpPassword = $this->team->smtp_password;
- $this->smtpTimeout = $this->team->smtp_timeout;
- $this->smtpRecipients = $this->team->smtp_recipients;
- $this->smtpNotificationsTest = $this->team->smtp_notifications_test;
- $this->smtpNotificationsDeployments = $this->team->smtp_notifications_deployments;
- $this->smtpNotificationsStatusChanges = $this->team->smtp_notifications_status_changes;
- $this->smtpNotificationsDatabaseBackups = $this->team->smtp_notifications_database_backups;
- $this->smtpNotificationsScheduledTasks = $this->team->smtp_notifications_scheduled_tasks;
- $this->smtpNotificationsServerDiskUsage = $this->team->smtp_notifications_server_disk_usage;
- $this->useInstanceEmailSettings = $this->team->use_instance_email_settings;
- $this->resendEnabled = $this->team->resend_enabled;
- $this->resendApiKey = $this->team->resend_api_key;
+ $this->smtpEnabled = $this->settings->smtp_enabled;
+ $this->smtpFromAddress = $this->settings->smtp_from_address;
+ $this->smtpFromName = $this->settings->smtp_from_name;
+ $this->smtpRecipients = $this->settings->smtp_recipients;
+ $this->smtpHost = $this->settings->smtp_host;
+ $this->smtpPort = $this->settings->smtp_port;
+ $this->smtpEncryption = $this->settings->smtp_encryption;
+ $this->smtpUsername = $this->settings->smtp_username;
+ $this->smtpPassword = $this->settings->smtp_password;
+ $this->smtpTimeout = $this->settings->smtp_timeout;
+
+ $this->resendEnabled = $this->settings->resend_enabled;
+ $this->resendApiKey = $this->settings->resend_api_key;
+
+ $this->useInstanceEmailSettings = $this->settings->use_instance_email_settings;
+
+ $this->deploymentSuccessEmailNotifications = $this->settings->deployment_success_email_notifications;
+ $this->deploymentFailureEmailNotifications = $this->settings->deployment_failure_email_notifications;
+ $this->statusChangeEmailNotifications = $this->settings->status_change_email_notifications;
+ $this->backupSuccessEmailNotifications = $this->settings->backup_success_email_notifications;
+ $this->backupFailureEmailNotifications = $this->settings->backup_failure_email_notifications;
+ $this->scheduledTaskSuccessEmailNotifications = $this->settings->scheduled_task_success_email_notifications;
+ $this->scheduledTaskFailureEmailNotifications = $this->settings->scheduled_task_failure_email_notifications;
+ $this->dockerCleanupSuccessEmailNotifications = $this->settings->docker_cleanup_success_email_notifications;
+ $this->dockerCleanupFailureEmailNotifications = $this->settings->docker_cleanup_failure_email_notifications;
+ $this->serverDiskUsageEmailNotifications = $this->settings->server_disk_usage_email_notifications;
+ $this->serverReachableEmailNotifications = $this->settings->server_reachable_email_notifications;
+ $this->serverUnreachableEmailNotifications = $this->settings->server_unreachable_email_notifications;
+ }
+ }
+
+ public function submit()
+ {
+ try {
+ $this->resetErrorBag();
+ $this->saveModel();
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
+ }
+ }
+
+ public function saveModel()
+ {
+ $this->syncData(true);
+ $this->dispatch('success', 'Email notifications settings updated.');
+ }
+
+ public function instantSave(?string $type = null)
+ {
+ try {
+ $this->resetErrorBag();
+
+ if ($type === 'SMTP') {
+ $this->submitSmtp();
+ } elseif ($type === 'Resend') {
+ $this->submitResend();
+ } else {
+ $this->smtpEnabled = false;
+ $this->resendEnabled = false;
+ $this->saveModel();
+
+ return;
+ }
+ } catch (\Throwable $e) {
+ if ($type === 'SMTP') {
+ $this->smtpEnabled = false;
+ } elseif ($type === 'Resend') {
+ $this->resendEnabled = false;
+ }
+
+ return handleError($e, $this);
+ } finally {
+ $this->dispatch('refresh');
+ }
+ }
+
+ public function submitSmtp()
+ {
+ try {
+ $this->resetErrorBag();
+ $this->validate([
+ 'smtpEnabled' => 'boolean',
+ 'smtpFromAddress' => 'required|email',
+ 'smtpFromName' => 'required|string',
+ 'smtpHost' => 'required|string',
+ 'smtpPort' => 'required|numeric',
+ 'smtpEncryption' => 'required|string|in:tls,ssl,none',
+ 'smtpUsername' => 'nullable|string',
+ 'smtpPassword' => 'nullable|string',
+ 'smtpTimeout' => 'nullable|numeric',
+ ], [
+ 'smtpFromAddress.required' => 'From Address is required.',
+ 'smtpFromAddress.email' => 'Please enter a valid email address.',
+ 'smtpFromName.required' => 'From Name is required.',
+ 'smtpHost.required' => 'SMTP Host is required.',
+ 'smtpPort.required' => 'SMTP Port is required.',
+ 'smtpPort.numeric' => 'SMTP Port must be a number.',
+ 'smtpEncryption.required' => 'Encryption type is required.',
+ ]);
+
+ $this->settings->resend_enabled = false;
+ $this->settings->use_instance_email_settings = false;
+ $this->resendEnabled = false;
+ $this->useInstanceEmailSettings = false;
+
+ $this->settings->smtp_enabled = $this->smtpEnabled;
+ $this->settings->smtp_from_address = $this->smtpFromAddress;
+ $this->settings->smtp_from_name = $this->smtpFromName;
+ $this->settings->smtp_host = $this->smtpHost;
+ $this->settings->smtp_port = $this->smtpPort;
+ $this->settings->smtp_encryption = $this->smtpEncryption;
+ $this->settings->smtp_username = $this->smtpUsername;
+ $this->settings->smtp_password = $this->smtpPassword;
+ $this->settings->smtp_timeout = $this->smtpTimeout;
+
+ $this->settings->save();
+ $this->dispatch('success', 'SMTP settings updated.');
+ } catch (\Throwable $e) {
+ $this->smtpEnabled = false;
+
+ return handleError($e);
+ }
+ }
+
+ public function submitResend()
+ {
+ try {
+ $this->resetErrorBag();
+ $this->validate([
+ 'resendEnabled' => 'boolean',
+ 'resendApiKey' => 'required|string',
+ 'smtpFromAddress' => 'required|email',
+ 'smtpFromName' => 'required|string',
+ ], [
+ 'resendApiKey.required' => 'Resend API Key is required.',
+ 'smtpFromAddress.required' => 'From Address is required.',
+ 'smtpFromAddress.email' => 'Please enter a valid email address.',
+ 'smtpFromName.required' => 'From Name is required.',
+ ]);
+
+ $this->settings->smtp_enabled = false;
+ $this->settings->use_instance_email_settings = false;
+ $this->smtpEnabled = false;
+ $this->useInstanceEmailSettings = false;
+
+ $this->settings->resend_enabled = $this->resendEnabled;
+ $this->settings->resend_api_key = $this->resendApiKey;
+ $this->settings->smtp_from_address = $this->smtpFromAddress;
+ $this->settings->smtp_from_name = $this->smtpFromName;
+
+ $this->settings->save();
+ $this->dispatch('success', 'Resend settings updated.');
+ } catch (\Throwable $e) {
+ return handleError($e, $this);
}
}
@@ -149,7 +318,7 @@ class Email extends Component
'test-email:'.$this->team->id,
$perMinute = 0,
function () {
- $this->team?->notify(new Test($this->testEmailAddress));
+ $this->team?->notify(new Test($this->testEmailAddress, 'email'));
$this->dispatch('success', 'Test Email sent.');
},
$decaySeconds = 10,
@@ -163,70 +332,6 @@ class Email extends Component
}
}
- public function instantSaveInstance()
- {
- try {
- $this->smtpEnabled = false;
- $this->resendEnabled = false;
- $this->saveModel();
- } catch (\Throwable $e) {
- return handleError($e, $this);
- }
- }
-
- public function instantSaveSmtpEnabled()
- {
- try {
- $this->validate([
- 'smtpHost' => 'required',
- 'smtpPort' => 'required|numeric',
- ], [
- 'smtpHost.required' => 'SMTP Host is required.',
- 'smtpPort.required' => 'SMTP Port is required.',
- ]);
- $this->resendEnabled = false;
- $this->saveModel();
- } catch (\Throwable $e) {
- $this->smtpEnabled = false;
-
- return handleError($e, $this);
- }
- }
-
- public function instantSaveResend()
- {
- try {
- $this->validate([
- 'resendApiKey' => 'required',
- ], [
- 'resendApiKey.required' => 'Resend API Key is required.',
- ]);
- $this->smtpEnabled = false;
- $this->saveModel();
- } catch (\Throwable $e) {
- $this->resendEnabled = false;
-
- return handleError($e, $this);
- }
- }
-
- public function saveModel()
- {
- $this->syncData(true);
- refreshSession();
- $this->dispatch('success', 'Settings saved.');
- }
-
- public function submit()
- {
- try {
- $this->resetErrorBag();
- $this->saveModel();
- } catch (\Throwable $e) {
- return handleError($e, $this);
- }
- }
-
public function copyFromInstanceSettings()
{
$settings = instanceSettings();
diff --git a/app/Livewire/Notifications/Slack.php b/app/Livewire/Notifications/Slack.php
index 06b7643ea..97464fa1c 100644
--- a/app/Livewire/Notifications/Slack.php
+++ b/app/Livewire/Notifications/Slack.php
@@ -2,6 +2,7 @@
namespace App\Livewire\Notifications;
+use App\Models\SlackNotificationSettings;
use App\Models\Team;
use App\Notifications\Test;
use Livewire\Attributes\Validate;
@@ -11,6 +12,8 @@ class Slack extends Component
{
public Team $team;
+ public SlackNotificationSettings $settings;
+
#[Validate(['boolean'])]
public bool $slackEnabled = false;
@@ -18,27 +21,46 @@ class Slack extends Component
public ?string $slackWebhookUrl = null;
#[Validate(['boolean'])]
- public bool $slackNotificationsTest = false;
+ public bool $deploymentSuccessSlackNotifications = false;
#[Validate(['boolean'])]
- public bool $slackNotificationsDeployments = false;
+ public bool $deploymentFailureSlackNotifications = true;
#[Validate(['boolean'])]
- public bool $slackNotificationsStatusChanges = false;
+ public bool $statusChangeSlackNotifications = false;
#[Validate(['boolean'])]
- public bool $slackNotificationsDatabaseBackups = false;
+ public bool $backupSuccessSlackNotifications = false;
#[Validate(['boolean'])]
- public bool $slackNotificationsScheduledTasks = false;
+ public bool $backupFailureSlackNotifications = true;
#[Validate(['boolean'])]
- public bool $slackNotificationsServerDiskUsage = false;
+ public bool $scheduledTaskSuccessSlackNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $scheduledTaskFailureSlackNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $dockerCleanupSuccessSlackNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $dockerCleanupFailureSlackNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $serverDiskUsageSlackNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $serverReachableSlackNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $serverUnreachableSlackNotifications = true;
public function mount()
{
try {
$this->team = auth()->user()->currentTeam();
+ $this->settings = $this->team->slackNotificationSettings;
$this->syncData();
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -49,25 +71,40 @@ class Slack extends Component
{
if ($toModel) {
$this->validate();
- $this->team->slack_enabled = $this->slackEnabled;
- $this->team->slack_webhook_url = $this->slackWebhookUrl;
- $this->team->slack_notifications_test = $this->slackNotificationsTest;
- $this->team->slack_notifications_deployments = $this->slackNotificationsDeployments;
- $this->team->slack_notifications_status_changes = $this->slackNotificationsStatusChanges;
- $this->team->slack_notifications_database_backups = $this->slackNotificationsDatabaseBackups;
- $this->team->slack_notifications_scheduled_tasks = $this->slackNotificationsScheduledTasks;
- $this->team->slack_notifications_server_disk_usage = $this->slackNotificationsServerDiskUsage;
- $this->team->save();
+ $this->settings->slack_enabled = $this->slackEnabled;
+ $this->settings->slack_webhook_url = $this->slackWebhookUrl;
+
+ $this->settings->deployment_success_slack_notifications = $this->deploymentSuccessSlackNotifications;
+ $this->settings->deployment_failure_slack_notifications = $this->deploymentFailureSlackNotifications;
+ $this->settings->status_change_slack_notifications = $this->statusChangeSlackNotifications;
+ $this->settings->backup_success_slack_notifications = $this->backupSuccessSlackNotifications;
+ $this->settings->backup_failure_slack_notifications = $this->backupFailureSlackNotifications;
+ $this->settings->scheduled_task_success_slack_notifications = $this->scheduledTaskSuccessSlackNotifications;
+ $this->settings->scheduled_task_failure_slack_notifications = $this->scheduledTaskFailureSlackNotifications;
+ $this->settings->docker_cleanup_success_slack_notifications = $this->dockerCleanupSuccessSlackNotifications;
+ $this->settings->docker_cleanup_failure_slack_notifications = $this->dockerCleanupFailureSlackNotifications;
+ $this->settings->server_disk_usage_slack_notifications = $this->serverDiskUsageSlackNotifications;
+ $this->settings->server_reachable_slack_notifications = $this->serverReachableSlackNotifications;
+ $this->settings->server_unreachable_slack_notifications = $this->serverUnreachableSlackNotifications;
+
+ $this->settings->save();
refreshSession();
} else {
- $this->slackEnabled = $this->team->slack_enabled;
- $this->slackWebhookUrl = $this->team->slack_webhook_url;
- $this->slackNotificationsTest = $this->team->slack_notifications_test;
- $this->slackNotificationsDeployments = $this->team->slack_notifications_deployments;
- $this->slackNotificationsStatusChanges = $this->team->slack_notifications_status_changes;
- $this->slackNotificationsDatabaseBackups = $this->team->slack_notifications_database_backups;
- $this->slackNotificationsScheduledTasks = $this->team->slack_notifications_scheduled_tasks;
- $this->slackNotificationsServerDiskUsage = $this->team->slack_notifications_server_disk_usage;
+ $this->slackEnabled = $this->settings->slack_enabled;
+ $this->slackWebhookUrl = $this->settings->slack_webhook_url;
+
+ $this->deploymentSuccessSlackNotifications = $this->settings->deployment_success_slack_notifications;
+ $this->deploymentFailureSlackNotifications = $this->settings->deployment_failure_slack_notifications;
+ $this->statusChangeSlackNotifications = $this->settings->status_change_slack_notifications;
+ $this->backupSuccessSlackNotifications = $this->settings->backup_success_slack_notifications;
+ $this->backupFailureSlackNotifications = $this->settings->backup_failure_slack_notifications;
+ $this->scheduledTaskSuccessSlackNotifications = $this->settings->scheduled_task_success_slack_notifications;
+ $this->scheduledTaskFailureSlackNotifications = $this->settings->scheduled_task_failure_slack_notifications;
+ $this->dockerCleanupSuccessSlackNotifications = $this->settings->docker_cleanup_success_slack_notifications;
+ $this->dockerCleanupFailureSlackNotifications = $this->settings->docker_cleanup_failure_slack_notifications;
+ $this->serverDiskUsageSlackNotifications = $this->settings->server_disk_usage_slack_notifications;
+ $this->serverReachableSlackNotifications = $this->settings->server_reachable_slack_notifications;
+ $this->serverUnreachableSlackNotifications = $this->settings->server_unreachable_slack_notifications;
}
}
@@ -117,7 +154,7 @@ class Slack extends Component
public function sendTestNotification()
{
try {
- $this->team->notify(new Test);
+ $this->team->notify(new Test(channel: 'slack'));
$this->dispatch('success', 'Test notification sent.');
} catch (\Throwable $e) {
return handleError($e, $this);
diff --git a/app/Livewire/Notifications/Telegram.php b/app/Livewire/Notifications/Telegram.php
index 15ec20577..de2fa9cdc 100644
--- a/app/Livewire/Notifications/Telegram.php
+++ b/app/Livewire/Notifications/Telegram.php
@@ -3,6 +3,7 @@
namespace App\Livewire\Notifications;
use App\Models\Team;
+use App\Models\TelegramNotificationSettings;
use App\Notifications\Test;
use Livewire\Attributes\Validate;
use Livewire\Component;
@@ -11,6 +12,8 @@ class Telegram extends Component
{
public Team $team;
+ public TelegramNotificationSettings $settings;
+
#[Validate(['boolean'])]
public bool $telegramEnabled = false;
@@ -21,42 +24,82 @@ class Telegram extends Component
public ?string $telegramChatId = null;
#[Validate(['boolean'])]
- public bool $telegramNotificationsTest = false;
+ public bool $deploymentSuccessTelegramNotifications = false;
#[Validate(['boolean'])]
- public bool $telegramNotificationsDeployments = false;
+ public bool $deploymentFailureTelegramNotifications = true;
#[Validate(['boolean'])]
- public bool $telegramNotificationsStatusChanges = false;
+ public bool $statusChangeTelegramNotifications = false;
#[Validate(['boolean'])]
- public bool $telegramNotificationsDatabaseBackups = false;
+ public bool $backupSuccessTelegramNotifications = false;
#[Validate(['boolean'])]
- public bool $telegramNotificationsScheduledTasks = false;
-
- #[Validate(['nullable', 'string'])]
- public ?string $telegramNotificationsTestMessageThreadId = null;
-
- #[Validate(['nullable', 'string'])]
- public ?string $telegramNotificationsDeploymentsMessageThreadId = null;
-
- #[Validate(['nullable', 'string'])]
- public ?string $telegramNotificationsStatusChangesMessageThreadId = null;
-
- #[Validate(['nullable', 'string'])]
- public ?string $telegramNotificationsDatabaseBackupsMessageThreadId = null;
-
- #[Validate(['nullable', 'string'])]
- public ?string $telegramNotificationsScheduledTasksThreadId = null;
+ public bool $backupFailureTelegramNotifications = true;
#[Validate(['boolean'])]
- public bool $telegramNotificationsServerDiskUsage = false;
+ public bool $scheduledTaskSuccessTelegramNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $scheduledTaskFailureTelegramNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $dockerCleanupSuccessTelegramNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $dockerCleanupFailureTelegramNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $serverDiskUsageTelegramNotifications = true;
+
+ #[Validate(['boolean'])]
+ public bool $serverReachableTelegramNotifications = false;
+
+ #[Validate(['boolean'])]
+ public bool $serverUnreachableTelegramNotifications = true;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $telegramNotificationsDeploymentSuccessTopicId = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $telegramNotificationsDeploymentFailureTopicId = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $telegramNotificationsStatusChangeTopicId = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $telegramNotificationsBackupSuccessTopicId = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $telegramNotificationsBackupFailureTopicId = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $telegramNotificationsScheduledTaskSuccessTopicId = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $telegramNotificationsScheduledTaskFailureTopicId = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $telegramNotificationsDockerCleanupSuccessTopicId = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $telegramNotificationsDockerCleanupFailureTopicId = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $telegramNotificationsServerDiskUsageTopicId = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $telegramNotificationsServerReachableTopicId = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $telegramNotificationsServerUnreachableTopicId = null;
public function mount()
{
try {
$this->team = auth()->user()->currentTeam();
+ $this->settings = $this->team->telegramNotificationSettings;
$this->syncData();
} catch (\Throwable $e) {
return handleError($e, $this);
@@ -67,39 +110,69 @@ class Telegram extends Component
{
if ($toModel) {
$this->validate();
- $this->team->telegram_enabled = $this->telegramEnabled;
- $this->team->telegram_token = $this->telegramToken;
- $this->team->telegram_chat_id = $this->telegramChatId;
- $this->team->telegram_notifications_test = $this->telegramNotificationsTest;
- $this->team->telegram_notifications_deployments = $this->telegramNotificationsDeployments;
- $this->team->telegram_notifications_status_changes = $this->telegramNotificationsStatusChanges;
- $this->team->telegram_notifications_database_backups = $this->telegramNotificationsDatabaseBackups;
- $this->team->telegram_notifications_scheduled_tasks = $this->telegramNotificationsScheduledTasks;
- $this->team->telegram_notifications_test_message_thread_id = $this->telegramNotificationsTestMessageThreadId;
- $this->team->telegram_notifications_deployments_message_thread_id = $this->telegramNotificationsDeploymentsMessageThreadId;
- $this->team->telegram_notifications_status_changes_message_thread_id = $this->telegramNotificationsStatusChangesMessageThreadId;
- $this->team->telegram_notifications_database_backups_message_thread_id = $this->telegramNotificationsDatabaseBackupsMessageThreadId;
- $this->team->telegram_notifications_scheduled_tasks_thread_id = $this->telegramNotificationsScheduledTasksThreadId;
- $this->team->telegram_notifications_server_disk_usage = $this->telegramNotificationsServerDiskUsage;
- $this->team->save();
+ $this->settings->telegram_enabled = $this->telegramEnabled;
+ $this->settings->telegram_token = $this->telegramToken;
+ $this->settings->telegram_chat_id = $this->telegramChatId;
+
+ $this->settings->deployment_success_telegram_notifications = $this->deploymentSuccessTelegramNotifications;
+ $this->settings->deployment_failure_telegram_notifications = $this->deploymentFailureTelegramNotifications;
+ $this->settings->status_change_telegram_notifications = $this->statusChangeTelegramNotifications;
+ $this->settings->backup_success_telegram_notifications = $this->backupSuccessTelegramNotifications;
+ $this->settings->backup_failure_telegram_notifications = $this->backupFailureTelegramNotifications;
+ $this->settings->scheduled_task_success_telegram_notifications = $this->scheduledTaskSuccessTelegramNotifications;
+ $this->settings->scheduled_task_failure_telegram_notifications = $this->scheduledTaskFailureTelegramNotifications;
+ $this->settings->docker_cleanup_success_telegram_notifications = $this->dockerCleanupSuccessTelegramNotifications;
+ $this->settings->docker_cleanup_failure_telegram_notifications = $this->dockerCleanupFailureTelegramNotifications;
+ $this->settings->server_disk_usage_telegram_notifications = $this->serverDiskUsageTelegramNotifications;
+ $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->save();
refreshSession();
} else {
- $this->telegramEnabled = $this->team->telegram_enabled;
- $this->telegramToken = $this->team->telegram_token;
- $this->telegramChatId = $this->team->telegram_chat_id;
- $this->telegramNotificationsTest = $this->team->telegram_notifications_test;
- $this->telegramNotificationsDeployments = $this->team->telegram_notifications_deployments;
- $this->telegramNotificationsStatusChanges = $this->team->telegram_notifications_status_changes;
- $this->telegramNotificationsDatabaseBackups = $this->team->telegram_notifications_database_backups;
- $this->telegramNotificationsScheduledTasks = $this->team->telegram_notifications_scheduled_tasks;
- $this->telegramNotificationsTestMessageThreadId = $this->team->telegram_notifications_test_message_thread_id;
- $this->telegramNotificationsDeploymentsMessageThreadId = $this->team->telegram_notifications_deployments_message_thread_id;
- $this->telegramNotificationsStatusChangesMessageThreadId = $this->team->telegram_notifications_status_changes_message_thread_id;
- $this->telegramNotificationsDatabaseBackupsMessageThreadId = $this->team->telegram_notifications_database_backups_message_thread_id;
- $this->telegramNotificationsScheduledTasksThreadId = $this->team->telegram_notifications_scheduled_tasks_thread_id;
- $this->telegramNotificationsServerDiskUsage = $this->team->telegram_notifications_server_disk_usage;
- }
+ $this->telegramEnabled = $this->settings->telegram_enabled;
+ $this->telegramToken = $this->settings->telegram_token;
+ $this->telegramChatId = $this->settings->telegram_chat_id;
+ $this->deploymentSuccessTelegramNotifications = $this->settings->deployment_success_telegram_notifications;
+ $this->deploymentFailureTelegramNotifications = $this->settings->deployment_failure_telegram_notifications;
+ $this->statusChangeTelegramNotifications = $this->settings->status_change_telegram_notifications;
+ $this->backupSuccessTelegramNotifications = $this->settings->backup_success_telegram_notifications;
+ $this->backupFailureTelegramNotifications = $this->settings->backup_failure_telegram_notifications;
+ $this->scheduledTaskSuccessTelegramNotifications = $this->settings->scheduled_task_success_telegram_notifications;
+ $this->scheduledTaskFailureTelegramNotifications = $this->settings->scheduled_task_failure_telegram_notifications;
+ $this->dockerCleanupSuccessTelegramNotifications = $this->settings->docker_cleanup_success_telegram_notifications;
+ $this->dockerCleanupFailureTelegramNotifications = $this->settings->docker_cleanup_failure_telegram_notifications;
+ $this->serverDiskUsageTelegramNotifications = $this->settings->server_disk_usage_telegram_notifications;
+ $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;
+ }
}
public function instantSave()
@@ -150,7 +223,7 @@ class Telegram extends Component
public function sendTestNotification()
{
try {
- $this->team->notify(new Test);
+ $this->team->notify(new Test(channel: 'telegram'));
$this->dispatch('success', 'Test notification sent.');
} catch (\Throwable $e) {
return handleError($e, $this);
diff --git a/app/Livewire/Settings/Index.php b/app/Livewire/Settings/Index.php
index c1be35ced..7adb0f8a7 100644
--- a/app/Livewire/Settings/Index.php
+++ b/app/Livewire/Settings/Index.php
@@ -23,9 +23,6 @@ class Index extends Component
#[Validate('nullable|string|max:255')]
public ?string $fqdn = null;
- #[Validate('nullable|string|max:255')]
- public ?string $resale_license = null;
-
#[Validate('required|integer|min:1025|max:65535')]
public int $public_port_min;
@@ -83,7 +80,6 @@ class Index extends Component
} else {
$this->settings = instanceSettings();
$this->fqdn = $this->settings->fqdn;
- $this->resale_license = $this->settings->resale_license;
$this->public_port_min = $this->settings->public_port_min;
$this->public_port_max = $this->settings->public_port_max;
$this->custom_dns_servers = $this->settings->custom_dns_servers;
@@ -122,7 +118,6 @@ class Index extends Component
}
$this->settings->fqdn = $this->fqdn;
- $this->settings->resale_license = $this->resale_license;
$this->settings->public_port_min = $this->public_port_min;
$this->settings->public_port_max = $this->public_port_max;
$this->settings->custom_dns_servers = $this->custom_dns_servers;
diff --git a/app/Livewire/SettingsEmail.php b/app/Livewire/SettingsEmail.php
index abf3a12f9..1c5edb108 100644
--- a/app/Livewire/SettingsEmail.php
+++ b/app/Livewire/SettingsEmail.php
@@ -3,6 +3,10 @@
namespace App\Livewire;
use App\Models\InstanceSettings;
+use App\Models\Team;
+use App\Notifications\Test;
+use Illuminate\Support\Facades\RateLimiter;
+use Livewire\Attributes\Locked;
use Livewire\Attributes\Validate;
use Livewire\Component;
@@ -10,9 +14,21 @@ class SettingsEmail extends Component
{
public InstanceSettings $settings;
+ #[Locked]
+ public Team $team;
+
#[Validate(['boolean'])]
public bool $smtpEnabled = false;
+ #[Validate(['nullable', 'email'])]
+ public ?string $smtpFromAddress = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $smtpFromName = null;
+
+ #[Validate(['nullable', 'string'])]
+ public ?string $smtpRecipients = null;
+
#[Validate(['nullable', 'string'])]
public ?string $smtpHost = null;
@@ -20,29 +36,26 @@ class SettingsEmail extends Component
public ?int $smtpPort = null;
#[Validate(['nullable', 'string', 'in:tls,ssl,none'])]
- public ?string $smtpEncryption = null;
+ public ?string $smtpEncryption = 'tls';
#[Validate(['nullable', 'string'])]
public ?string $smtpUsername = null;
- #[Validate(['nullable'])]
+ #[Validate(['nullable', 'string'])]
public ?string $smtpPassword = null;
#[Validate(['nullable', 'numeric'])]
public ?int $smtpTimeout = null;
- #[Validate(['nullable', 'email'])]
- public ?string $smtpFromAddress = null;
-
- #[Validate(['nullable', 'string'])]
- public ?string $smtpFromName = null;
-
#[Validate(['boolean'])]
public bool $resendEnabled = false;
#[Validate(['nullable', 'string'])]
public ?string $resendApiKey = null;
+ #[Validate(['nullable', 'email'])]
+ public ?string $testEmailAddress = null;
+
public function mount()
{
if (isInstanceAdmin() === false) {
@@ -50,6 +63,8 @@ class SettingsEmail extends Component
}
$this->settings = instanceSettings();
$this->syncData();
+ $this->team = auth()->user()->currentTeam();
+ $this->testEmailAddress = auth()->user()->email;
}
public function syncData(bool $toModel = false)
@@ -90,7 +105,7 @@ class SettingsEmail extends Component
try {
$this->resetErrorBag();
$this->syncData(true);
- $this->dispatch('success', 'Settings saved.');
+ $this->dispatch('success', 'Transactional email settings updated.');
} catch (\Throwable $e) {
return handleError($e, $this);
}
@@ -99,19 +114,129 @@ class SettingsEmail extends Component
public function instantSave(string $type)
{
try {
+ $this->resetErrorBag();
+
if ($type === 'SMTP') {
- $this->resendEnabled = false;
- } else {
- $this->smtpEnabled = false;
- }
- $this->syncData(true);
- if ($this->smtpEnabled || $this->resendEnabled) {
- $this->dispatch('success', "{$type} enabled.");
- } else {
- $this->dispatch('success', "{$type} disabled.");
+ $this->submitSmtp();
+ } elseif ($type === 'Resend') {
+ $this->submitResend();
}
+
} catch (\Throwable $e) {
+ if ($type === 'SMTP') {
+ $this->smtpEnabled = false;
+ } elseif ($type === 'Resend') {
+ $this->resendEnabled = false;
+ }
+
return handleError($e, $this);
}
}
+
+ public function submitSmtp()
+ {
+ try {
+ $this->validate([
+ 'smtpEnabled' => 'boolean',
+ 'smtpFromAddress' => 'required|email',
+ 'smtpFromName' => 'required|string',
+ 'smtpHost' => 'required|string',
+ 'smtpPort' => 'required|numeric',
+ 'smtpEncryption' => 'required|string|in:tls,ssl,none',
+ 'smtpUsername' => 'nullable|string',
+ 'smtpPassword' => 'nullable|string',
+ 'smtpTimeout' => 'nullable|numeric',
+ ], [
+ 'smtpFromAddress.required' => 'From Address is required.',
+ 'smtpFromAddress.email' => 'Please enter a valid email address.',
+ 'smtpFromName.required' => 'From Name is required.',
+ 'smtpHost.required' => 'SMTP Host is required.',
+ 'smtpPort.required' => 'SMTP Port is required.',
+ 'smtpPort.numeric' => 'SMTP Port must be a number.',
+ 'smtpEncryption.required' => 'Encryption type is required.',
+ ]);
+
+ $this->resendEnabled = false;
+ $this->settings->resend_enabled = false;
+
+ $this->settings->smtp_enabled = $this->smtpEnabled;
+ $this->settings->smtp_host = $this->smtpHost;
+ $this->settings->smtp_port = $this->smtpPort;
+ $this->settings->smtp_encryption = $this->smtpEncryption;
+ $this->settings->smtp_username = $this->smtpUsername;
+ $this->settings->smtp_password = $this->smtpPassword;
+ $this->settings->smtp_timeout = $this->smtpTimeout;
+ $this->settings->smtp_from_address = $this->smtpFromAddress;
+ $this->settings->smtp_from_name = $this->smtpFromName;
+
+ $this->settings->save();
+
+ $this->dispatch('success', 'SMTP settings updated.');
+ } catch (\Throwable $e) {
+ $this->smtpEnabled = false;
+
+ return handleError($e);
+ }
+ }
+
+ public function submitResend()
+ {
+ try {
+ $this->validate([
+ 'resendEnabled' => 'boolean',
+ 'resendApiKey' => 'required|string',
+ 'smtpFromAddress' => 'required|email',
+ 'smtpFromName' => 'required|string',
+ ], [
+ 'resendApiKey.required' => 'Resend API Key is required.',
+ 'smtpFromAddress.required' => 'From Address is required.',
+ 'smtpFromAddress.email' => 'Please enter a valid email address.',
+ 'smtpFromName.required' => 'From Name is required.',
+ ]);
+
+ $this->smtpEnabled = false;
+ $this->settings->smtp_enabled = false;
+
+ $this->settings->resend_enabled = $this->resendEnabled;
+ $this->settings->resend_api_key = $this->resendApiKey;
+ $this->settings->smtp_from_address = $this->smtpFromAddress;
+ $this->settings->smtp_from_name = $this->smtpFromName;
+
+ $this->settings->save();
+
+ $this->dispatch('success', 'Resend settings updated.');
+ } catch (\Throwable $e) {
+ $this->resendEnabled = false;
+
+ return handleError($e);
+ }
+ }
+
+ public function sendTestEmail()
+ {
+ try {
+ $this->validate([
+ 'testEmailAddress' => 'required|email',
+ ], [
+ 'testEmailAddress.required' => 'Test email address is required.',
+ 'testEmailAddress.email' => 'Please enter a valid email address.',
+ ]);
+
+ $executed = RateLimiter::attempt(
+ 'test-email:'.$this->team->id,
+ $perMinute = 0,
+ function () {
+ $this->team?->notify(new Test($this->testEmailAddress, 'email'));
+ $this->dispatch('success', 'Test Email sent.');
+ },
+ $decaySeconds = 10,
+ );
+
+ if (! $executed) {
+ throw new \Exception('Too many messages sent!');
+ }
+ } catch (\Throwable $e) {
+ return handleError($e);
+ }
+ }
}
diff --git a/app/Livewire/Waitlist/Index.php b/app/Livewire/Waitlist/Index.php
deleted file mode 100644
index 0524b495c..000000000
--- a/app/Livewire/Waitlist/Index.php
+++ /dev/null
@@ -1,70 +0,0 @@
- 'required|email',
- ];
-
- public function render()
- {
- return view('livewire.waitlist.index')->layout('layouts.simple');
- }
-
- public function mount()
- {
- if (config('constants.waitlist.enabled') == false) {
- return redirect()->route('register');
- }
- $this->waitingInLine = Waitlist::whereVerified(true)->count();
- $this->users = User::count();
- if (isDev()) {
- $this->email = 'waitlist@example.com';
- }
- }
-
- public function submit()
- {
- $this->validate();
- try {
- $already_registered = User::whereEmail($this->email)->first();
- if ($already_registered) {
- throw new \Exception('You are already on the waitlist or registered.
Please check your email to verify your email address or contact support.');
- }
- $found = Waitlist::where('email', $this->email)->first();
- if ($found) {
- if (! $found->verified) {
- $this->dispatch('error', 'You are already on the waitlist.
Please check your email to verify your email address.');
-
- return;
- }
- $this->dispatch('error', 'You are already on the waitlist.
You will be notified when your turn comes.
Thank you.');
-
- return;
- }
- $waitlist = Waitlist::create([
- 'email' => Str::lower($this->email),
- 'type' => 'registration',
- ]);
-
- $this->dispatch('success', 'Check your email to verify your email address.');
- dispatch(new SendConfirmationForWaitlistJob($this->email, $waitlist->uuid));
- } catch (\Throwable $e) {
- return handleError($e, $this);
- }
- }
-}
diff --git a/app/Models/Application.php b/app/Models/Application.php
index d1efd3f33..82ad0c2d1 100644
--- a/app/Models/Application.php
+++ b/app/Models/Application.php
@@ -327,7 +327,7 @@ class Application extends BaseModel
return null;
}
- public function failedTaskLink($task_uuid)
+ public function taskLink($task_uuid)
{
if (data_get($this, 'environment.project.uuid')) {
$route = route('project.application.scheduled-tasks', [
diff --git a/app/Models/DiscordNotificationSettings.php b/app/Models/DiscordNotificationSettings.php
new file mode 100644
index 000000000..619393ddc
--- /dev/null
+++ b/app/Models/DiscordNotificationSettings.php
@@ -0,0 +1,59 @@
+ 'boolean',
+ 'discord_webhook_url' => 'encrypted',
+
+ 'deployment_success_discord_notifications' => 'boolean',
+ 'deployment_failure_discord_notifications' => 'boolean',
+ 'status_change_discord_notifications' => 'boolean',
+ 'backup_success_discord_notifications' => 'boolean',
+ 'backup_failure_discord_notifications' => 'boolean',
+ 'scheduled_task_success_discord_notifications' => 'boolean',
+ 'scheduled_task_failure_discord_notifications' => 'boolean',
+ 'docker_cleanup_discord_notifications' => 'boolean',
+ 'server_disk_usage_discord_notifications' => 'boolean',
+ 'server_reachable_discord_notifications' => 'boolean',
+ 'server_unreachable_discord_notifications' => 'boolean',
+ ];
+
+ public function team()
+ {
+ return $this->belongsTo(Team::class);
+ }
+
+ public function isEnabled()
+ {
+ return $this->discord_enabled;
+ }
+}
diff --git a/app/Models/EmailNotificationSettings.php b/app/Models/EmailNotificationSettings.php
new file mode 100644
index 000000000..ae118986f
--- /dev/null
+++ b/app/Models/EmailNotificationSettings.php
@@ -0,0 +1,79 @@
+ 'boolean',
+ 'smtp_from_address' => 'encrypted',
+ 'smtp_from_name' => 'encrypted',
+ 'smtp_recipients' => 'encrypted',
+ 'smtp_host' => 'encrypted',
+ 'smtp_port' => 'integer',
+ 'smtp_username' => 'encrypted',
+ 'smtp_password' => 'encrypted',
+ 'smtp_timeout' => 'integer',
+
+ 'resend_enabled' => 'boolean',
+ 'resend_api_key' => 'encrypted',
+
+ 'use_instance_email_settings' => 'boolean',
+
+ 'deployment_success_email_notifications' => 'boolean',
+ 'deployment_failure_email_notifications' => 'boolean',
+ 'status_change_email_notifications' => 'boolean',
+ 'backup_success_email_notifications' => 'boolean',
+ 'backup_failure_email_notifications' => 'boolean',
+ 'scheduled_task_success_email_notifications' => 'boolean',
+ 'scheduled_task_failure_email_notifications' => 'boolean',
+ 'server_disk_usage_email_notifications' => 'boolean',
+ ];
+
+ public function team()
+ {
+ return $this->belongsTo(Team::class);
+ }
+
+ public function isEnabled()
+ {
+ if (isCloud()) {
+ return true;
+ }
+
+ return $this->smtp_enabled || $this->resend_enabled || $this->use_instance_email_settings;
+ }
+}
diff --git a/app/Models/InstanceSettings.php b/app/Models/InstanceSettings.php
index eeb803925..5b89bb401 100644
--- a/app/Models/InstanceSettings.php
+++ b/app/Models/InstanceSettings.php
@@ -16,8 +16,19 @@ class InstanceSettings extends Model implements SendsEmail
protected $guarded = [];
protected $casts = [
- 'resale_license' => 'encrypted',
+ 'smtp_enabled' => 'boolean',
+ 'smtp_from_address' => 'encrypted',
+ 'smtp_from_name' => 'encrypted',
+ 'smtp_recipients' => 'encrypted',
+ 'smtp_host' => 'encrypted',
+ 'smtp_port' => 'integer',
+ 'smtp_username' => 'encrypted',
'smtp_password' => 'encrypted',
+ 'smtp_timeout' => 'integer',
+
+ 'resend_enabled' => 'boolean',
+ 'resend_api_key' => 'encrypted',
+
'allowed_ip_ranges' => 'array',
'is_auto_update_enabled' => 'boolean',
'auto_update_frequency' => 'string',
@@ -81,7 +92,7 @@ class InstanceSettings extends Model implements SendsEmail
return InstanceSettings::findOrFail(0);
}
- public function getRecepients($notification)
+ public function getRecipients($notification)
{
$recipients = data_get($notification, 'emails', null);
if (is_null($recipients) || $recipients === '') {
diff --git a/app/Models/S3Storage.php b/app/Models/S3Storage.php
index a432a6e9c..f1247e6f7 100644
--- a/app/Models/S3Storage.php
+++ b/app/Models/S3Storage.php
@@ -59,7 +59,7 @@ class S3Storage extends BaseModel
$this->is_usable = true;
} catch (\Throwable $e) {
$this->is_usable = false;
- if ($this->unusable_email_sent === false && is_transactional_emails_active()) {
+ if ($this->unusable_email_sent === false && is_transactional_emails_enabled()) {
$mail = new MailMessage;
$mail->subject('Coolify: S3 Storage Connection Error');
$mail->view('emails.s3-connection-error', ['name' => $this->name, 'reason' => $e->getMessage(), 'url' => route('storage.show', ['storage_uuid' => $this->uuid])]);
diff --git a/app/Models/Server.php b/app/Models/Server.php
index 6dfb0a4a1..cc8211789 100644
--- a/app/Models/Server.php
+++ b/app/Models/Server.php
@@ -1042,7 +1042,7 @@ $schema://$host {
$this->unreachable_notification_sent = false;
$this->save();
$this->refresh();
- // $this->team->notify(new Reachable($this));
+ $this->team->notify(new Reachable($this));
}
public function sendUnreachableNotification()
@@ -1050,7 +1050,7 @@ $schema://$host {
$this->unreachable_notification_sent = true;
$this->save();
$this->refresh();
- // $this->team->notify(new Unreachable($this));
+ $this->team->notify(new Unreachable($this));
}
public function validateConnection(bool $justCheckingNewKey = false)
diff --git a/app/Models/Service.php b/app/Models/Service.php
index 6d3d2024b..117677d53 100644
--- a/app/Models/Service.php
+++ b/app/Models/Service.php
@@ -1140,7 +1140,7 @@ class Service extends BaseModel
return null;
}
- public function failedTaskLink($task_uuid)
+ public function taskLink($task_uuid)
{
if (data_get($this, 'environment.project.uuid')) {
$route = route('project.service.scheduled-tasks', [
diff --git a/app/Models/SlackNotificationSettings.php b/app/Models/SlackNotificationSettings.php
new file mode 100644
index 000000000..48153f2ea
--- /dev/null
+++ b/app/Models/SlackNotificationSettings.php
@@ -0,0 +1,59 @@
+ 'boolean',
+ 'slack_webhook_url' => 'encrypted',
+
+ 'deployment_success_slack_notifications' => 'boolean',
+ 'deployment_failure_slack_notifications' => 'boolean',
+ 'status_change_slack_notifications' => 'boolean',
+ 'backup_success_slack_notifications' => 'boolean',
+ 'backup_failure_slack_notifications' => 'boolean',
+ 'scheduled_task_success_slack_notifications' => 'boolean',
+ 'scheduled_task_failure_slack_notifications' => 'boolean',
+ 'docker_cleanup_slack_notifications' => 'boolean',
+ 'server_disk_usage_slack_notifications' => 'boolean',
+ 'server_reachable_slack_notifications' => 'boolean',
+ 'server_unreachable_slack_notifications' => 'boolean',
+ ];
+
+ public function team()
+ {
+ return $this->belongsTo(Team::class);
+ }
+
+ public function isEnabled()
+ {
+ return $this->slack_enabled;
+ }
+}
diff --git a/app/Models/Team.php b/app/Models/Team.php
index ecf662787..07424a55f 100644
--- a/app/Models/Team.php
+++ b/app/Models/Team.php
@@ -5,6 +5,7 @@ namespace App\Models;
use App\Notifications\Channels\SendsDiscord;
use App\Notifications\Channels\SendsEmail;
use App\Notifications\Channels\SendsSlack;
+use App\Traits\HasNotificationSettings;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
@@ -20,49 +21,8 @@ use OpenApi\Attributes as OA;
'personal_team' => ['type' => 'boolean', 'description' => 'Whether the team is personal or not.'],
'created_at' => ['type' => 'string', 'description' => 'The date and time the team was created.'],
'updated_at' => ['type' => 'string', 'description' => 'The date and time the team was last updated.'],
- 'smtp_enabled' => ['type' => 'boolean', 'description' => 'Whether SMTP is enabled or not.'],
- 'smtp_from_address' => ['type' => 'string', 'description' => 'The email address to send emails from.'],
- 'smtp_from_name' => ['type' => 'string', 'description' => 'The name to send emails from.'],
- 'smtp_recipients' => ['type' => 'string', 'description' => 'The email addresses to send emails to.'],
- 'smtp_host' => ['type' => 'string', 'description' => 'The SMTP host.'],
- 'smtp_port' => ['type' => 'string', 'description' => 'The SMTP port.'],
- 'smtp_encryption' => ['type' => 'string', 'description' => 'The SMTP encryption.'],
- 'smtp_username' => ['type' => 'string', 'description' => 'The SMTP username.'],
- 'smtp_password' => ['type' => 'string', 'description' => 'The SMTP password.'],
- 'smtp_timeout' => ['type' => 'string', 'description' => 'The SMTP timeout.'],
- 'smtp_notifications_test' => ['type' => 'boolean', 'description' => 'Whether to send test notifications via SMTP.'],
- 'smtp_notifications_deployments' => ['type' => 'boolean', 'description' => 'Whether to send deployment notifications via SMTP.'],
- 'smtp_notifications_status_changes' => ['type' => 'boolean', 'description' => 'Whether to send status change notifications via SMTP.'],
- 'smtp_notifications_scheduled_tasks' => ['type' => 'boolean', 'description' => 'Whether to send scheduled task notifications via SMTP.'],
- 'smtp_notifications_database_backups' => ['type' => 'boolean', 'description' => 'Whether to send database backup notifications via SMTP.'],
- 'smtp_notifications_server_disk_usage' => ['type' => 'boolean', 'description' => 'Whether to send server disk usage notifications via SMTP.'],
- 'discord_enabled' => ['type' => 'boolean', 'description' => 'Whether Discord is enabled or not.'],
- 'discord_webhook_url' => ['type' => 'string', 'description' => 'The Discord webhook URL.'],
- 'discord_notifications_test' => ['type' => 'boolean', 'description' => 'Whether to send test notifications via Discord.'],
- 'discord_notifications_deployments' => ['type' => 'boolean', 'description' => 'Whether to send deployment notifications via Discord.'],
- 'discord_notifications_status_changes' => ['type' => 'boolean', 'description' => 'Whether to send status change notifications via Discord.'],
- 'discord_notifications_database_backups' => ['type' => 'boolean', 'description' => 'Whether to send database backup notifications via Discord.'],
- 'discord_notifications_scheduled_tasks' => ['type' => 'boolean', 'description' => 'Whether to send scheduled task notifications via Discord.'],
- 'discord_notifications_server_disk_usage' => ['type' => 'boolean', 'description' => 'Whether to send server disk usage notifications via Discord.'],
'show_boarding' => ['type' => 'boolean', 'description' => 'Whether to show the boarding screen or not.'],
- 'resend_enabled' => ['type' => 'boolean', 'description' => 'Whether to enable resending or not.'],
- 'resend_api_key' => ['type' => 'string', 'description' => 'The resending API key.'],
- 'use_instance_email_settings' => ['type' => 'boolean', 'description' => 'Whether to use instance email settings or not.'],
- 'telegram_enabled' => ['type' => 'boolean', 'description' => 'Whether Telegram is enabled or not.'],
- 'telegram_token' => ['type' => 'string', 'description' => 'The Telegram token.'],
- 'telegram_chat_id' => ['type' => 'string', 'description' => 'The Telegram chat ID.'],
- 'telegram_notifications_test' => ['type' => 'boolean', 'description' => 'Whether to send test notifications via Telegram.'],
- 'telegram_notifications_deployments' => ['type' => 'boolean', 'description' => 'Whether to send deployment notifications via Telegram.'],
- 'telegram_notifications_status_changes' => ['type' => 'boolean', 'description' => 'Whether to send status change notifications via Telegram.'],
- 'telegram_notifications_database_backups' => ['type' => 'boolean', 'description' => 'Whether to send database backup notifications via Telegram.'],
- 'telegram_notifications_test_message_thread_id' => ['type' => 'string', 'description' => 'The Telegram test message thread ID.'],
- 'telegram_notifications_deployments_message_thread_id' => ['type' => 'string', 'description' => 'The Telegram deployment message thread ID.'],
- 'telegram_notifications_status_changes_message_thread_id' => ['type' => 'string', 'description' => 'The Telegram status change message thread ID.'],
- 'telegram_notifications_database_backups_message_thread_id' => ['type' => 'string', 'description' => 'The Telegram database backup message thread ID.'],
-
'custom_server_limit' => ['type' => 'string', 'description' => 'The custom server limit.'],
- 'telegram_notifications_scheduled_tasks' => ['type' => 'boolean', 'description' => 'Whether to send scheduled task notifications via Telegram.'],
- 'telegram_notifications_scheduled_tasks_thread_id' => ['type' => 'string', 'description' => 'The Telegram scheduled task message thread ID.'],
'members' => new OA\Property(
property: 'members',
type: 'array',
@@ -71,20 +31,26 @@ use OpenApi\Attributes as OA;
),
]
)]
+
class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack
{
- use Notifiable;
+ use HasNotificationSettings, Notifiable;
protected $guarded = [];
protected $casts = [
'personal_team' => 'boolean',
- 'smtp_password' => 'encrypted',
- 'resend_api_key' => 'encrypted',
];
protected static function booted()
{
+ static::created(function ($team) {
+ $team->emailNotificationSettings()->create();
+ $team->discordNotificationSettings()->create();
+ $team->slackNotificationSettings()->create();
+ $team->telegramNotificationSettings()->create();
+ });
+
static::saving(function ($team) {
if (auth()->user()?->isMember()) {
throw new \Exception('You are not allowed to update this team.');
@@ -115,34 +81,6 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack
});
}
- public function routeNotificationForDiscord()
- {
- return data_get($this, 'discord_webhook_url', null);
- }
-
- public function routeNotificationForTelegram()
- {
- return [
- 'token' => data_get($this, 'telegram_token', null),
- 'chat_id' => data_get($this, 'telegram_chat_id', null),
- ];
- }
-
- public function routeNotificationForSlack()
- {
- return data_get($this, 'slack_webhook_url', null);
- }
-
- public function getRecepients($notification)
- {
- $recipients = data_get($notification, 'emails', null);
- if (is_null($recipients)) {
- return $this->members()->pluck('email')->toArray();
- }
-
- return explode(',', $recipients);
- }
-
public static function serverLimitReached()
{
$serverLimit = Team::serverLimit();
@@ -196,10 +134,66 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack
return $serverLimit ?? 2;
}
-
);
}
+ public function routeNotificationForDiscord()
+ {
+ return data_get($this, 'discord_webhook_url', null);
+ }
+
+ public function routeNotificationForTelegram()
+ {
+ return [
+ 'token' => data_get($this, 'telegram_token', null),
+ 'chat_id' => data_get($this, 'telegram_chat_id', null),
+ ];
+ }
+
+ public function routeNotificationForSlack()
+ {
+ return data_get($this, 'slack_webhook_url', null);
+ }
+
+ public function getRecipients($notification)
+ {
+ $recipients = data_get($notification, 'emails', null);
+ if (is_null($recipients)) {
+ return $this->members()->pluck('email')->toArray();
+ }
+
+ return explode(',', $recipients);
+ }
+
+ public function isAnyNotificationEnabled()
+ {
+ if (isCloud()) {
+ return true;
+ }
+
+ return $this->getNotificationSettings('email')?->isEnabled() ||
+ $this->getNotificationSettings('discord')?->isEnabled() ||
+ $this->getNotificationSettings('slack')?->isEnabled() ||
+ $this->getNotificationSettings('telegram')?->isEnabled();
+ }
+
+ public function subscriptionEnded()
+ {
+ $this->subscription->update([
+ 'stripe_subscription_id' => null,
+ 'stripe_plan_id' => null,
+ 'stripe_cancel_at_period_end' => false,
+ 'stripe_invoice_paid' => false,
+ 'stripe_trial_already_ended' => false,
+ ]);
+ foreach ($this->servers as $server) {
+ $server->settings()->update([
+ 'is_usable' => false,
+ 'is_reachable' => false,
+ ]);
+ }
+ }
+
public function environment_variables()
{
return $this->hasMany(SharedEnvironmentVariable::class)->whereNull('project_id')->whereNull('environment_id');
@@ -263,32 +257,23 @@ class Team extends Model implements SendsDiscord, SendsEmail, SendsSlack
return $this->hasMany(S3Storage::class)->where('is_usable', true);
}
- public function subscriptionEnded()
+ public function emailNotificationSettings()
{
- $this->subscription->update([
- 'stripe_subscription_id' => null,
- 'stripe_plan_id' => null,
- 'stripe_cancel_at_period_end' => false,
- 'stripe_invoice_paid' => false,
- 'stripe_trial_already_ended' => false,
- ]);
- foreach ($this->servers as $server) {
- $server->settings()->update([
- 'is_usable' => false,
- 'is_reachable' => false,
- ]);
- }
+ return $this->hasOne(EmailNotificationSettings::class);
}
- public function isAnyNotificationEnabled()
+ public function discordNotificationSettings()
{
- if (isCloud()) {
- return true;
- }
- if ($this->smtp_enabled || $this->resend_enabled || $this->discord_enabled || $this->telegram_enabled || $this->use_instance_email_settings) {
- return true;
- }
+ return $this->hasOne(DiscordNotificationSettings::class);
+ }
- return false;
+ public function telegramNotificationSettings()
+ {
+ return $this->hasOne(TelegramNotificationSettings::class);
+ }
+
+ public function slackNotificationSettings()
+ {
+ return $this->hasOne(SlackNotificationSettings::class);
}
}
diff --git a/app/Models/TelegramNotificationSettings.php b/app/Models/TelegramNotificationSettings.php
new file mode 100644
index 000000000..2edca14ff
--- /dev/null
+++ b/app/Models/TelegramNotificationSettings.php
@@ -0,0 +1,85 @@
+ 'boolean',
+ 'telegram_token' => 'encrypted',
+ 'telegram_chat_id' => 'encrypted',
+
+ 'deployment_success_telegram_notifications' => 'boolean',
+ 'deployment_failure_telegram_notifications' => 'boolean',
+ 'status_change_telegram_notifications' => 'boolean',
+ 'backup_success_telegram_notifications' => 'boolean',
+ 'backup_failure_telegram_notifications' => 'boolean',
+ 'scheduled_task_success_telegram_notifications' => 'boolean',
+ 'scheduled_task_failure_telegram_notifications' => 'boolean',
+ 'docker_cleanup_telegram_notifications' => 'boolean',
+ 'server_disk_usage_telegram_notifications' => 'boolean',
+ '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',
+ ];
+
+ public function team()
+ {
+ return $this->belongsTo(Team::class);
+ }
+
+ public function isEnabled()
+ {
+ return $this->telegram_enabled;
+ }
+}
diff --git a/app/Models/User.php b/app/Models/User.php
index 25fb33d66..7c23631c3 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -114,7 +114,7 @@ class User extends Authenticatable implements SendsEmail
return $this->belongsToMany(Team::class)->withPivot('role');
}
- public function getRecepients($notification)
+ public function getRecipients($notification)
{
return $this->email;
}
diff --git a/app/Models/Waitlist.php b/app/Models/Waitlist.php
deleted file mode 100644
index 28e5f01fd..000000000
--- a/app/Models/Waitlist.php
+++ /dev/null
@@ -1,12 +0,0 @@
-getEnabledChannels('deployment_failure');
}
public function toMail(): MailMessage
diff --git a/app/Notifications/Application/DeploymentSuccess.php b/app/Notifications/Application/DeploymentSuccess.php
index 79ae19f66..548f5c6e5 100644
--- a/app/Notifications/Application/DeploymentSuccess.php
+++ b/app/Notifications/Application/DeploymentSuccess.php
@@ -45,13 +45,7 @@ class DeploymentSuccess extends CustomEmailNotification
public function via(object $notifiable): array
{
- $channels = setNotificationChannels($notifiable, 'deployments');
- if (isCloud()) {
- // TODO: Make batch notifications work with email
- $channels = array_diff($channels, [\App\Notifications\Channels\EmailChannel::class]);
- }
-
- return $channels;
+ return $notifiable->getEnabledChannels('deployment_success');
}
public function toMail(): MailMessage
diff --git a/app/Notifications/Application/StatusChanged.php b/app/Notifications/Application/StatusChanged.php
index 2167e9f13..32a6a659a 100644
--- a/app/Notifications/Application/StatusChanged.php
+++ b/app/Notifications/Application/StatusChanged.php
@@ -35,7 +35,7 @@ class StatusChanged extends CustomEmailNotification
public function via(object $notifiable): array
{
- return setNotificationChannels($notifiable, 'status_changes');
+ return $notifiable->getEnabledChannels('status_change');
}
public function toMail(): MailMessage
diff --git a/app/Notifications/Channels/DiscordChannel.php b/app/Notifications/Channels/DiscordChannel.php
index df7040f8f..362006d8e 100644
--- a/app/Notifications/Channels/DiscordChannel.php
+++ b/app/Notifications/Channels/DiscordChannel.php
@@ -13,10 +13,13 @@ class DiscordChannel
public function send(SendsDiscord $notifiable, Notification $notification): void
{
$message = $notification->toDiscord();
- $webhookUrl = $notifiable->routeNotificationForDiscord();
- if (! $webhookUrl) {
+
+ $discordSettings = $notifiable->discordNotificationSettings;
+
+ if (! $discordSettings || ! $discordSettings->isEnabled() || ! $discordSettings->discord_webhook_url) {
return;
}
- SendMessageToDiscordJob::dispatch($message, $webhookUrl);
+
+ SendMessageToDiscordJob::dispatch($message, $discordSettings->discord_webhook_url);
}
}
diff --git a/app/Notifications/Channels/EmailChannel.php b/app/Notifications/Channels/EmailChannel.php
index 5394f6106..0985f4393 100644
--- a/app/Notifications/Channels/EmailChannel.php
+++ b/app/Notifications/Channels/EmailChannel.php
@@ -13,7 +13,7 @@ class EmailChannel
{
try {
$this->bootConfigs($notifiable);
- $recipients = $notifiable->getRecepients($notification);
+ $recipients = $notifiable->getRecipients($notification);
if (count($recipients) === 0) {
throw new Exception('No email recipients found');
}
@@ -46,7 +46,9 @@ class EmailChannel
private function bootConfigs($notifiable): void
{
- if (data_get($notifiable, 'use_instance_email_settings')) {
+ $emailSettings = $notifiable->emailNotificationSettings;
+
+ if ($emailSettings->use_instance_email_settings) {
$type = set_transanctional_email_settings();
if (! $type) {
throw new Exception('No email settings found.');
@@ -54,24 +56,27 @@ class EmailChannel
return;
}
- config()->set('mail.from.address', data_get($notifiable, 'smtp_from_address', 'test@example.com'));
- config()->set('mail.from.name', data_get($notifiable, 'smtp_from_name', 'Test'));
- if (data_get($notifiable, 'resend_enabled')) {
+
+ config()->set('mail.from.address', $emailSettings->smtp_from_address ?? 'test@example.com');
+ config()->set('mail.from.name', $emailSettings->smtp_from_name ?? 'Test');
+
+ if ($emailSettings->resend_enabled) {
config()->set('mail.default', 'resend');
- config()->set('resend.api_key', data_get($notifiable, 'resend_api_key'));
+ config()->set('resend.api_key', $emailSettings->resend_api_key);
}
- if (data_get($notifiable, 'smtp_enabled')) {
+
+ if ($emailSettings->smtp_enabled) {
config()->set('mail.default', 'smtp');
config()->set('mail.mailers.smtp', [
'transport' => 'smtp',
- 'host' => data_get($notifiable, 'smtp_host'),
- 'port' => data_get($notifiable, 'smtp_port'),
- 'encryption' => data_get($notifiable, 'smtp_encryption') === 'none' ? null : data_get($notifiable, 'smtp_encryption'),
- 'username' => data_get($notifiable, 'smtp_username'),
- 'password' => data_get($notifiable, 'smtp_password'),
- 'timeout' => data_get($notifiable, 'smtp_timeout'),
+ 'host' => $emailSettings->smtp_host,
+ 'port' => $emailSettings->smtp_port,
+ 'encryption' => $emailSettings->smtp_encryption === 'none' ? null : $emailSettings->smtp_encryption,
+ 'username' => $emailSettings->smtp_username,
+ 'password' => $emailSettings->smtp_password,
+ 'timeout' => $emailSettings->smtp_timeout,
'local_domain' => null,
- 'auto_tls' => data_get($notifiable, 'smtp_encryption') === 'none' ? '0' : '',
+ 'auto_tls' => $emailSettings->smtp_encryption === 'none' ? '0' : '',
]);
}
}
diff --git a/app/Notifications/Channels/SendsEmail.php b/app/Notifications/Channels/SendsEmail.php
index fc7528834..3adc6d0a2 100644
--- a/app/Notifications/Channels/SendsEmail.php
+++ b/app/Notifications/Channels/SendsEmail.php
@@ -4,5 +4,5 @@ namespace App\Notifications\Channels;
interface SendsEmail
{
- public function getRecepients($notification);
+ public function getRecipients($notification);
}
diff --git a/app/Notifications/Channels/SlackChannel.php b/app/Notifications/Channels/SlackChannel.php
index 32fdbe9cf..cddb7a561 100644
--- a/app/Notifications/Channels/SlackChannel.php
+++ b/app/Notifications/Channels/SlackChannel.php
@@ -13,10 +13,12 @@ class SlackChannel
public function send(SendsSlack $notifiable, Notification $notification): void
{
$message = $notification->toSlack();
- $webhookUrl = $notifiable->routeNotificationForSlack();
- if (! $webhookUrl) {
+ $slackSettings = $notifiable->slackNotificationSettings;
+
+ if (! $slackSettings || ! $slackSettings->isEnabled() || ! $slackSettings->slack_webhook_url) {
return;
}
- SendMessageToSlackJob::dispatch($message, $webhookUrl);
+
+ SendMessageToSlackJob::dispatch($message, $slackSettings->slack_webhook_url);
}
}
diff --git a/app/Notifications/Channels/TelegramChannel.php b/app/Notifications/Channels/TelegramChannel.php
index 958c46c21..17d76181a 100644
--- a/app/Notifications/Channels/TelegramChannel.php
+++ b/app/Notifications/Channels/TelegramChannel.php
@@ -9,38 +9,32 @@ class TelegramChannel
public function send($notifiable, $notification): void
{
$data = $notification->toTelegram($notifiable);
- $telegramData = $notifiable->routeNotificationForTelegram();
+ $settings = $notifiable->telegramNotificationSettings;
+
$message = data_get($data, 'message');
$buttons = data_get($data, 'buttons', []);
- $telegramToken = data_get($telegramData, 'token');
- $chatId = data_get($telegramData, 'chat_id');
- $topicId = null;
- $topicsInstance = get_class($notification);
+ $telegramToken = $settings->telegram_token;
+ $chatId = $settings->telegram_chat_id;
+
+ $topicId = match (get_class($notification)) {
+ \App\Notifications\Test::class => $settings->telegram_notifications_test_topic_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,
+ default => null,
+ };
- switch ($topicsInstance) {
- case \App\Notifications\Test::class:
- $topicId = data_get($notifiable, 'telegram_notifications_test_message_thread_id');
- break;
- case \App\Notifications\Application\StatusChanged::class:
- case \App\Notifications\Container\ContainerRestarted::class:
- case \App\Notifications\Container\ContainerStopped::class:
- $topicId = data_get($notifiable, 'telegram_notifications_status_changes_message_thread_id');
- break;
- case \App\Notifications\Application\DeploymentSuccess::class:
- case \App\Notifications\Application\DeploymentFailed::class:
- $topicId = data_get($notifiable, 'telegram_notifications_deployments_message_thread_id');
- break;
- case \App\Notifications\Database\BackupSuccess::class:
- case \App\Notifications\Database\BackupFailed::class:
- $topicId = data_get($notifiable, 'telegram_notifications_database_backups_message_thread_id');
- break;
- case \App\Notifications\ScheduledTask\TaskFailed::class:
- $topicId = data_get($notifiable, 'telegram_notifications_scheduled_tasks_thread_id');
- break;
- }
if (! $telegramToken || ! $chatId || ! $message) {
return;
}
+
SendMessageToTelegramJob::dispatch($message, $buttons, $telegramToken, $chatId, $topicId);
}
}
diff --git a/app/Notifications/Channels/TransactionalEmailChannel.php b/app/Notifications/Channels/TransactionalEmailChannel.php
index cc7d76ebf..761780231 100644
--- a/app/Notifications/Channels/TransactionalEmailChannel.php
+++ b/app/Notifications/Channels/TransactionalEmailChannel.php
@@ -7,7 +7,6 @@ use Exception;
use Illuminate\Mail\Message;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
-use Log;
class TransactionalEmailChannel
{
@@ -15,8 +14,6 @@ class TransactionalEmailChannel
{
$settings = instanceSettings();
if (! data_get($settings, 'smtp_enabled') && ! data_get($settings, 'resend_enabled')) {
- Log::info('SMTP/Resend not enabled');
-
return;
}
$email = $notifiable->email;
diff --git a/app/Notifications/Container/ContainerRestarted.php b/app/Notifications/Container/ContainerRestarted.php
index f9becd0e8..c25072ecf 100644
--- a/app/Notifications/Container/ContainerRestarted.php
+++ b/app/Notifications/Container/ContainerRestarted.php
@@ -17,7 +17,7 @@ class ContainerRestarted extends CustomEmailNotification
public function via(object $notifiable): array
{
- return setNotificationChannels($notifiable, 'status_changes');
+ return $notifiable->getEnabledChannels('status_change');
}
public function toMail(): MailMessage
diff --git a/app/Notifications/Container/ContainerStopped.php b/app/Notifications/Container/ContainerStopped.php
index eae2cf552..bc6e52b6d 100644
--- a/app/Notifications/Container/ContainerStopped.php
+++ b/app/Notifications/Container/ContainerStopped.php
@@ -17,7 +17,7 @@ class ContainerStopped extends CustomEmailNotification
public function via(object $notifiable): array
{
- return setNotificationChannels($notifiable, 'status_changes');
+ return $notifiable->getEnabledChannels('status_change');
}
public function toMail(): MailMessage
diff --git a/app/Notifications/Database/BackupFailed.php b/app/Notifications/Database/BackupFailed.php
index 2056255d6..2208f7b1d 100644
--- a/app/Notifications/Database/BackupFailed.php
+++ b/app/Notifications/Database/BackupFailed.php
@@ -23,13 +23,13 @@ class BackupFailed extends CustomEmailNotification
public function via(object $notifiable): array
{
- return setNotificationChannels($notifiable, 'database_backups');
+ return $notifiable->getEnabledChannels('backup_failure');
}
public function toMail(): MailMessage
{
$mail = new MailMessage;
- $mail->subject("Coolify: [ACTION REQUIRED] Backup FAILED for {$this->database->name}");
+ $mail->subject("Coolify: [ACTION REQUIRED] Database Backup FAILED for {$this->database->name}");
$mail->view('emails.backup-failed', [
'name' => $this->name,
'database_name' => $this->database_name,
diff --git a/app/Notifications/Database/BackupSuccess.php b/app/Notifications/Database/BackupSuccess.php
index 71866f9fc..10b4ff3df 100644
--- a/app/Notifications/Database/BackupSuccess.php
+++ b/app/Notifications/Database/BackupSuccess.php
@@ -24,7 +24,7 @@ class BackupSuccess extends CustomEmailNotification
public function via(object $notifiable): array
{
- return setNotificationChannels($notifiable, 'database_backups');
+ return $notifiable->getEnabledChannels('backup_success');
}
public function toMail(): MailMessage
diff --git a/app/Notifications/Internal/GeneralNotification.php b/app/Notifications/Internal/GeneralNotification.php
index 347f4a0dd..7049e3055 100644
--- a/app/Notifications/Internal/GeneralNotification.php
+++ b/app/Notifications/Internal/GeneralNotification.php
@@ -2,9 +2,6 @@
namespace App\Notifications\Internal;
-use App\Notifications\Channels\DiscordChannel;
-use App\Notifications\Channels\SlackChannel;
-use App\Notifications\Channels\TelegramChannel;
use App\Notifications\Dto\DiscordMessage;
use App\Notifications\Dto\SlackMessage;
use Illuminate\Bus\Queueable;
@@ -24,22 +21,7 @@ class GeneralNotification extends Notification implements ShouldQueue
public function via(object $notifiable): array
{
- $channels = [];
- $isDiscordEnabled = data_get($notifiable, 'discord_enabled');
- $isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
- $isSlackEnabled = data_get($notifiable, 'slack_enabled');
-
- if ($isDiscordEnabled) {
- $channels[] = DiscordChannel::class;
- }
- if ($isTelegramEnabled) {
- $channels[] = TelegramChannel::class;
- }
- if ($isSlackEnabled) {
- $channels[] = SlackChannel::class;
- }
-
- return $channels;
+ return $notifiable->getEnabledChannels('general');
}
public function toDiscord(): DiscordMessage
diff --git a/app/Notifications/ScheduledTask/TaskFailed.php b/app/Notifications/ScheduledTask/TaskFailed.php
index 753da7ff0..56c410ecb 100644
--- a/app/Notifications/ScheduledTask/TaskFailed.php
+++ b/app/Notifications/ScheduledTask/TaskFailed.php
@@ -16,15 +16,15 @@ class TaskFailed extends CustomEmailNotification
{
$this->onQueue('high');
if ($task->application) {
- $this->url = $task->application->failedTaskLink($task->uuid);
+ $this->url = $task->application->taskLink($task->uuid);
} elseif ($task->service) {
- $this->url = $task->service->failedTaskLink($task->uuid);
+ $this->url = $task->service->taskLink($task->uuid);
}
}
public function via(object $notifiable): array
{
- return setNotificationChannels($notifiable, 'scheduled_tasks');
+ return $notifiable->getEnabledChannels('scheduled_task_failure');
}
public function toMail(): MailMessage
diff --git a/app/Notifications/ScheduledTask/TaskSuccess.php b/app/Notifications/ScheduledTask/TaskSuccess.php
new file mode 100644
index 000000000..fc79aea37
--- /dev/null
+++ b/app/Notifications/ScheduledTask/TaskSuccess.php
@@ -0,0 +1,88 @@
+onQueue('high');
+ if ($task->application) {
+ $this->url = $task->application->taskLink($task->uuid);
+ } elseif ($task->service) {
+ $this->url = $task->service->taskLink($task->uuid);
+ }
+ }
+
+ public function via(object $notifiable): array
+ {
+ return $notifiable->getEnabledChannels('scheduled_task_success');
+ }
+
+ public function toMail(): MailMessage
+ {
+ $mail = new MailMessage;
+ $mail->subject("Coolify: Scheduled task ({$this->task->name}) succeeded.");
+ $mail->view('emails.scheduled-task-success', [
+ 'task' => $this->task,
+ 'url' => $this->url,
+ 'output' => $this->output,
+ ]);
+
+ return $mail;
+ }
+
+ public function toDiscord(): DiscordMessage
+ {
+ $message = new DiscordMessage(
+ title: ':white_check_mark: Scheduled task succeeded',
+ description: "Scheduled task ({$this->task->name}) succeeded.",
+ color: DiscordMessage::successColor(),
+ );
+
+ if ($this->url) {
+ $message->addField('Scheduled task', '[Link]('.$this->url.')');
+ }
+
+ return $message;
+ }
+
+ public function toTelegram(): array
+ {
+ $message = "Coolify: Scheduled task ({$this->task->name}) succeeded.";
+ if ($this->url) {
+ $buttons[] = [
+ 'text' => 'Open task in Coolify',
+ 'url' => (string) $this->url,
+ ];
+ }
+
+ return [
+ 'message' => $message,
+ ];
+ }
+
+ public function toSlack(): SlackMessage
+ {
+ $title = 'Scheduled task succeeded';
+ $description = "Scheduled task ({$this->task->name}) succeeded.";
+
+ if ($this->url) {
+ $description .= "\n\n**Task URL:** {$this->url}";
+ }
+
+ return new SlackMessage(
+ title: $title,
+ description: $description,
+ color: SlackMessage::successColor()
+ );
+ }
+}
diff --git a/app/Notifications/Server/DockerCleanup.php b/app/Notifications/Server/DockerCleanup.php
deleted file mode 100644
index 46b730c7b..000000000
--- a/app/Notifications/Server/DockerCleanup.php
+++ /dev/null
@@ -1,78 +0,0 @@
-onQueue('high');
- }
-
- public function via(object $notifiable): array
- {
- $channels = [];
- // $isEmailEnabled = isEmailEnabled($notifiable);
- $isDiscordEnabled = data_get($notifiable, 'discord_enabled');
- $isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
- $isSlackEnabled = data_get($notifiable, 'slack_enabled');
- if ($isDiscordEnabled) {
- $channels[] = DiscordChannel::class;
- }
- // if ($isEmailEnabled) {
- // $channels[] = EmailChannel::class;
- // }
- if ($isTelegramEnabled) {
- $channels[] = TelegramChannel::class;
- }
- if ($isSlackEnabled) {
- $channels[] = SlackChannel::class;
- }
-
- return $channels;
- }
-
- // public function toMail(): MailMessage
- // {
- // $mail = new MailMessage();
- // $mail->subject("Coolify: Server ({$this->server->name}) high disk usage detected!");
- // $mail->view('emails.high-disk-usage', [
- // 'name' => $this->server->name,
- // 'disk_usage' => $this->disk_usage,
- // 'threshold' => $this->docker_cleanup_threshold,
- // ]);
- // return $mail;
- // }
-
- public function toDiscord(): DiscordMessage
- {
- return new DiscordMessage(
- title: ':white_check_mark: Server cleanup job done',
- description: $this->message,
- color: DiscordMessage::successColor(),
- );
- }
-
- public function toTelegram(): array
- {
- return [
- 'message' => "Coolify: Server '{$this->server->name}' cleanup job done!\n\n{$this->message}",
- ];
- }
-
- public function toSlack(): SlackMessage
- {
- return new SlackMessage(
- title: 'Server cleanup job done',
- description: "Server '{$this->server->name}' cleanup job done!\n\n{$this->message}",
- color: SlackMessage::successColor()
- );
- }
-}
diff --git a/app/Notifications/Server/DockerCleanupFailed.php b/app/Notifications/Server/DockerCleanupFailed.php
new file mode 100644
index 000000000..53714925c
--- /dev/null
+++ b/app/Notifications/Server/DockerCleanupFailed.php
@@ -0,0 +1,59 @@
+onQueue('high');
+ }
+
+ public function via(object $notifiable): array
+ {
+ return $notifiable->getEnabledChannels('docker_cleanup_failure');
+ }
+
+ public function toMail(): MailMessage
+ {
+ $mail = new MailMessage;
+ $mail->subject("Coolify: [ACTION REQUIRED] Docker cleanup job failed on {$this->server->name}");
+ $mail->view('emails.docker-cleanup-failed', [
+ 'name' => $this->server->name,
+ 'text' => $this->message,
+ ]);
+
+ return $mail;
+ }
+
+ public function toDiscord(): DiscordMessage
+ {
+ return new DiscordMessage(
+ title: ':cross_mark: Coolify: [ACTION REQUIRED] Docker cleanup job failed on '.$this->server->name,
+ description: $this->message,
+ color: DiscordMessage::errorColor(),
+ );
+ }
+
+ public function toTelegram(): array
+ {
+ return [
+ 'message' => "Coolify: [ACTION REQUIRED] Docker cleanup job failed on {$this->server->name}!\n\n{$this->message}",
+ ];
+ }
+
+ public function toSlack(): SlackMessage
+ {
+ return new SlackMessage(
+ title: 'Coolify: [ACTION REQUIRED] Docker cleanup job failed',
+ description: "Docker cleanup job failed on '{$this->server->name}'!\n\n{$this->message}",
+ color: SlackMessage::errorColor()
+ );
+ }
+}
diff --git a/app/Notifications/Server/DockerCleanupSuccess.php b/app/Notifications/Server/DockerCleanupSuccess.php
new file mode 100644
index 000000000..85a819da2
--- /dev/null
+++ b/app/Notifications/Server/DockerCleanupSuccess.php
@@ -0,0 +1,59 @@
+onQueue('high');
+ }
+
+ public function via(object $notifiable): array
+ {
+ return $notifiable->getEnabledChannels('docker_cleanup_success');
+ }
+
+ public function toMail(): MailMessage
+ {
+ $mail = new MailMessage;
+ $mail->subject("Coolify: Docker cleanup job succeeded on {$this->server->name}");
+ $mail->view('emails.docker-cleanup-success', [
+ 'name' => $this->server->name,
+ 'text' => $this->message,
+ ]);
+
+ return $mail;
+ }
+
+ public function toDiscord(): DiscordMessage
+ {
+ return new DiscordMessage(
+ title: ':white_check_mark: Coolify: Docker cleanup job succeeded on '.$this->server->name,
+ description: $this->message,
+ color: DiscordMessage::successColor(),
+ );
+ }
+
+ public function toTelegram(): array
+ {
+ return [
+ 'message' => "Coolify: Docker cleanup job succeeded on {$this->server->name}!\n\n{$this->message}",
+ ];
+ }
+
+ public function toSlack(): SlackMessage
+ {
+ return new SlackMessage(
+ title: 'Coolify: Docker cleanup job succeeded',
+ description: "Docker cleanup job succeeded on '{$this->server->name}'!\n\n{$this->message}",
+ color: SlackMessage::successColor()
+ );
+ }
+}
diff --git a/app/Notifications/Server/ForceDisabled.php b/app/Notifications/Server/ForceDisabled.php
index 143917dde..e122849da 100644
--- a/app/Notifications/Server/ForceDisabled.php
+++ b/app/Notifications/Server/ForceDisabled.php
@@ -3,10 +3,6 @@
namespace App\Notifications\Server;
use App\Models\Server;
-use App\Notifications\Channels\DiscordChannel;
-use App\Notifications\Channels\EmailChannel;
-use App\Notifications\Channels\SlackChannel;
-use App\Notifications\Channels\TelegramChannel;
use App\Notifications\CustomEmailNotification;
use App\Notifications\Dto\DiscordMessage;
use App\Notifications\Dto\SlackMessage;
@@ -21,25 +17,7 @@ class ForceDisabled extends CustomEmailNotification
public function via(object $notifiable): array
{
- $channels = [];
- $isEmailEnabled = isEmailEnabled($notifiable);
- $isDiscordEnabled = data_get($notifiable, 'discord_enabled');
- $isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
- $isSlackEnabled = data_get($notifiable, 'slack_enabled');
- if ($isDiscordEnabled) {
- $channels[] = DiscordChannel::class;
- }
- if ($isEmailEnabled) {
- $channels[] = EmailChannel::class;
- }
- if ($isTelegramEnabled) {
- $channels[] = TelegramChannel::class;
- }
- if ($isSlackEnabled) {
- $channels[] = SlackChannel::class;
- }
-
- return $channels;
+ return $notifiable->getEnabledChannels('server_force_disabled');
}
public function toMail(): MailMessage
diff --git a/app/Notifications/Server/ForceEnabled.php b/app/Notifications/Server/ForceEnabled.php
index 3b83882d8..a0e0a8b59 100644
--- a/app/Notifications/Server/ForceEnabled.php
+++ b/app/Notifications/Server/ForceEnabled.php
@@ -3,10 +3,6 @@
namespace App\Notifications\Server;
use App\Models\Server;
-use App\Notifications\Channels\DiscordChannel;
-use App\Notifications\Channels\EmailChannel;
-use App\Notifications\Channels\SlackChannel;
-use App\Notifications\Channels\TelegramChannel;
use App\Notifications\CustomEmailNotification;
use App\Notifications\Dto\DiscordMessage;
use App\Notifications\Dto\SlackMessage;
@@ -21,25 +17,7 @@ class ForceEnabled extends CustomEmailNotification
public function via(object $notifiable): array
{
- $channels = [];
- $isEmailEnabled = isEmailEnabled($notifiable);
- $isDiscordEnabled = data_get($notifiable, 'discord_enabled');
- $isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
- $isSlackEnabled = data_get($notifiable, 'slack_enabled');
- if ($isDiscordEnabled) {
- $channels[] = DiscordChannel::class;
- }
- if ($isEmailEnabled) {
- $channels[] = EmailChannel::class;
- }
- if ($isTelegramEnabled) {
- $channels[] = TelegramChannel::class;
- }
- if ($isSlackEnabled) {
- $channels[] = SlackChannel::class;
- }
-
- return $channels;
+ return $notifiable->getEnabledChannels('server_force_enabled');
}
public function toMail(): MailMessage
diff --git a/app/Notifications/Server/HighDiskUsage.php b/app/Notifications/Server/HighDiskUsage.php
index baff49508..9f4826689 100644
--- a/app/Notifications/Server/HighDiskUsage.php
+++ b/app/Notifications/Server/HighDiskUsage.php
@@ -17,7 +17,7 @@ class HighDiskUsage extends CustomEmailNotification
public function via(object $notifiable): array
{
- return setNotificationChannels($notifiable, 'server_disk_usage');
+ return $notifiable->getEnabledChannels('server_disk_usage');
}
public function toMail(): MailMessage
diff --git a/app/Notifications/Server/Reachable.php b/app/Notifications/Server/Reachable.php
index 62ece34e8..4917e04f8 100644
--- a/app/Notifications/Server/Reachable.php
+++ b/app/Notifications/Server/Reachable.php
@@ -3,10 +3,6 @@
namespace App\Notifications\Server;
use App\Models\Server;
-use App\Notifications\Channels\DiscordChannel;
-use App\Notifications\Channels\EmailChannel;
-use App\Notifications\Channels\SlackChannel;
-use App\Notifications\Channels\TelegramChannel;
use App\Notifications\CustomEmailNotification;
use App\Notifications\Dto\DiscordMessage;
use App\Notifications\Dto\SlackMessage;
@@ -30,25 +26,7 @@ class Reachable extends CustomEmailNotification
return [];
}
- $channels = [];
- $isEmailEnabled = isEmailEnabled($notifiable);
- $isDiscordEnabled = data_get($notifiable, 'discord_enabled');
- $isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
- $isSlackEnabled = data_get($notifiable, 'slack_enabled');
- if ($isDiscordEnabled) {
- $channels[] = DiscordChannel::class;
- }
- if ($isEmailEnabled) {
- $channels[] = EmailChannel::class;
- }
- if ($isTelegramEnabled) {
- $channels[] = TelegramChannel::class;
- }
- if ($isSlackEnabled) {
- $channels[] = SlackChannel::class;
- }
-
- return $channels;
+ return $notifiable->getEnabledChannels('server_reachable');
}
public function toMail(): MailMessage
diff --git a/app/Notifications/Server/Unreachable.php b/app/Notifications/Server/Unreachable.php
index 2a90d7552..43f176d49 100644
--- a/app/Notifications/Server/Unreachable.php
+++ b/app/Notifications/Server/Unreachable.php
@@ -3,10 +3,6 @@
namespace App\Notifications\Server;
use App\Models\Server;
-use App\Notifications\Channels\DiscordChannel;
-use App\Notifications\Channels\EmailChannel;
-use App\Notifications\Channels\SlackChannel;
-use App\Notifications\Channels\TelegramChannel;
use App\Notifications\CustomEmailNotification;
use App\Notifications\Dto\DiscordMessage;
use App\Notifications\Dto\SlackMessage;
@@ -30,26 +26,7 @@ class Unreachable extends CustomEmailNotification
return [];
}
- $channels = [];
- $isEmailEnabled = isEmailEnabled($notifiable);
- $isDiscordEnabled = data_get($notifiable, 'discord_enabled');
- $isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
- $isSlackEnabled = data_get($notifiable, 'slack_enabled');
-
- if ($isDiscordEnabled) {
- $channels[] = DiscordChannel::class;
- }
- if ($isEmailEnabled) {
- $channels[] = EmailChannel::class;
- }
- if ($isTelegramEnabled) {
- $channels[] = TelegramChannel::class;
- }
- if ($isSlackEnabled) {
- $channels[] = SlackChannel::class;
- }
-
- return $channels;
+ return $notifiable->getEnabledChannels('server_unreachable');
}
public function toMail(): ?MailMessage
diff --git a/app/Notifications/Test.php b/app/Notifications/Test.php
index 03f6c3296..da9098500 100644
--- a/app/Notifications/Test.php
+++ b/app/Notifications/Test.php
@@ -2,6 +2,10 @@
namespace App\Notifications;
+use App\Notifications\Channels\DiscordChannel;
+use App\Notifications\Channels\EmailChannel;
+use App\Notifications\Channels\SlackChannel;
+use App\Notifications\Channels\TelegramChannel;
use App\Notifications\Dto\DiscordMessage;
use App\Notifications\Dto\SlackMessage;
use Illuminate\Bus\Queueable;
@@ -16,20 +20,32 @@ class Test extends Notification implements ShouldQueue
public $tries = 5;
- public function __construct(public ?string $emails = null)
+ public function __construct(public ?string $emails = null, public ?string $channel = null)
{
$this->onQueue('high');
}
public function via(object $notifiable): array
{
- return setNotificationChannels($notifiable, 'test');
+ if ($this->channel) {
+ $channels = match ($this->channel) {
+ 'email' => [EmailChannel::class],
+ 'discord' => [DiscordChannel::class],
+ 'telegram' => [TelegramChannel::class],
+ 'slack' => [SlackChannel::class],
+ default => [],
+ };
+ } else {
+ $channels = $notifiable->getEnabledChannels('test');
+ }
+
+ return $channels;
}
public function middleware(object $notifiable, string $channel)
{
return match ($channel) {
- \App\Notifications\Channels\EmailChannel::class => [new RateLimited('email')],
+ EmailChannel::class => [new RateLimited('email')],
default => [],
};
}
diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php
index bbbf48345..ed27a158a 100644
--- a/app/Providers/FortifyServiceProvider.php
+++ b/app/Providers/FortifyServiceProvider.php
@@ -50,13 +50,10 @@ class FortifyServiceProvider extends ServiceProvider
if (! $settings->is_registration_enabled) {
return redirect()->route('login');
}
- if (config('constants.waitlist.enabled')) {
- return redirect()->route('waitlist.index');
- } else {
- return view('auth.register', [
- 'isFirstUser' => $isFirstUser,
- ]);
- }
+
+ return view('auth.register', [
+ 'isFirstUser' => $isFirstUser,
+ ]);
});
Fortify::loginView(function () {
diff --git a/app/Traits/HasNotificationSettings.php b/app/Traits/HasNotificationSettings.php
new file mode 100644
index 000000000..82cbda6ad
--- /dev/null
+++ b/app/Traits/HasNotificationSettings.php
@@ -0,0 +1,90 @@
+ $this->emailNotificationSettings,
+ 'discord' => $this->discordNotificationSettings,
+ 'telegram' => $this->telegramNotificationSettings,
+ 'slack' => $this->slackNotificationSettings,
+ default => null,
+ };
+ }
+
+ /**
+ * Check if a notification channel is enabled
+ */
+ public function isNotificationEnabled(string $channel): bool
+ {
+ $settings = $this->getNotificationSettings($channel);
+
+ return $settings?->isEnabled() ?? false;
+ }
+
+ /**
+ * Check if a specific notification type is enabled for a channel
+ */
+ public function isNotificationTypeEnabled(string $channel, string $event): bool
+ {
+ $settings = $this->getNotificationSettings($channel);
+
+ if (! $settings || ! $this->isNotificationEnabled($channel)) {
+ return false;
+ }
+
+ if (in_array($event, $this->alwaysSendEvents)) {
+ return true;
+ }
+
+ $settingKey = "{$event}_{$channel}_notifications";
+
+ return (bool) $settings->$settingKey;
+ }
+
+ /**
+ * Get all enabled notification channels for an event
+ */
+ public function getEnabledChannels(string $event): array
+ {
+ $channels = [];
+
+ $channelMap = [
+ 'email' => EmailChannel::class,
+ 'discord' => DiscordChannel::class,
+ 'telegram' => TelegramChannel::class,
+ 'slack' => SlackChannel::class,
+ ];
+
+ if ($event === 'general') {
+ unset($channelMap['email']);
+ }
+
+ foreach ($channelMap as $channel => $channelClass) {
+ if ($this->isNotificationEnabled($channel) && $this->isNotificationTypeEnabled($channel, $event)) {
+ $channels[] = $channelClass;
+ }
+ }
+
+ return $channels;
+ }
+}
diff --git a/bootstrap/helpers/notifications.php b/bootstrap/helpers/notifications.php
new file mode 100644
index 000000000..3b1eb758b
--- /dev/null
+++ b/bootstrap/helpers/notifications.php
@@ -0,0 +1,87 @@
+smtp_enabled || $settings->resend_enabled;
+}
+
+function send_internal_notification(string $message): void
+{
+ try {
+ $team = Team::find(0);
+ $team?->notify(new GeneralNotification($message));
+ } catch (\Throwable $e) {
+ ray($e->getMessage());
+ }
+}
+
+function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null): void
+{
+ $settings = instanceSettings();
+ $type = set_transanctional_email_settings($settings);
+ if (! $type) {
+ throw new Exception('No email settings found.');
+ }
+ if ($cc) {
+ Mail::send(
+ [],
+ [],
+ fn (Message $message) => $message
+ ->to($email)
+ ->replyTo($email)
+ ->cc($cc)
+ ->subject($mail->subject)
+ ->html((string) $mail->render())
+ );
+ } else {
+ Mail::send(
+ [],
+ [],
+ fn (Message $message) => $message
+ ->to($email)
+ ->subject($mail->subject)
+ ->html((string) $mail->render())
+ );
+ }
+}
+
+function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string //
+{
+ if (! $settings) {
+ $settings = instanceSettings();
+ }
+ config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
+ config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
+ if (data_get($settings, 'resend_enabled')) {
+ config()->set('mail.default', 'resend');
+ config()->set('resend.api_key', data_get($settings, 'resend_api_key'));
+
+ return 'resend';
+ }
+ if (data_get($settings, 'smtp_enabled')) {
+ config()->set('mail.default', 'smtp');
+ config()->set('mail.mailers.smtp', [
+ 'transport' => 'smtp',
+ 'host' => data_get($settings, 'smtp_host'),
+ 'port' => data_get($settings, 'smtp_port'),
+ 'encryption' => data_get($settings, 'smtp_encryption'),
+ 'username' => data_get($settings, 'smtp_username'),
+ 'password' => data_get($settings, 'smtp_password'),
+ 'timeout' => data_get($settings, 'smtp_timeout'),
+ 'local_domain' => null,
+ ]);
+
+ return 'smtp';
+ }
+
+ return null;
+}
diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php
index 835ead24c..ea051c84e 100644
--- a/bootstrap/helpers/shared.php
+++ b/bootstrap/helpers/shared.php
@@ -25,23 +25,15 @@ use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use App\Models\Team;
use App\Models\User;
-use App\Notifications\Channels\DiscordChannel;
-use App\Notifications\Channels\EmailChannel;
-use App\Notifications\Channels\SlackChannel;
-use App\Notifications\Channels\TelegramChannel;
-use App\Notifications\Internal\GeneralNotification;
use Carbon\CarbonImmutable;
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
use Illuminate\Database\UniqueConstraintViolationException;
-use Illuminate\Mail\Message;
-use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Process\Pool;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
-use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Request;
@@ -267,43 +259,6 @@ function generate_application_name(string $git_repository, string $git_branch, ?
return Str::kebab("$git_repository:$git_branch-$cuid");
}
-function is_transactional_emails_active(): bool
-{
- return isEmailEnabled(\App\Models\InstanceSettings::get());
-}
-
-function set_transanctional_email_settings(?InstanceSettings $settings = null): ?string
-{
- if (! $settings) {
- $settings = instanceSettings();
- }
- config()->set('mail.from.address', data_get($settings, 'smtp_from_address'));
- config()->set('mail.from.name', data_get($settings, 'smtp_from_name'));
- if (data_get($settings, 'resend_enabled')) {
- config()->set('mail.default', 'resend');
- config()->set('resend.api_key', data_get($settings, 'resend_api_key'));
-
- return 'resend';
- }
- if (data_get($settings, 'smtp_enabled')) {
- config()->set('mail.default', 'smtp');
- config()->set('mail.mailers.smtp', [
- 'transport' => 'smtp',
- 'host' => data_get($settings, 'smtp_host'),
- 'port' => data_get($settings, 'smtp_port'),
- 'encryption' => data_get($settings, 'smtp_encryption'),
- 'username' => data_get($settings, 'smtp_username'),
- 'password' => data_get($settings, 'smtp_password'),
- 'timeout' => data_get($settings, 'smtp_timeout'),
- 'local_domain' => null,
- ]);
-
- return 'smtp';
- }
-
- return null;
-}
-
function base_ip(): string
{
if (isDev()) {
@@ -414,85 +369,7 @@ function validate_timezone(string $timezone): bool
{
return in_array($timezone, timezone_identifiers_list());
}
-function send_internal_notification(string $message): void
-{
- try {
- $team = Team::find(0);
- $team?->notify(new GeneralNotification($message));
- } catch (\Throwable $e) {
- ray($e->getMessage());
- }
-}
-function send_user_an_email(MailMessage $mail, string $email, ?string $cc = null): void
-{
- $settings = instanceSettings();
- $type = set_transanctional_email_settings($settings);
- if (! $type) {
- throw new Exception('No email settings found.');
- }
- if ($cc) {
- Mail::send(
- [],
- [],
- fn (Message $message) => $message
- ->to($email)
- ->replyTo($email)
- ->cc($cc)
- ->subject($mail->subject)
- ->html((string) $mail->render())
- );
- } else {
- Mail::send(
- [],
- [],
- fn (Message $message) => $message
- ->to($email)
- ->subject($mail->subject)
- ->html((string) $mail->render())
- );
- }
-}
-function isTestEmailEnabled($notifiable)
-{
- if (data_get($notifiable, 'use_instance_email_settings') && isInstanceAdmin()) {
- return true;
- } elseif (data_get($notifiable, 'smtp_enabled') || data_get($notifiable, 'resend_enabled') && auth()->user()->isAdminFromSession()) {
- return true;
- }
- return false;
-}
-function isEmailEnabled($notifiable)
-{
- return data_get($notifiable, 'smtp_enabled') || data_get($notifiable, 'resend_enabled') || data_get($notifiable, 'use_instance_email_settings');
-}
-function setNotificationChannels($notifiable, $event)
-{
- $channels = [];
- $isEmailEnabled = isEmailEnabled($notifiable);
- $isSlackEnabled = data_get($notifiable, 'slack_enabled');
- $isDiscordEnabled = data_get($notifiable, 'discord_enabled');
- $isTelegramEnabled = data_get($notifiable, 'telegram_enabled');
- $isSubscribedToEmailEvent = data_get($notifiable, "smtp_notifications_$event");
- $isSubscribedToDiscordEvent = data_get($notifiable, "discord_notifications_$event");
- $isSubscribedToTelegramEvent = data_get($notifiable, "telegram_notifications_$event");
- $isSubscribedToSlackEvent = data_get($notifiable, "slack_notifications_$event");
-
- if ($isDiscordEnabled && $isSubscribedToDiscordEvent) {
- $channels[] = DiscordChannel::class;
- }
- if ($isEmailEnabled && $isSubscribedToEmailEvent) {
- $channels[] = EmailChannel::class;
- }
- if ($isTelegramEnabled && $isSubscribedToTelegramEvent) {
- $channels[] = TelegramChannel::class;
- }
- if ($isSlackEnabled && $isSubscribedToSlackEvent) {
- $channels[] = SlackChannel::class;
- }
-
- return $channels;
-}
function parseEnvFormatToArray($env_file_contents)
{
$env_array = [];
diff --git a/bootstrap/helpers/subscriptions.php b/bootstrap/helpers/subscriptions.php
index 8ddb1331c..ab9ee9b9d 100644
--- a/bootstrap/helpers/subscriptions.php
+++ b/bootstrap/helpers/subscriptions.php
@@ -67,7 +67,6 @@ function allowedPathsForUnsubscribedAccounts()
'subscription/new',
'login',
'logout',
- 'waitlist',
'force-password-reset',
'livewire/update',
];
diff --git a/config/constants.php b/config/constants.php
index 42abd09cb..4ea4411b6 100644
--- a/config/constants.php
+++ b/config/constants.php
@@ -77,11 +77,6 @@ return [
],
],
- 'waitlist' => [
- 'enabled' => env('WAITLIST', false),
- 'expiration' => 10,
- ],
-
'sentry' => [
'sentry_dsn' => env('SENTRY_DSN'),
],
diff --git a/database/migrations/2024_12_05_212355_create_email_notification_settings_table.php b/database/migrations/2024_12_05_212355_create_email_notification_settings_table.php
new file mode 100644
index 000000000..7338a8d0d
--- /dev/null
+++ b/database/migrations/2024_12_05_212355_create_email_notification_settings_table.php
@@ -0,0 +1,58 @@
+id();
+ $table->foreignId('team_id')->constrained()->cascadeOnDelete();
+
+ $table->boolean('smtp_enabled')->default(false);
+ $table->text('smtp_from_address')->nullable();
+ $table->text('smtp_from_name')->nullable();
+ $table->text('smtp_recipients')->nullable();
+ $table->text('smtp_host')->nullable();
+ $table->integer('smtp_port')->nullable();
+ $table->string('smtp_encryption')->nullable();
+ $table->text('smtp_username')->nullable();
+ $table->text('smtp_password')->nullable();
+ $table->integer('smtp_timeout')->nullable();
+
+ $table->boolean('resend_enabled')->default(false);
+ $table->text('resend_api_key')->nullable();
+
+ $table->boolean('use_instance_email_settings')->default(false);
+
+ $table->boolean('deployment_success_email_notifications')->default(false);
+ $table->boolean('deployment_failure_email_notifications')->default(true);
+ $table->boolean('status_change_email_notifications')->default(false);
+ $table->boolean('backup_success_email_notifications')->default(false);
+ $table->boolean('backup_failure_email_notifications')->default(true);
+ $table->boolean('scheduled_task_success_email_notifications')->default(false);
+ $table->boolean('scheduled_task_failure_email_notifications')->default(true);
+ $table->boolean('docker_cleanup_success_email_notifications')->default(false);
+ $table->boolean('docker_cleanup_failure_email_notifications')->default(true);
+ $table->boolean('server_disk_usage_email_notifications')->default(true);
+ $table->boolean('server_reachable_email_notifications')->default(false);
+ $table->boolean('server_unreachable_email_notifications')->default(true);
+
+ $table->unique(['team_id']);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('email_notification_settings');
+ }
+};
diff --git a/database/migrations/2024_12_05_212416_create_discord_notification_settings_table.php b/database/migrations/2024_12_05_212416_create_discord_notification_settings_table.php
new file mode 100644
index 000000000..0f2ced67f
--- /dev/null
+++ b/database/migrations/2024_12_05_212416_create_discord_notification_settings_table.php
@@ -0,0 +1,45 @@
+id();
+ $table->foreignId('team_id')->constrained()->cascadeOnDelete();
+
+ $table->boolean('discord_enabled')->default(false);
+ $table->text('discord_webhook_url')->nullable();
+
+ $table->boolean('deployment_success_discord_notifications')->default(false);
+ $table->boolean('deployment_failure_discord_notifications')->default(true);
+ $table->boolean('status_change_discord_notifications')->default(false);
+ $table->boolean('backup_success_discord_notifications')->default(false);
+ $table->boolean('backup_failure_discord_notifications')->default(true);
+ $table->boolean('scheduled_task_success_discord_notifications')->default(false);
+ $table->boolean('scheduled_task_failure_discord_notifications')->default(true);
+ $table->boolean('docker_cleanup_success_discord_notifications')->default(false);
+ $table->boolean('docker_cleanup_failure_discord_notifications')->default(true);
+ $table->boolean('server_disk_usage_discord_notifications')->default(true);
+ $table->boolean('server_reachable_discord_notifications')->default(false);
+ $table->boolean('server_unreachable_discord_notifications')->default(true);
+
+ $table->unique(['team_id']);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('discord_notification_settings');
+ }
+};
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
new file mode 100644
index 000000000..190d148fc
--- /dev/null
+++ b/database/migrations/2024_12_05_212440_create_telegram_notification_settings_table.php
@@ -0,0 +1,59 @@
+id();
+ $table->foreignId('team_id')->constrained()->cascadeOnDelete();
+
+ $table->boolean('telegram_enabled')->default(false);
+ $table->text('telegram_token')->nullable();
+ $table->text('telegram_chat_id')->nullable();
+
+ $table->boolean('deployment_success_telegram_notifications')->default(false);
+ $table->boolean('deployment_failure_telegram_notifications')->default(true);
+ $table->boolean('status_change_telegram_notifications')->default(false);
+ $table->boolean('backup_success_telegram_notifications')->default(false);
+ $table->boolean('backup_failure_telegram_notifications')->default(true);
+ $table->boolean('scheduled_task_success_telegram_notifications')->default(false);
+ $table->boolean('scheduled_task_failure_telegram_notifications')->default(true);
+ $table->boolean('docker_cleanup_success_telegram_notifications')->default(false);
+ $table->boolean('docker_cleanup_failure_telegram_notifications')->default(true);
+ $table->boolean('server_disk_usage_telegram_notifications')->default(true);
+ $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->unique(['team_id']);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('telegram_notification_settings');
+ }
+};
diff --git a/database/migrations/2024_12_05_212546_migrate_email_notification_settings_from_teams_table.php b/database/migrations/2024_12_05_212546_migrate_email_notification_settings_from_teams_table.php
new file mode 100644
index 000000000..384f62f06
--- /dev/null
+++ b/database/migrations/2024_12_05_212546_migrate_email_notification_settings_from_teams_table.php
@@ -0,0 +1,135 @@
+get();
+
+ foreach ($teams as $team) {
+ try {
+ DB::table('email_notification_settings')->updateOrInsert(
+ ['team_id' => $team->id],
+ [
+ 'smtp_enabled' => $team->smtp_enabled ?? false,
+ 'smtp_from_address' => $team->smtp_from_address ? Crypt::encryptString($team->smtp_from_address) : null,
+ 'smtp_from_name' => $team->smtp_from_name ? Crypt::encryptString($team->smtp_from_name) : null,
+ 'smtp_recipients' => $team->smtp_recipients ? Crypt::encryptString($team->smtp_recipients) : null,
+ 'smtp_host' => $team->smtp_host ? Crypt::encryptString($team->smtp_host) : null,
+ 'smtp_port' => $team->smtp_port,
+ 'smtp_encryption' => $team->smtp_encryption,
+ 'smtp_username' => $team->smtp_username ? Crypt::encryptString($team->smtp_username) : null,
+ 'smtp_password' => $team->smtp_password,
+ 'smtp_timeout' => $team->smtp_timeout,
+
+ 'use_instance_email_settings' => $team->use_instance_email_settings ?? false,
+
+ 'resend_enabled' => $team->resend_enabled ?? false,
+ 'resend_api_key' => $team->resend_api_key,
+
+ 'deployment_success_email_notifications' => $team->smtp_notifications_deployments ?? false,
+ 'deployment_failure_email_notifications' => $team->smtp_notifications_deployments ?? true,
+ 'backup_success_email_notifications' => $team->smtp_notifications_database_backups ?? false,
+ 'backup_failure_email_notifications' => $team->smtp_notifications_database_backups ?? true,
+ 'scheduled_task_success_email_notifications' => $team->smtp_notifications_scheduled_tasks ?? false,
+ 'scheduled_task_failure_email_notifications' => $team->smtp_notifications_scheduled_tasks ?? true,
+ 'status_change_email_notifications' => $team->smtp_notifications_status_changes ?? false,
+ 'server_disk_usage_email_notifications' => $team->smtp_notifications_server_disk_usage ?? true,
+ ]
+ );
+ } catch (Exception $e) {
+ \Log::error('Error migrating email notification settings from teams table: '.$e->getMessage());
+ }
+ }
+
+ Schema::table('teams', function (Blueprint $table) {
+ $table->dropColumn([
+ 'smtp_enabled',
+ 'smtp_from_address',
+ 'smtp_from_name',
+ 'smtp_recipients',
+ 'smtp_host',
+ 'smtp_port',
+ 'smtp_encryption',
+ 'smtp_username',
+ 'smtp_password',
+ 'smtp_timeout',
+ 'use_instance_email_settings',
+ 'resend_enabled',
+ 'resend_api_key',
+ 'smtp_notifications_test',
+ 'smtp_notifications_deployments',
+ 'smtp_notifications_database_backups',
+ 'smtp_notifications_scheduled_tasks',
+ 'smtp_notifications_status_changes',
+ 'smtp_notifications_server_disk_usage',
+ ]);
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('teams', function (Blueprint $table) {
+ $table->boolean('smtp_enabled')->default(false);
+ $table->string('smtp_from_address')->nullable();
+ $table->string('smtp_from_name')->nullable();
+ $table->string('smtp_recipients')->nullable();
+ $table->string('smtp_host')->nullable();
+ $table->integer('smtp_port')->nullable();
+ $table->string('smtp_encryption')->nullable();
+ $table->text('smtp_username')->nullable();
+ $table->text('smtp_password')->nullable();
+ $table->integer('smtp_timeout')->nullable();
+
+ $table->boolean('use_instance_email_settings')->default(false);
+ $table->boolean('resend_enabled')->default(false);
+
+ $table->text('resend_api_key')->nullable();
+
+ $table->boolean('smtp_notifications_test')->default(false);
+ $table->boolean('smtp_notifications_deployments')->default(false);
+ $table->boolean('smtp_notifications_database_backups')->default(true);
+ $table->boolean('smtp_notifications_scheduled_tasks')->default(false);
+ $table->boolean('smtp_notifications_status_changes')->default(false);
+ $table->boolean('smtp_notifications_server_disk_usage')->default(true);
+ });
+
+ $settings = DB::table('email_notification_settings')->get();
+ foreach ($settings as $setting) {
+ try {
+ DB::table('teams')
+ ->where('id', $setting->team_id)
+ ->update([
+ 'smtp_enabled' => $setting->smtp_enabled,
+ 'smtp_from_address' => $setting->smtp_from_address ? Crypt::decryptString($setting->smtp_from_address) : null,
+ 'smtp_from_name' => $setting->smtp_from_name ? Crypt::decryptString($setting->smtp_from_name) : null,
+ 'smtp_recipients' => $setting->smtp_recipients ? Crypt::decryptString($setting->smtp_recipients) : null,
+ 'smtp_host' => $setting->smtp_host ? Crypt::decryptString($setting->smtp_host) : null,
+ 'smtp_port' => $setting->smtp_port,
+ 'smtp_encryption' => $setting->smtp_encryption,
+ 'smtp_username' => $setting->smtp_username ? Crypt::decryptString($setting->smtp_username) : null,
+ 'smtp_password' => $setting->smtp_password,
+ 'smtp_timeout' => $setting->smtp_timeout,
+
+ 'use_instance_email_settings' => $setting->use_instance_email_settings,
+
+ 'resend_enabled' => $setting->resend_enabled,
+ 'resend_api_key' => $setting->resend_api_key,
+
+ 'smtp_notifications_deployments' => $setting->deployment_success_email_notifications || $setting->deployment_failure_email_notifications,
+ 'smtp_notifications_database_backups' => $setting->backup_success_email_notifications || $setting->backup_failure_email_notifications,
+ 'smtp_notifications_scheduled_tasks' => $setting->scheduled_task_success_email_notifications || $setting->scheduled_task_failure_email_notifications,
+ 'smtp_notifications_status_changes' => $setting->status_change_email_notifications,
+ ]);
+ } catch (Exception $e) {
+ \Log::error('Error migrating email notification settings from teams table: '.$e->getMessage());
+ }
+ }
+ }
+};
diff --git a/database/migrations/2024_12_05_212631_migrate_discord_notification_settings_from_teams_table.php b/database/migrations/2024_12_05_212631_migrate_discord_notification_settings_from_teams_table.php
new file mode 100644
index 000000000..ed9e9af82
--- /dev/null
+++ b/database/migrations/2024_12_05_212631_migrate_discord_notification_settings_from_teams_table.php
@@ -0,0 +1,92 @@
+get();
+
+ foreach ($teams as $team) {
+ try {
+ DB::table('discord_notification_settings')->updateOrInsert(
+ ['team_id' => $team->id],
+ [
+ 'discord_enabled' => $team->discord_enabled ?? false,
+ 'discord_webhook_url' => $team->discord_webhook_url ? Crypt::encryptString($team->discord_webhook_url) : null,
+
+ 'deployment_success_discord_notifications' => $team->discord_notifications_deployments ?? false,
+ 'deployment_failure_discord_notifications' => $team->discord_notifications_deployments ?? true,
+ 'backup_success_discord_notifications' => $team->discord_notifications_database_backups ?? false,
+ 'backup_failure_discord_notifications' => $team->discord_notifications_database_backups ?? true,
+ 'scheduled_task_success_discord_notifications' => $team->discord_notifications_scheduled_tasks ?? false,
+ 'scheduled_task_failure_discord_notifications' => $team->discord_notifications_scheduled_tasks ?? true,
+ 'status_change_discord_notifications' => $team->discord_notifications_status_changes ?? false,
+ 'server_disk_usage_discord_notifications' => $team->discord_notifications_server_disk_usage ?? true,
+ ]
+ );
+ } catch (Exception $e) {
+ \Log::error('Error migrating discord notification settings from teams table: '.$e->getMessage());
+ }
+ }
+
+ Schema::table('teams', function (Blueprint $table) {
+ $table->dropColumn([
+ 'discord_enabled',
+ 'discord_webhook_url',
+ 'discord_notifications_test',
+ 'discord_notifications_deployments',
+ 'discord_notifications_status_changes',
+ 'discord_notifications_database_backups',
+ 'discord_notifications_scheduled_tasks',
+ 'discord_notifications_server_disk_usage',
+ ]);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('teams', function (Blueprint $table) {
+ $table->boolean('discord_enabled')->default(false);
+ $table->string('discord_webhook_url')->nullable();
+
+ $table->boolean('discord_notifications_test')->default(true);
+ $table->boolean('discord_notifications_deployments')->default(true);
+ $table->boolean('discord_notifications_status_changes')->default(true);
+ $table->boolean('discord_notifications_database_backups')->default(true);
+ $table->boolean('discord_notifications_scheduled_tasks')->default(true);
+ $table->boolean('discord_notifications_server_disk_usage')->default(true);
+ });
+
+ $settings = DB::table('discord_notification_settings')->get();
+ foreach ($settings as $setting) {
+ try {
+ DB::table('teams')
+ ->where('id', $setting->team_id)
+ ->update([
+ 'discord_enabled' => $setting->discord_enabled,
+ 'discord_webhook_url' => Crypt::decryptString($setting->discord_webhook_url),
+
+ 'discord_notifications_deployments' => $setting->deployment_success_discord_notifications || $setting->deployment_failure_discord_notifications,
+ 'discord_notifications_status_changes' => $setting->status_change_discord_notifications,
+ 'discord_notifications_database_backups' => $setting->backup_success_discord_notifications || $setting->backup_failure_discord_notifications,
+ 'discord_notifications_scheduled_tasks' => $setting->scheduled_task_success_discord_notifications || $setting->scheduled_task_failure_discord_notifications,
+ 'discord_notifications_server_disk_usage' => $setting->server_disk_usage_discord_notifications,
+ ]);
+ } catch (Exception $e) {
+ \Log::error('Error migrating discord notification settings from teams table: '.$e->getMessage());
+ }
+ }
+ }
+};
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
new file mode 100644
index 000000000..0c10646b9
--- /dev/null
+++ b/database/migrations/2024_12_05_212705_migrate_telegram_notification_settings_from_teams_table.php
@@ -0,0 +1,114 @@
+get();
+
+ foreach ($teams as $team) {
+ try {
+ DB::table('telegram_notification_settings')->updateOrInsert(
+ ['team_id' => $team->id],
+ [
+ 'telegram_enabled' => $team->telegram_enabled ?? false,
+ 'telegram_token' => $team->telegram_token ? Crypt::encryptString($team->telegram_token) : null,
+ 'telegram_chat_id' => $team->telegram_chat_id ? Crypt::encryptString($team->telegram_chat_id) : null,
+
+ 'deployment_success_telegram_notifications' => $team->telegram_notifications_deployments ?? false,
+ 'deployment_failure_telegram_notifications' => $team->telegram_notifications_deployments ?? true,
+ 'backup_success_telegram_notifications' => $team->telegram_notifications_database_backups ?? false,
+ 'backup_failure_telegram_notifications' => $team->telegram_notifications_database_backups ?? true,
+ 'scheduled_task_success_telegram_notifications' => $team->telegram_notifications_scheduled_tasks ?? false,
+ 'scheduled_task_failure_telegram_notifications' => $team->telegram_notifications_scheduled_tasks ?? true,
+ '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,
+ ]
+ );
+ } catch (Exception $e) {
+ \Log::error('Error migrating telegram notification settings from teams table: '.$e->getMessage());
+ }
+ }
+
+ Schema::table('teams', function (Blueprint $table) {
+ $table->dropColumn([
+ 'telegram_enabled',
+ 'telegram_token',
+ 'telegram_chat_id',
+ 'telegram_notifications_test',
+ 'telegram_notifications_deployments',
+ 'telegram_notifications_status_changes',
+ 'telegram_notifications_database_backups',
+ 'telegram_notifications_scheduled_tasks',
+ 'telegram_notifications_server_disk_usage',
+ 'telegram_notifications_test_message_thread_id',
+ 'telegram_notifications_deployments_message_thread_id',
+ 'telegram_notifications_status_changes_message_thread_id',
+ 'telegram_notifications_database_backups_message_thread_id',
+ 'telegram_notifications_scheduled_tasks_thread_id',
+ ]);
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::table('teams', function (Blueprint $table) {
+ $table->boolean('telegram_enabled')->default(false);
+ $table->text('telegram_token')->nullable();
+ $table->text('telegram_chat_id')->nullable();
+
+ $table->boolean('telegram_notifications_test')->default(true);
+ $table->boolean('telegram_notifications_deployments')->default(true);
+ $table->boolean('telegram_notifications_status_changes')->default(true);
+ $table->boolean('telegram_notifications_database_backups')->default(true);
+ $table->boolean('telegram_notifications_scheduled_tasks')->default(true);
+ $table->boolean('telegram_notifications_server_disk_usage')->default(true);
+
+ $table->text('telegram_notifications_test_message_thread_id')->nullable();
+ $table->text('telegram_notifications_deployments_message_thread_id')->nullable();
+ $table->text('telegram_notifications_status_changes_message_thread_id')->nullable();
+ $table->text('telegram_notifications_database_backups_message_thread_id')->nullable();
+ $table->text('telegram_notifications_scheduled_tasks_thread_id')->nullable();
+ });
+
+ $settings = DB::table('telegram_notification_settings')->get();
+ foreach ($settings as $setting) {
+ try {
+ DB::table('teams')
+ ->where('id', $setting->team_id)
+ ->update([
+ 'telegram_enabled' => $setting->telegram_enabled,
+ 'telegram_token' => $setting->telegram_token ? Crypt::decryptString($setting->telegram_token) : null,
+ 'telegram_chat_id' => $setting->telegram_chat_id ? Crypt::decryptString($setting->telegram_chat_id) : null,
+
+ 'telegram_notifications_deployments' => $setting->deployment_success_telegram_notifications || $setting->deployment_failure_telegram_notifications,
+ 'telegram_notifications_status_changes' => $setting->status_change_telegram_notifications,
+ 'telegram_notifications_database_backups' => $setting->backup_success_telegram_notifications || $setting->backup_failure_telegram_notifications,
+ '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,
+ ]);
+ } catch (Exception $e) {
+ \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
new file mode 100644
index 000000000..790e0f667
--- /dev/null
+++ b/database/migrations/2024_12_06_142014_create_slack_notification_settings_table.php
@@ -0,0 +1,57 @@
+id();
+ $table->foreignId('team_id')->constrained()->cascadeOnDelete();
+
+ $table->boolean('slack_enabled')->default(false);
+ $table->text('slack_webhook_url')->nullable();
+
+ $table->boolean('deployment_success_slack_notifications')->default(false);
+ $table->boolean('deployment_failure_slack_notifications')->default(true);
+ $table->boolean('status_change_slack_notifications')->default(false);
+ $table->boolean('backup_success_slack_notifications')->default(false);
+ $table->boolean('backup_failure_slack_notifications')->default(true);
+ $table->boolean('scheduled_task_success_slack_notifications')->default(false);
+ $table->boolean('scheduled_task_failure_slack_notifications')->default(true);
+ $table->boolean('docker_cleanup_success_slack_notifications')->default(false);
+ $table->boolean('docker_cleanup_failure_slack_notifications')->default(true);
+ $table->boolean('server_disk_usage_slack_notifications')->default(true);
+ $table->boolean('server_reachable_slack_notifications')->default(false);
+ $table->boolean('server_unreachable_slack_notifications')->default(true);
+
+ $table->unique(['team_id']);
+ });
+ $teams = DB::table('teams')->get();
+
+ foreach ($teams as $team) {
+ try {
+ DB::table('slack_notification_settings')->insert([
+ 'team_id' => $team->id,
+ ]);
+ } catch (\Throwable $e) {
+ \Log::error('Error migrating slack notification settings from teams table: '.$e->getMessage());
+ }
+ }
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('slack_notification_settings');
+ }
+};
diff --git a/database/migrations/2024_12_09_105711_drop_waitlists_table.php b/database/migrations/2024_12_09_105711_drop_waitlists_table.php
new file mode 100644
index 000000000..0e319369d
--- /dev/null
+++ b/database/migrations/2024_12_09_105711_drop_waitlists_table.php
@@ -0,0 +1,31 @@
+id();
+ $table->string('uuid');
+ $table->string('type');
+ $table->string('email')->unique();
+ $table->boolean('verified')->default(false);
+ $table->timestamps();
+ });
+ }
+};
diff --git a/database/migrations/2024_12_10_122142_encrypt_instance_settings_email_columns.php b/database/migrations/2024_12_10_122142_encrypt_instance_settings_email_columns.php
new file mode 100644
index 000000000..44e0f2f47
--- /dev/null
+++ b/database/migrations/2024_12_10_122142_encrypt_instance_settings_email_columns.php
@@ -0,0 +1,72 @@
+exists()) {
+ Schema::table('instance_settings', function (Blueprint $table) {
+ $table->text('smtp_from_address')->nullable()->change();
+ $table->text('smtp_from_name')->nullable()->change();
+ $table->text('smtp_recipients')->nullable()->change();
+ $table->text('smtp_host')->nullable()->change();
+ $table->text('smtp_username')->nullable()->change();
+ });
+
+ $settings = DB::table('instance_settings')->get();
+ foreach ($settings as $setting) {
+ try {
+ DB::table('instance_settings')->where('id', $setting->id)->update([
+ 'smtp_from_address' => $setting->smtp_from_address ? Crypt::encryptString($setting->smtp_from_address) : null,
+ 'smtp_from_name' => $setting->smtp_from_name ? Crypt::encryptString($setting->smtp_from_name) : null,
+ 'smtp_recipients' => $setting->smtp_recipients ? Crypt::encryptString($setting->smtp_recipients) : null,
+ 'smtp_host' => $setting->smtp_host ? Crypt::encryptString($setting->smtp_host) : null,
+ 'smtp_username' => $setting->smtp_username ? Crypt::encryptString($setting->smtp_username) : null,
+ ]);
+ } catch (Exception $e) {
+ \Log::error('Error encrypting instance settings email columns: '.$e->getMessage());
+ }
+ }
+ }
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('instance_settings', function (Blueprint $table) {
+ $table->string('smtp_from_address')->nullable()->change();
+ $table->string('smtp_from_name')->nullable()->change();
+ $table->string('smtp_recipients')->nullable()->change();
+ $table->string('smtp_host')->nullable()->change();
+ $table->string('smtp_username')->nullable()->change();
+ });
+
+ if (DB::table('instance_settings')->exists()) {
+ $settings = DB::table('instance_settings')->get();
+ foreach ($settings as $setting) {
+ try {
+ DB::table('instance_settings')->where('id', $setting->id)->update([
+ 'smtp_from_address' => $setting->smtp_from_address ? Crypt::decryptString($setting->smtp_from_address) : null,
+ 'smtp_from_name' => $setting->smtp_from_name ? Crypt::decryptString($setting->smtp_from_name) : null,
+ 'smtp_recipients' => $setting->smtp_recipients ? Crypt::decryptString($setting->smtp_recipients) : null,
+ 'smtp_host' => $setting->smtp_host ? Crypt::decryptString($setting->smtp_host) : null,
+ 'smtp_username' => $setting->smtp_username ? Crypt::decryptString($setting->smtp_username) : null,
+ ]);
+ } catch (Exception $e) {
+ \Log::error('Error decrypting instance settings email columns: '.$e->getMessage());
+ }
+ }
+ }
+ }
+};
diff --git a/database/migrations/2024_12_10_122143_drop_resale_license.php b/database/migrations/2024_12_10_122143_drop_resale_license.php
new file mode 100644
index 000000000..aaf498c3b
--- /dev/null
+++ b/database/migrations/2024_12_10_122143_drop_resale_license.php
@@ -0,0 +1,30 @@
+dropColumn('is_resale_license_active');
+ $table->dropColumn('resale_license');
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('instance_settings', function (Blueprint $table) {
+ $table->boolean('is_resale_license_active')->default(false);
+ $table->longText('resale_license')->nullable();
+ });
+ }
+};
diff --git a/database/seeders/InstanceSettingsSeeder.php b/database/seeders/InstanceSettingsSeeder.php
index 35fc8506b..7f2deb3a6 100644
--- a/database/seeders/InstanceSettingsSeeder.php
+++ b/database/seeders/InstanceSettingsSeeder.php
@@ -16,7 +16,6 @@ class InstanceSettingsSeeder extends Seeder
InstanceSettings::create([
'id' => 0,
'is_registration_enabled' => true,
- 'is_resale_license_active' => true,
'smtp_enabled' => true,
'smtp_host' => 'coolify-mail',
'smtp_port' => 1025,
diff --git a/database/seeders/TestTeamSeeder.php b/database/seeders/TestTeamSeeder.php
deleted file mode 100644
index 940c45cc5..000000000
--- a/database/seeders/TestTeamSeeder.php
+++ /dev/null
@@ -1,42 +0,0 @@
-create([
- 'name' => '1 personal, 1 other team, owner, no other members',
- 'email' => '1@example.com',
- ]);
- $team = Team::create([
- 'name' => '1@example.com',
- 'personal_team' => false,
- 'show_boarding' => true,
- ]);
- $user->teams()->attach($team, ['role' => 'owner']);
-
- // User has 2 teams, 1 personal, 1 other where it is the owner and 1 other member is in the team
- $user = User::factory()->create([
- 'name' => 'owner: 1 personal, 1 other team, owner, 1 other member',
- 'email' => '2@example.com',
- ]);
- $team = Team::create([
- 'name' => '2@example.com',
- 'personal_team' => false,
- 'show_boarding' => true,
- ]);
- $user->teams()->attach($team, ['role' => 'owner']);
- $user = User::factory()->create([
- 'name' => 'member: 1 personal, 1 other team, owner, 1 other member',
- 'email' => '3@example.com',
- ]);
- $team->members()->attach($user, ['role' => 'member']);
- }
-}
diff --git a/resources/views/auth/forgot-password.blade.php b/resources/views/auth/forgot-password.blade.php
index 00cf95a44..a61a8fb32 100644
--- a/resources/views/auth/forgot-password.blade.php
+++ b/resources/views/auth/forgot-password.blade.php
@@ -9,7 +9,7 @@
+ Select events for which you would like to receive Discord notifications. +
+