Merge pull request #4525 from peaklabs-dev/separate-success-and-failure-notifications
Feat: New Notification Settings
This commit is contained in:
		@@ -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',
 | 
			
		||||
                ]);
 | 
			
		||||
 
 | 
			
		||||
@@ -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');
 | 
			
		||||
 
 | 
			
		||||
@@ -1,114 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Console\Commands;
 | 
			
		||||
 | 
			
		||||
use App\Models\User;
 | 
			
		||||
use App\Models\Waitlist;
 | 
			
		||||
use Illuminate\Console\Command;
 | 
			
		||||
use Illuminate\Notifications\Messages\MailMessage;
 | 
			
		||||
use Illuminate\Support\Facades\Crypt;
 | 
			
		||||
use Illuminate\Support\Facades\Hash;
 | 
			
		||||
use Illuminate\Support\Str;
 | 
			
		||||
 | 
			
		||||
class WaitlistInvite extends Command
 | 
			
		||||
{
 | 
			
		||||
    public Waitlist|User|null $next_patient = null;
 | 
			
		||||
 | 
			
		||||
    public ?string $password = null;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The name and signature of the console command.
 | 
			
		||||
     *
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    protected $signature = 'waitlist:invite {--people=1} {--only-email} {email?}';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The console command description.
 | 
			
		||||
     *
 | 
			
		||||
     * @var string
 | 
			
		||||
     */
 | 
			
		||||
    protected $description = 'Send invitation to the next user (or by email) in the waitlist';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Execute the console command.
 | 
			
		||||
     */
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        $people = $this->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. 📧');
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,58 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Console\Commands;
 | 
			
		||||
 | 
			
		||||
use App\Actions\Server\ServerCheck;
 | 
			
		||||
use App\Enums\ProxyStatus;
 | 
			
		||||
use App\Enums\ProxyTypes;
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use Illuminate\Console\Command;
 | 
			
		||||
use Str;
 | 
			
		||||
 | 
			
		||||
class Weird extends Command
 | 
			
		||||
{
 | 
			
		||||
    protected $signature = 'weird {--number=1} {--run}';
 | 
			
		||||
 | 
			
		||||
    protected $description = 'Weird stuff';
 | 
			
		||||
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            if (! isDev()) {
 | 
			
		||||
                $this->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());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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']),
 | 
			
		||||
 
 | 
			
		||||
@@ -1,63 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Http\Controllers\Webhook;
 | 
			
		||||
 | 
			
		||||
use App\Http\Controllers\Controller;
 | 
			
		||||
use App\Models\Waitlist as ModelsWaitlist;
 | 
			
		||||
use Exception;
 | 
			
		||||
use Illuminate\Http\Request;
 | 
			
		||||
 | 
			
		||||
class Waitlist extends Controller
 | 
			
		||||
{
 | 
			
		||||
    public function confirm(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) {
 | 
			
		||||
                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');
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,28 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs;
 | 
			
		||||
 | 
			
		||||
use App\Actions\License\CheckResaleLicense;
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
 | 
			
		||||
class CheckResaleLicenseJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public function __construct() {}
 | 
			
		||||
 | 
			
		||||
    public function handle(): void
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            CheckResaleLicense::run();
 | 
			
		||||
        } catch (\Throwable $e) {
 | 
			
		||||
            send_internal_notification('CheckResaleLicenseJob failed with: '.$e->getMessage());
 | 
			
		||||
            throw $e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Jobs;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Bus\Queueable;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
 | 
			
		||||
use Illuminate\Contracts\Queue\ShouldQueue;
 | 
			
		||||
use Illuminate\Foundation\Bus\Dispatchable;
 | 
			
		||||
use Illuminate\Notifications\Messages\MailMessage;
 | 
			
		||||
use Illuminate\Queue\InteractsWithQueue;
 | 
			
		||||
use Illuminate\Queue\SerializesModels;
 | 
			
		||||
 | 
			
		||||
class SendConfirmationForWaitlistJob implements ShouldBeEncrypted, ShouldQueue
 | 
			
		||||
{
 | 
			
		||||
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
 | 
			
		||||
 | 
			
		||||
    public function __construct(public string $email, public string $uuid) {}
 | 
			
		||||
 | 
			
		||||
    public function handle()
 | 
			
		||||
    {
 | 
			
		||||
        try {
 | 
			
		||||
            $mail = new MailMessage;
 | 
			
		||||
            $confirmation_url = base_url().'/webhooks/waitlist/confirm?email='.$this->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;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,70 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Livewire\Waitlist;
 | 
			
		||||
 | 
			
		||||
use App\Jobs\SendConfirmationForWaitlistJob;
 | 
			
		||||
use App\Models\User;
 | 
			
		||||
use App\Models\Waitlist;
 | 
			
		||||
use Illuminate\Support\Str;
 | 
			
		||||
use Livewire\Component;
 | 
			
		||||
 | 
			
		||||
class Index extends Component
 | 
			
		||||
{
 | 
			
		||||
    public string $email;
 | 
			
		||||
 | 
			
		||||
    public int $users = 0;
 | 
			
		||||
 | 
			
		||||
    public int $waitingInLine = 0;
 | 
			
		||||
 | 
			
		||||
    protected $rules = [
 | 
			
		||||
        'email' => '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. <br>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. <br>Please check your email to verify your email address.');
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                $this->dispatch('error', 'You are already on the waitlist. <br>You will be notified when your turn comes. <br>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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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', [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								app/Models/DiscordNotificationSettings.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								app/Models/DiscordNotificationSettings.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Models;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
use Illuminate\Notifications\Notifiable;
 | 
			
		||||
 | 
			
		||||
class DiscordNotificationSettings extends Model
 | 
			
		||||
{
 | 
			
		||||
    use Notifiable;
 | 
			
		||||
 | 
			
		||||
    public $timestamps = false;
 | 
			
		||||
 | 
			
		||||
    protected $fillable = [
 | 
			
		||||
        'team_id',
 | 
			
		||||
 | 
			
		||||
        'discord_enabled',
 | 
			
		||||
        'discord_webhook_url',
 | 
			
		||||
 | 
			
		||||
        'deployment_success_discord_notifications',
 | 
			
		||||
        'deployment_failure_discord_notifications',
 | 
			
		||||
        'status_change_discord_notifications',
 | 
			
		||||
        'backup_success_discord_notifications',
 | 
			
		||||
        'backup_failure_discord_notifications',
 | 
			
		||||
        'scheduled_task_success_discord_notifications',
 | 
			
		||||
        'scheduled_task_failure_discord_notifications',
 | 
			
		||||
        'docker_cleanup_discord_notifications',
 | 
			
		||||
        'server_disk_usage_discord_notifications',
 | 
			
		||||
        'server_reachable_discord_notifications',
 | 
			
		||||
        'server_unreachable_discord_notifications',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    protected $casts = [
 | 
			
		||||
        'discord_enabled' => '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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								app/Models/EmailNotificationSettings.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								app/Models/EmailNotificationSettings.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,79 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Models;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
 | 
			
		||||
class EmailNotificationSettings extends Model
 | 
			
		||||
{
 | 
			
		||||
    public $timestamps = false;
 | 
			
		||||
 | 
			
		||||
    protected $fillable = [
 | 
			
		||||
        'team_id',
 | 
			
		||||
 | 
			
		||||
        'smtp_enabled',
 | 
			
		||||
        'smtp_from_address',
 | 
			
		||||
        'smtp_from_name',
 | 
			
		||||
        'smtp_recipients',
 | 
			
		||||
        'smtp_host',
 | 
			
		||||
        'smtp_port',
 | 
			
		||||
        'smtp_encryption',
 | 
			
		||||
        'smtp_username',
 | 
			
		||||
        'smtp_password',
 | 
			
		||||
        'smtp_timeout',
 | 
			
		||||
 | 
			
		||||
        'resend_enabled',
 | 
			
		||||
        'resend_api_key',
 | 
			
		||||
 | 
			
		||||
        'use_instance_email_settings',
 | 
			
		||||
 | 
			
		||||
        'deployment_success_email_notifications',
 | 
			
		||||
        'deployment_failure_email_notifications',
 | 
			
		||||
        'status_change_email_notifications',
 | 
			
		||||
        'backup_success_email_notifications',
 | 
			
		||||
        'backup_failure_email_notifications',
 | 
			
		||||
        'scheduled_task_success_email_notifications',
 | 
			
		||||
        'scheduled_task_failure_email_notifications',
 | 
			
		||||
        'server_disk_usage_email_notifications',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    protected $casts = [
 | 
			
		||||
        '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',
 | 
			
		||||
 | 
			
		||||
        '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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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 === '') {
 | 
			
		||||
 
 | 
			
		||||
@@ -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])]);
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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', [
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										59
									
								
								app/Models/SlackNotificationSettings.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								app/Models/SlackNotificationSettings.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Models;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
use Illuminate\Notifications\Notifiable;
 | 
			
		||||
 | 
			
		||||
class SlackNotificationSettings extends Model
 | 
			
		||||
{
 | 
			
		||||
    use Notifiable;
 | 
			
		||||
 | 
			
		||||
    public $timestamps = false;
 | 
			
		||||
 | 
			
		||||
    protected $fillable = [
 | 
			
		||||
        'team_id',
 | 
			
		||||
 | 
			
		||||
        'slack_enabled',
 | 
			
		||||
        'slack_webhook_url',
 | 
			
		||||
 | 
			
		||||
        'deployment_success_slack_notifications',
 | 
			
		||||
        'deployment_failure_slack_notifications',
 | 
			
		||||
        'status_change_slack_notifications',
 | 
			
		||||
        'backup_success_slack_notifications',
 | 
			
		||||
        'backup_failure_slack_notifications',
 | 
			
		||||
        'scheduled_task_success_slack_notifications',
 | 
			
		||||
        'scheduled_task_failure_slack_notifications',
 | 
			
		||||
        'docker_cleanup_slack_notifications',
 | 
			
		||||
        'server_disk_usage_slack_notifications',
 | 
			
		||||
        'server_reachable_slack_notifications',
 | 
			
		||||
        'server_unreachable_slack_notifications',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    protected $casts = [
 | 
			
		||||
        'slack_enabled' => '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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										85
									
								
								app/Models/TelegramNotificationSettings.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								app/Models/TelegramNotificationSettings.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Models;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
use Illuminate\Notifications\Notifiable;
 | 
			
		||||
 | 
			
		||||
class TelegramNotificationSettings extends Model
 | 
			
		||||
{
 | 
			
		||||
    use Notifiable;
 | 
			
		||||
 | 
			
		||||
    public $timestamps = false;
 | 
			
		||||
 | 
			
		||||
    protected $fillable = [
 | 
			
		||||
        'team_id',
 | 
			
		||||
 | 
			
		||||
        'telegram_enabled',
 | 
			
		||||
        'telegram_token',
 | 
			
		||||
        'telegram_chat_id',
 | 
			
		||||
 | 
			
		||||
        'deployment_success_telegram_notifications',
 | 
			
		||||
        'deployment_failure_telegram_notifications',
 | 
			
		||||
        'status_change_telegram_notifications',
 | 
			
		||||
        'backup_success_telegram_notifications',
 | 
			
		||||
        'backup_failure_telegram_notifications',
 | 
			
		||||
        'scheduled_task_success_telegram_notifications',
 | 
			
		||||
        'scheduled_task_failure_telegram_notifications',
 | 
			
		||||
        'docker_cleanup_telegram_notifications',
 | 
			
		||||
        'server_disk_usage_telegram_notifications',
 | 
			
		||||
        'server_reachable_telegram_notifications',
 | 
			
		||||
        'server_unreachable_telegram_notifications',
 | 
			
		||||
 | 
			
		||||
        'telegram_notifications_deployment_success_topic_id',
 | 
			
		||||
        'telegram_notifications_deployment_failure_topic_id',
 | 
			
		||||
        'telegram_notifications_status_change_topic_id',
 | 
			
		||||
        'telegram_notifications_backup_success_topic_id',
 | 
			
		||||
        'telegram_notifications_backup_failure_topic_id',
 | 
			
		||||
        'telegram_notifications_scheduled_task_success_topic_id',
 | 
			
		||||
        'telegram_notifications_scheduled_task_failure_topic_id',
 | 
			
		||||
        'telegram_notifications_docker_cleanup_topic_id',
 | 
			
		||||
        'telegram_notifications_server_disk_usage_topic_id',
 | 
			
		||||
        'telegram_notifications_server_reachable_topic_id',
 | 
			
		||||
        'telegram_notifications_server_unreachable_topic_id',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    protected $casts = [
 | 
			
		||||
        'telegram_enabled' => '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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Models;
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
 | 
			
		||||
 | 
			
		||||
class Waitlist extends BaseModel
 | 
			
		||||
{
 | 
			
		||||
    use HasFactory;
 | 
			
		||||
 | 
			
		||||
    protected $guarded = [];
 | 
			
		||||
}
 | 
			
		||||
@@ -45,7 +45,7 @@ class DeploymentFailed extends CustomEmailNotification
 | 
			
		||||
 | 
			
		||||
    public function via(object $notifiable): array
 | 
			
		||||
    {
 | 
			
		||||
        return setNotificationChannels($notifiable, 'deployments');
 | 
			
		||||
        return $notifiable->getEnabledChannels('deployment_failure');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function toMail(): MailMessage
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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' : '',
 | 
			
		||||
            ]);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -4,5 +4,5 @@ namespace App\Notifications\Channels;
 | 
			
		||||
 | 
			
		||||
interface SendsEmail
 | 
			
		||||
{
 | 
			
		||||
    public function getRecepients($notification);
 | 
			
		||||
    public function getRecipients($notification);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										88
									
								
								app/Notifications/ScheduledTask/TaskSuccess.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								app/Notifications/ScheduledTask/TaskSuccess.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Notifications\ScheduledTask;
 | 
			
		||||
 | 
			
		||||
use App\Models\ScheduledTask;
 | 
			
		||||
use App\Notifications\CustomEmailNotification;
 | 
			
		||||
use App\Notifications\Dto\DiscordMessage;
 | 
			
		||||
use App\Notifications\Dto\SlackMessage;
 | 
			
		||||
use Illuminate\Notifications\Messages\MailMessage;
 | 
			
		||||
 | 
			
		||||
class TaskSuccess extends CustomEmailNotification
 | 
			
		||||
{
 | 
			
		||||
    public ?string $url = null;
 | 
			
		||||
 | 
			
		||||
    public function __construct(public ScheduledTask $task, public string $output)
 | 
			
		||||
    {
 | 
			
		||||
        $this->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()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,78 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Notifications\Server;
 | 
			
		||||
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use App\Notifications\Channels\DiscordChannel;
 | 
			
		||||
use App\Notifications\Channels\TelegramChannel;
 | 
			
		||||
use App\Notifications\CustomEmailNotification;
 | 
			
		||||
use App\Notifications\Dto\DiscordMessage;
 | 
			
		||||
use App\Notifications\Dto\SlackMessage;
 | 
			
		||||
 | 
			
		||||
class DockerCleanup extends CustomEmailNotification
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(public Server $server, public string $message)
 | 
			
		||||
    {
 | 
			
		||||
        $this->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()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								app/Notifications/Server/DockerCleanupFailed.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								app/Notifications/Server/DockerCleanupFailed.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Notifications\Server;
 | 
			
		||||
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use App\Notifications\CustomEmailNotification;
 | 
			
		||||
use App\Notifications\Dto\DiscordMessage;
 | 
			
		||||
use App\Notifications\Dto\SlackMessage;
 | 
			
		||||
use Illuminate\Notifications\Messages\MailMessage;
 | 
			
		||||
 | 
			
		||||
class DockerCleanupFailed extends CustomEmailNotification
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(public Server $server, public string $message)
 | 
			
		||||
    {
 | 
			
		||||
        $this->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()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								app/Notifications/Server/DockerCleanupSuccess.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								app/Notifications/Server/DockerCleanupSuccess.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Notifications\Server;
 | 
			
		||||
 | 
			
		||||
use App\Models\Server;
 | 
			
		||||
use App\Notifications\CustomEmailNotification;
 | 
			
		||||
use App\Notifications\Dto\DiscordMessage;
 | 
			
		||||
use App\Notifications\Dto\SlackMessage;
 | 
			
		||||
use Illuminate\Notifications\Messages\MailMessage;
 | 
			
		||||
 | 
			
		||||
class DockerCleanupSuccess extends CustomEmailNotification
 | 
			
		||||
{
 | 
			
		||||
    public function __construct(public Server $server, public string $message)
 | 
			
		||||
    {
 | 
			
		||||
        $this->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()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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 => [],
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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 () {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								app/Traits/HasNotificationSettings.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								app/Traits/HasNotificationSettings.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,90 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace App\Traits;
 | 
			
		||||
 | 
			
		||||
use App\Notifications\Channels\DiscordChannel;
 | 
			
		||||
use App\Notifications\Channels\EmailChannel;
 | 
			
		||||
use App\Notifications\Channels\SlackChannel;
 | 
			
		||||
use App\Notifications\Channels\TelegramChannel;
 | 
			
		||||
use Illuminate\Database\Eloquent\Model;
 | 
			
		||||
 | 
			
		||||
trait HasNotificationSettings
 | 
			
		||||
{
 | 
			
		||||
    protected $alwaysSendEvents = [
 | 
			
		||||
        'server_force_enabled',
 | 
			
		||||
        'server_force_disabled',
 | 
			
		||||
        'general',
 | 
			
		||||
        'test',
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get settings model for specific channel
 | 
			
		||||
     */
 | 
			
		||||
    public function getNotificationSettings(string $channel): ?Model
 | 
			
		||||
    {
 | 
			
		||||
        return match ($channel) {
 | 
			
		||||
            'email' => $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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										87
									
								
								bootstrap/helpers/notifications.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								bootstrap/helpers/notifications.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use App\Models\InstanceSettings;
 | 
			
		||||
use App\Models\Team;
 | 
			
		||||
use App\Notifications\Internal\GeneralNotification;
 | 
			
		||||
use Illuminate\Mail\Message;
 | 
			
		||||
use Illuminate\Notifications\Messages\MailMessage;
 | 
			
		||||
use Illuminate\Support\Facades\Mail;
 | 
			
		||||
 | 
			
		||||
function is_transactional_emails_enabled(): bool
 | 
			
		||||
{
 | 
			
		||||
    $settings = instanceSettings();
 | 
			
		||||
 | 
			
		||||
    return $settings->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;
 | 
			
		||||
}
 | 
			
		||||
@@ -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 = [];
 | 
			
		||||
 
 | 
			
		||||
@@ -67,7 +67,6 @@ function allowedPathsForUnsubscribedAccounts()
 | 
			
		||||
        'subscription/new',
 | 
			
		||||
        'login',
 | 
			
		||||
        'logout',
 | 
			
		||||
        'waitlist',
 | 
			
		||||
        'force-password-reset',
 | 
			
		||||
        'livewire/update',
 | 
			
		||||
    ];
 | 
			
		||||
 
 | 
			
		||||
@@ -77,11 +77,6 @@ return [
 | 
			
		||||
        ],
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'waitlist' => [
 | 
			
		||||
        'enabled' => env('WAITLIST', false),
 | 
			
		||||
        'expiration' => 10,
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    'sentry' => [
 | 
			
		||||
        'sentry_dsn' => env('SENTRY_DSN'),
 | 
			
		||||
    ],
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,58 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        Schema::create('email_notification_settings', function (Blueprint $table) {
 | 
			
		||||
            $table->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');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,45 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        Schema::create('discord_notification_settings', function (Blueprint $table) {
 | 
			
		||||
            $table->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');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,59 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        Schema::create('telegram_notification_settings', function (Blueprint $table) {
 | 
			
		||||
            $table->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');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,135 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Crypt;
 | 
			
		||||
use Illuminate\Support\Facades\DB;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration
 | 
			
		||||
{
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        $teams = DB::table('teams')->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());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,92 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Crypt;
 | 
			
		||||
use Illuminate\Support\Facades\DB;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        $teams = DB::table('teams')->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());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,114 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Crypt;
 | 
			
		||||
use Illuminate\Support\Facades\DB;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration
 | 
			
		||||
{
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        $teams = DB::table('teams')->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());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,57 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\DB;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        Schema::create('slack_notification_settings', function (Blueprint $table) {
 | 
			
		||||
            $table->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');
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,31 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        Schema::dropIfExists('waitlists');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Reverse the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function down(): void
 | 
			
		||||
    {
 | 
			
		||||
        Schema::create('waitlists', function (Blueprint $table) {
 | 
			
		||||
            $table->id();
 | 
			
		||||
            $table->string('uuid');
 | 
			
		||||
            $table->string('type');
 | 
			
		||||
            $table->string('email')->unique();
 | 
			
		||||
            $table->boolean('verified')->default(false);
 | 
			
		||||
            $table->timestamps();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,72 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Crypt;
 | 
			
		||||
use Illuminate\Support\Facades\DB;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        if (DB::table('instance_settings')->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());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -0,0 +1,30 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
use Illuminate\Database\Migrations\Migration;
 | 
			
		||||
use Illuminate\Database\Schema\Blueprint;
 | 
			
		||||
use Illuminate\Support\Facades\Schema;
 | 
			
		||||
 | 
			
		||||
return new class extends Migration
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * Run the migrations.
 | 
			
		||||
     */
 | 
			
		||||
    public function up(): void
 | 
			
		||||
    {
 | 
			
		||||
        Schema::table('instance_settings', function (Blueprint $table) {
 | 
			
		||||
            $table->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();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
@@ -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,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Database\Seeders;
 | 
			
		||||
 | 
			
		||||
use App\Models\Team;
 | 
			
		||||
use App\Models\User;
 | 
			
		||||
use Illuminate\Database\Seeder;
 | 
			
		||||
 | 
			
		||||
class TestTeamSeeder extends Seeder
 | 
			
		||||
{
 | 
			
		||||
    public function run(): void
 | 
			
		||||
    {
 | 
			
		||||
        // User has 2 teams, 1 personal, 1 other where it is the owner and no other members are in the team
 | 
			
		||||
        $user = User::factory()->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']);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -9,7 +9,7 @@
 | 
			
		||||
            <div
 | 
			
		||||
                class="w-full bg-white shadow md:mt-0 sm:max-w-md xl:p-0 dark:bg-base ">
 | 
			
		||||
                <div class="p-6 space-y-4 md:space-y-6 sm:p-8">
 | 
			
		||||
                    @if (is_transactional_emails_active())
 | 
			
		||||
                    @if (is_transactional_emails_enabled())
 | 
			
		||||
                    <form action="/forgot-password" method="POST" class="flex flex-col gap-2">
 | 
			
		||||
                        @csrf
 | 
			
		||||
                        <x-forms.input required type="email" name="email" label="{{ __('input.email') }}" />
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										8
									
								
								resources/views/emails/docker-cleanup-failed.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								resources/views/emails/docker-cleanup-failed.blade.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
<x-emails.layout>
 | 
			
		||||
Docker Cleanup on {{ $name }} FAILED with the following error:
 | 
			
		||||
 | 
			
		||||
<pre>
 | 
			
		||||
{{ $text }}
 | 
			
		||||
</pre>
 | 
			
		||||
 | 
			
		||||
</x-emails.layout>
 | 
			
		||||
							
								
								
									
										9
									
								
								resources/views/emails/docker-cleanup-success.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								resources/views/emails/docker-cleanup-success.blade.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
<x-emails.layout>
 | 
			
		||||
Docker Cleanup on {{ $name }} succeeded with the following message:
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<pre>
 | 
			
		||||
{{ $text }}
 | 
			
		||||
</pre>
 | 
			
		||||
 | 
			
		||||
</x-emails.layout>
 | 
			
		||||
							
								
								
									
										9
									
								
								resources/views/emails/scheduled-task-success.blade.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								resources/views/emails/scheduled-task-success.blade.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
			
		||||
<x-emails.layout>
 | 
			
		||||
Scheduled task ({{ $task->name }}) completed successfully with the following output:
 | 
			
		||||
 | 
			
		||||
<pre>
 | 
			
		||||
{{ $output }}
 | 
			
		||||
</pre>
 | 
			
		||||
 | 
			
		||||
Click [here]({{ $url }}) to view the task.
 | 
			
		||||
</x-emails.layout>
 | 
			
		||||
@@ -1,7 +0,0 @@
 | 
			
		||||
<x-emails.layout>
 | 
			
		||||
Someone added this email to the Coolify Cloud's waitlist. [Click here]({{ $confirmation_url }}) to confirm!
 | 
			
		||||
 | 
			
		||||
The link will expire in {{ config('constants.waitlist.expiration') }} minutes.
 | 
			
		||||
 | 
			
		||||
You have no idea what [Coolify Cloud](https://coolify.io) is or this waitlist? [Click here]({{ $cancel_url }}) to remove you from the waitlist.
 | 
			
		||||
</x-emails.layout>
 | 
			
		||||
@@ -1,3 +0,0 @@
 | 
			
		||||
<x-emails.layout>
 | 
			
		||||
You have been invited to join the Coolify Cloud: [Get Started]({{ $loginLink }})
 | 
			
		||||
</x-emails.layout>
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
        Notifications | Coolify
 | 
			
		||||
    </x-slot>
 | 
			
		||||
    <x-notification.navbar />
 | 
			
		||||
    <form wire:submit='submit' class="flex flex-col gap-4">
 | 
			
		||||
    <form wire:submit='submit' class="flex flex-col gap-4 pb-4">
 | 
			
		||||
        <div class="flex items-center gap-2">
 | 
			
		||||
            <h2>Discord</h2>
 | 
			
		||||
            <x-forms.button type="submit">
 | 
			
		||||
@@ -12,7 +12,11 @@
 | 
			
		||||
            @if ($discordEnabled)
 | 
			
		||||
                <x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
 | 
			
		||||
                    wire:click="sendTestNotification">
 | 
			
		||||
                    Send Test Notifications
 | 
			
		||||
                    Send Test Notification
 | 
			
		||||
                </x-forms.button>
 | 
			
		||||
            @else
 | 
			
		||||
                <x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
 | 
			
		||||
                    Send Test Notification
 | 
			
		||||
                </x-forms.button>
 | 
			
		||||
            @endif
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -23,21 +27,55 @@
 | 
			
		||||
            helper="Generate a webhook in Discord.<br>Example: https://discord.com/api/webhooks/...." required
 | 
			
		||||
            id="discordWebhookUrl" label="Webhook" />
 | 
			
		||||
    </form>
 | 
			
		||||
    @if ($discordEnabled)
 | 
			
		||||
        <h2 class="mt-4">Subscribe to events</h2>
 | 
			
		||||
        <div class="w-64">
 | 
			
		||||
            @if (isDev())
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="discordNotificationsTest" label="Test" />
 | 
			
		||||
            @endif
 | 
			
		||||
            <x-forms.checkbox instantSave="saveModel" id="discordNotificationsStatusChanges"
 | 
			
		||||
                label="Container Status Changes" />
 | 
			
		||||
            <x-forms.checkbox instantSave="saveModel" id="discordNotificationsDeployments"
 | 
			
		||||
                label="Application Deployments" />
 | 
			
		||||
            <x-forms.checkbox instantSave="saveModel" id="discordNotificationsDatabaseBackups" label="Backup Status" />
 | 
			
		||||
            <x-forms.checkbox instantSave="saveModel" id="discordNotificationsScheduledTasks"
 | 
			
		||||
                label="Scheduled Tasks Status" />
 | 
			
		||||
            <x-forms.checkbox instantSave="saveModel" id="discordNotificationsServerDiskUsage"
 | 
			
		||||
                label="Server Disk Usage" />
 | 
			
		||||
    <h2 class="mt-4">Notification Settings</h2>
 | 
			
		||||
    <p class="mb-4">
 | 
			
		||||
        Select events for which you would like to receive Discord notifications.
 | 
			
		||||
    </p>
 | 
			
		||||
    <div class="flex flex-col gap-4 max-w-2xl">
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="font-medium mb-3">Deployments</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="deploymentSuccessDiscordNotifications"
 | 
			
		||||
                    label="Deployment Success" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="deploymentFailureDiscordNotifications"
 | 
			
		||||
                    label="Deployment Failure" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel"
 | 
			
		||||
                    helper="Send a notification when a container status changes. It will notify for Stopped and Restarted events of a container."
 | 
			
		||||
                    id="statusChangeDiscordNotifications" label="Container Status Changes" />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    @endif
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="font-medium mb-3">Backups</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="backupSuccessDiscordNotifications"
 | 
			
		||||
                    label="Backup Success" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="backupFailureDiscordNotifications"
 | 
			
		||||
                    label="Backup Failure" />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="font-medium mb-3">Scheduled Tasks</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessDiscordNotifications"
 | 
			
		||||
                    label="Scheduled Task Success" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailureDiscordNotifications"
 | 
			
		||||
                    label="Scheduled Task Failure" />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="font-medium mb-3">Server</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessDiscordNotifications"
 | 
			
		||||
                    label="Docker Cleanup Success" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailureDiscordNotifications"
 | 
			
		||||
                    label="Docker Cleanup Failure" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="serverDiskUsageDiscordNotifications"
 | 
			
		||||
                    label="Server Disk Usage" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="serverReachableDiscordNotifications"
 | 
			
		||||
                    label="Server Reachable" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="serverUnreachableDiscordNotifications"
 | 
			
		||||
                    label="Server Unreachable" />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -9,26 +9,27 @@
 | 
			
		||||
            <x-forms.button type="submit">
 | 
			
		||||
                Save
 | 
			
		||||
            </x-forms.button>
 | 
			
		||||
            @if (isInstanceAdmin() && !$useInstanceEmailSettings)
 | 
			
		||||
                <x-forms.button wire:click='copyFromInstanceSettings'>
 | 
			
		||||
                    Copy from Instance Settings
 | 
			
		||||
                </x-forms.button>
 | 
			
		||||
            @endif
 | 
			
		||||
            @if (isEmailEnabled($team) && auth()->user()->isAdminFromSession() && isTestEmailEnabled($team))
 | 
			
		||||
                <x-modal-input buttonTitle="Send Test Email" title="Send Test Email">
 | 
			
		||||
                    <form wire:submit.prevent="sendTestEmail" class="flex flex-col w-full gap-2">
 | 
			
		||||
                        <x-forms.input wire:model="testEmailAddress" placeholder="test@example.com" id="testEmailAddress"
 | 
			
		||||
                            label="Recipients" required />
 | 
			
		||||
                        <x-forms.button type="submit" @click="modalOpen=false">
 | 
			
		||||
                            Send Email
 | 
			
		||||
                        </x-forms.button>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </x-modal-input>
 | 
			
		||||
            @if (auth()->user()->isAdminFromSession())
 | 
			
		||||
                @if ($team->isNotificationEnabled('email'))
 | 
			
		||||
                    <x-modal-input buttonTitle="Send Test Email" title="Send Test Email">
 | 
			
		||||
                        <form wire:submit.prevent="sendTestEmail" class="flex flex-col w-full gap-2">
 | 
			
		||||
                            <x-forms.input wire:model="testEmailAddress" placeholder="test@example.com"
 | 
			
		||||
                                id="testEmailAddress" label="Recipients" required />
 | 
			
		||||
                            <x-forms.button type="submit" @click="modalOpen=false">
 | 
			
		||||
                                Send Email
 | 
			
		||||
                            </x-forms.button>
 | 
			
		||||
                        </form>
 | 
			
		||||
                    </x-modal-input>
 | 
			
		||||
                @else
 | 
			
		||||
                    <x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
 | 
			
		||||
                        Send Test Email
 | 
			
		||||
                    </x-forms.button>
 | 
			
		||||
                @endif
 | 
			
		||||
            @endif
 | 
			
		||||
        </div>
 | 
			
		||||
        @if (!isCloud())
 | 
			
		||||
            <div class="w-96">
 | 
			
		||||
                <x-forms.checkbox instantSave="instantSaveInstance" id="useInstanceEmailSettings"
 | 
			
		||||
                <x-forms.checkbox instantSave="instantSave()" id="useInstanceEmailSettings"
 | 
			
		||||
                    label="Use system wide (transactional) email settings" />
 | 
			
		||||
            </div>
 | 
			
		||||
        @endif
 | 
			
		||||
@@ -38,17 +39,22 @@
 | 
			
		||||
                <x-forms.input required id="smtpFromAddress" helper="Email address used in emails."
 | 
			
		||||
                    label="From Address" />
 | 
			
		||||
            </div>
 | 
			
		||||
            @if (isInstanceAdmin() && !$useInstanceEmailSettings)
 | 
			
		||||
                <x-forms.button wire:click='copyFromInstanceSettings'>
 | 
			
		||||
                    Copy from Instance Settings
 | 
			
		||||
                </x-forms.button>
 | 
			
		||||
            @endif
 | 
			
		||||
        @endif
 | 
			
		||||
    </form>
 | 
			
		||||
    @if (isCloud())
 | 
			
		||||
        <div class="w-64 py-4">
 | 
			
		||||
            <x-forms.checkbox instantSave="instantSaveInstance" id="useInstanceEmailSettings"
 | 
			
		||||
            <x-forms.checkbox instantSave="instantSave()" id="useInstanceEmailSettings"
 | 
			
		||||
                label="Use Hosted Email Service" />
 | 
			
		||||
        </div>
 | 
			
		||||
    @endif
 | 
			
		||||
    @if (!$useInstanceEmailSettings)
 | 
			
		||||
        <div class="flex flex-col gap-4">
 | 
			
		||||
            <form wire:submit='submit' class="p-4 border dark:border-coolgray-300 flex flex-col gap-2">
 | 
			
		||||
            <form wire:submit='submitSmtp' class="p-4 border dark:border-coolgray-300 flex flex-col gap-2">
 | 
			
		||||
                <div class="flex items-center gap-2">
 | 
			
		||||
                    <h3>SMTP Server</h3>
 | 
			
		||||
                    <x-forms.button type="submit">
 | 
			
		||||
@@ -56,7 +62,8 @@
 | 
			
		||||
                    </x-forms.button>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="w-32">
 | 
			
		||||
                    <x-forms.checkbox instantSave="instantSaveSmtpEnabled" id="smtpEnabled" label="Enabled" />
 | 
			
		||||
                    <x-forms.checkbox wire:model="smtpEnabled" instantSave="instantSave('SMTP')" id="smtpEnabled"
 | 
			
		||||
                        label="Enabled" />
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex flex-col">
 | 
			
		||||
                    <div class="flex flex-col gap-4">
 | 
			
		||||
@@ -78,7 +85,7 @@
 | 
			
		||||
                    </div>
 | 
			
		||||
                </div>
 | 
			
		||||
            </form>
 | 
			
		||||
            <form wire:submit='submit' class="p-4 border dark:border-coolgray-300 flex flex-col gap-2">
 | 
			
		||||
            <form wire:submit='submitResend' class="p-4 border dark:border-coolgray-300 flex flex-col gap-2">
 | 
			
		||||
                <div class="flex items-center gap-2">
 | 
			
		||||
                    <h3>Resend</h3>
 | 
			
		||||
                    <x-forms.button type="submit">
 | 
			
		||||
@@ -86,7 +93,8 @@
 | 
			
		||||
                    </x-forms.button>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="w-32">
 | 
			
		||||
                    <x-forms.checkbox instantSave='instantSaveResend' id="resendEnabled" label="Enabled" />
 | 
			
		||||
                    <x-forms.checkbox wire:model="resendEnabled" instantSave="instantSave('Resend')" id="resendEnabled"
 | 
			
		||||
                        label="Enabled" />
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex flex-col">
 | 
			
		||||
                    <div class="flex flex-col gap-4">
 | 
			
		||||
@@ -99,20 +107,55 @@
 | 
			
		||||
            </form>
 | 
			
		||||
        </div>
 | 
			
		||||
    @endif
 | 
			
		||||
    @if (isEmailEnabled($team) || $useInstanceEmailSettings)
 | 
			
		||||
        <h2 class="mt-4">Subscribe to events</h2>
 | 
			
		||||
        <div class="w-64">
 | 
			
		||||
            @if (isDev())
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="smtpNotificationsTest" label="Test" />
 | 
			
		||||
            @endif
 | 
			
		||||
            <x-forms.checkbox instantSave="saveModel" id="smtpNotificationsStatusChanges"
 | 
			
		||||
                label="Container Status Changes" />
 | 
			
		||||
            <x-forms.checkbox instantSave="saveModel" id="smtpNotificationsDeployments"
 | 
			
		||||
                label="Application Deployments" />
 | 
			
		||||
            <x-forms.checkbox instantSave="saveModel" id="smtpNotificationsDatabaseBackups" label="Backup Status" />
 | 
			
		||||
            <x-forms.checkbox instantSave="saveModel" id="smtpNotificationsScheduledTasks"
 | 
			
		||||
                label="Scheduled Tasks Status" />
 | 
			
		||||
            <x-forms.checkbox instantSave="saveModel" id="smtpNotificationsServerDiskUsage" label="Server Disk Usage" />
 | 
			
		||||
    <h2 class="mt-4">Notification Settings</h2>
 | 
			
		||||
    <p class="mb-4">
 | 
			
		||||
        Select events for which you would like to receive email notifications.
 | 
			
		||||
    </p>
 | 
			
		||||
    <div class="flex flex-col gap-4 max-w-2xl">
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="font-medium mb-3">Deployments</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="deploymentSuccessEmailNotifications"
 | 
			
		||||
                    label="Deployment Success" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="deploymentFailureEmailNotifications"
 | 
			
		||||
                    label="Deployment Failure" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel"
 | 
			
		||||
                    helper="Send an email when a container status changes. It will send and email for Stopped and Restarted events of a container."
 | 
			
		||||
                    id="statusChangeEmailNotifications" label="Container Status Changes" />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    @endif
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="font-medium mb-3">Backups</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="backupSuccessEmailNotifications"
 | 
			
		||||
                    label="Backup Success" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="backupFailureEmailNotifications"
 | 
			
		||||
                    label="Backup Failure" />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="font-medium mb-3">Scheduled Tasks</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessEmailNotifications"
 | 
			
		||||
                    label="Scheduled Task Success" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailureEmailNotifications"
 | 
			
		||||
                    label="Scheduled Task Failure" />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="font-medium mb-3">Server</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessEmailNotifications"
 | 
			
		||||
                    label="Docker Cleanup Success" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailureEmailNotifications"
 | 
			
		||||
                    label="Docker Cleanup Failure" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="serverDiskUsageEmailNotifications"
 | 
			
		||||
                    label="Server Disk Usage" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="serverReachableEmailNotifications"
 | 
			
		||||
                    label="Server Reachable" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="serverUnreachableEmailNotifications"
 | 
			
		||||
                    label="Server Unreachable" />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,43 +1,79 @@
 | 
			
		||||
<div>
 | 
			
		||||
    <x-slot:title>
 | 
			
		||||
        Notifications | Coolify
 | 
			
		||||
        </x-slot>
 | 
			
		||||
        <x-notification.navbar />
 | 
			
		||||
        <form wire:submit='submit' class="flex flex-col gap-4">
 | 
			
		||||
            <div class="flex items-center gap-2">
 | 
			
		||||
                <h2>Slack</h2>
 | 
			
		||||
                <x-forms.button type="submit">
 | 
			
		||||
                    Save
 | 
			
		||||
    </x-slot>
 | 
			
		||||
    <x-notification.navbar />
 | 
			
		||||
    <form wire:submit='submit' class="flex flex-col gap-4 pb-4">
 | 
			
		||||
        <div class="flex items-center gap-2">
 | 
			
		||||
            <h2>Slack</h2>
 | 
			
		||||
            <x-forms.button type="submit">
 | 
			
		||||
                Save
 | 
			
		||||
            </x-forms.button>
 | 
			
		||||
            @if ($slackEnabled)
 | 
			
		||||
                <x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
 | 
			
		||||
                    wire:click="sendTestNotification">
 | 
			
		||||
                    Send Test Notification
 | 
			
		||||
                </x-forms.button>
 | 
			
		||||
                @if ($slackEnabled)
 | 
			
		||||
                    <x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
 | 
			
		||||
                        wire:click="sendTestNotification">
 | 
			
		||||
                        Send Test Notifications
 | 
			
		||||
                    </x-forms.button>
 | 
			
		||||
                @endif
 | 
			
		||||
            @else
 | 
			
		||||
                <x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
 | 
			
		||||
                    Send Test Notification
 | 
			
		||||
                </x-forms.button>
 | 
			
		||||
            @endif
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="w-32">
 | 
			
		||||
            <x-forms.checkbox instantSave="instantSaveSlackEnabled" id="slackEnabled" label="Enabled" />
 | 
			
		||||
        </div>
 | 
			
		||||
        <x-forms.input type="password"
 | 
			
		||||
            helper="Generate a webhook in Slack.<br>Example: https://hooks.slack.com/services/...." required
 | 
			
		||||
            id="slackWebhookUrl" label="Webhook" />
 | 
			
		||||
    </form>
 | 
			
		||||
    <h2 class="mt-4">Notification Settings</h2>
 | 
			
		||||
    <p class="mb-4">
 | 
			
		||||
        Select events for which you would like to receive Slack notifications.
 | 
			
		||||
    </p>
 | 
			
		||||
    <div class="flex flex-col gap-4 max-w-2xl">
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="font-medium mb-3">Deployments</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="deploymentSuccessSlackNotifications"
 | 
			
		||||
                    label="Deployment Success" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="deploymentFailureSlackNotifications"
 | 
			
		||||
                    label="Deployment Failure" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel"
 | 
			
		||||
                    helper="Send a notification when a container status changes. It will notify for Stopped and Restarted events of a container."
 | 
			
		||||
                    id="statusChangeSlackNotifications" label="Container Status Changes" />
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class="w-32">
 | 
			
		||||
                <x-forms.checkbox instantSave="instantSaveSlackEnabled" id="slackEnabled" label="Enabled" />
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="font-medium mb-3">Backups</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="backupSuccessSlackNotifications" label="Backup Success" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="backupFailureSlackNotifications" label="Backup Failure" />
 | 
			
		||||
            </div>
 | 
			
		||||
            <x-forms.input type="password"
 | 
			
		||||
                helper="Generate a webhook in Slack.<br>Example: https://hooks.slack.com/services/...." required
 | 
			
		||||
                id="slackWebhookUrl" label="Webhook" />
 | 
			
		||||
        </form>
 | 
			
		||||
        @if ($slackEnabled)
 | 
			
		||||
            <h2 class="mt-4">Subscribe to events</h2>
 | 
			
		||||
            <div class="w-64">
 | 
			
		||||
                @if (isDev())
 | 
			
		||||
                    <x-forms.checkbox instantSave="saveModel" id="slackNotificationsTest" label="Test" />
 | 
			
		||||
                @endif
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="slackNotificationsStatusChanges"
 | 
			
		||||
                    label="Container Status Changes" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="slackNotificationsDeployments"
 | 
			
		||||
                    label="Application Deployments" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="slackNotificationsDatabaseBackups" label="Backup Status" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="slackNotificationsScheduledTasks"
 | 
			
		||||
                    label="Scheduled Tasks Status" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="slackNotificationsServerDiskUsage"
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="font-medium mb-3">Scheduled Tasks</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessSlackNotifications"
 | 
			
		||||
                    label="Scheduled Task Success" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailureSlackNotifications"
 | 
			
		||||
                    label="Scheduled Task Failure" />
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="font-medium mb-3">Server</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessSlackNotifications"
 | 
			
		||||
                    label="Docker Cleanup Success" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailureSlackNotifications"
 | 
			
		||||
                    label="Docker Cleanup Failure" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="serverDiskUsageSlackNotifications"
 | 
			
		||||
                    label="Server Disk Usage" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="serverReachableSlackNotifications"
 | 
			
		||||
                    label="Server Reachable" />
 | 
			
		||||
                <x-forms.checkbox instantSave="saveModel" id="serverUnreachableSlackNotifications"
 | 
			
		||||
                    label="Server Unreachable" />
 | 
			
		||||
            </div>
 | 
			
		||||
        @endif
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
        Notifications | Coolify
 | 
			
		||||
    </x-slot>
 | 
			
		||||
    <x-notification.navbar />
 | 
			
		||||
    <form wire:submit='submit' class="flex flex-col gap-4">
 | 
			
		||||
    <form wire:submit='submit' class="flex flex-col gap-4 pb-4">
 | 
			
		||||
        <div class="flex items-center gap-2">
 | 
			
		||||
            <h2>Telegram</h2>
 | 
			
		||||
            <x-forms.button type="submit">
 | 
			
		||||
@@ -12,7 +12,11 @@
 | 
			
		||||
            @if ($telegramEnabled)
 | 
			
		||||
                <x-forms.button class="normal-case dark:text-white btn btn-xs no-animation btn-primary"
 | 
			
		||||
                    wire:click="sendTestNotification">
 | 
			
		||||
                    Send Test Notifications
 | 
			
		||||
                    Send Test Notification
 | 
			
		||||
                </x-forms.button>
 | 
			
		||||
            @else
 | 
			
		||||
                <x-forms.button disabled class="normal-case dark:text-white btn btn-xs no-animation btn-primary">
 | 
			
		||||
                    Send Test Notification
 | 
			
		||||
                </x-forms.button>
 | 
			
		||||
            @endif
 | 
			
		||||
        </div>
 | 
			
		||||
@@ -20,61 +24,143 @@
 | 
			
		||||
            <x-forms.checkbox instantSave="instantSaveTelegramEnabled" id="telegramEnabled" label="Enabled" />
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="flex gap-2">
 | 
			
		||||
 | 
			
		||||
            <x-forms.input type="password" autocomplete="new-password"
 | 
			
		||||
                helper="Get it from the <a class='inline-block underline dark:text-white' href='https://t.me/botfather' target='_blank'>BotFather Bot</a> on Telegram."
 | 
			
		||||
                required id="telegramToken" label="Token" />
 | 
			
		||||
            <x-forms.input helper="Recommended to add your bot to a group chat and add its Chat ID here." required
 | 
			
		||||
                id="telegramChatId" label="Chat ID" />
 | 
			
		||||
        </div>
 | 
			
		||||
        @if ($telegramEnabled)
 | 
			
		||||
            <h2 class="mt-4">Subscribe to events</h2>
 | 
			
		||||
            <div class="flex flex-col gap-4 w-96">
 | 
			
		||||
                @if (isDev())
 | 
			
		||||
                    <div class="flex flex-col">
 | 
			
		||||
                        <h4>Test Notification</h4>
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="telegramNotificationsTest" label="Enabled" />
 | 
			
		||||
                        <x-forms.input
 | 
			
		||||
                            helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
 | 
			
		||||
                            id="telegramNotificationsTestMessageThreadId" label="Custom Topic ID" />
 | 
			
		||||
    </form>
 | 
			
		||||
    <h2 class="mt-4">Notification Settings</h2>
 | 
			
		||||
    <p class="mb-4">
 | 
			
		||||
        Select events for which you would like to receive Telegram notifications.
 | 
			
		||||
    </p>
 | 
			
		||||
    <div class="flex flex-col gap-4 ">
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="text-lg font-medium mb-3">Deployments</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <div class="pl-1 flex gap-2">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="deploymentSuccessTelegramNotifications"
 | 
			
		||||
                            label="Deployment Success" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                @endif
 | 
			
		||||
                <div class="flex flex-col">
 | 
			
		||||
                    <h4>Container Status Changes</h4>
 | 
			
		||||
                    <x-forms.checkbox instantSave="saveModel" id="telegramNotificationsStatusChanges" label="Enabled" />
 | 
			
		||||
                    <x-forms.input
 | 
			
		||||
                        helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
 | 
			
		||||
                        id="telegramNotificationsStatusChangesMessageThreadId" label="Custom Topic ID" />
 | 
			
		||||
                    <x-forms.input type="password" placeholder="Custom Telegram Topic ID"
 | 
			
		||||
                        id="telegramNotificationsDeploymentSuccessTopicId" />
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex flex-col">
 | 
			
		||||
                    <h4>Application Deployments</h4>
 | 
			
		||||
                    <x-forms.checkbox instantSave="saveModel" id="telegramNotificationsDeployments" label="Enabled" />
 | 
			
		||||
                    <x-forms.input
 | 
			
		||||
                        helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
 | 
			
		||||
                        id="telegramNotificationsDeploymentsMessageThreadId" label="Custom Topic ID" />
 | 
			
		||||
                <div class="pl-1 flex gap-2">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="deploymentFailureTelegramNotifications"
 | 
			
		||||
                            label="Deployment Failure" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <x-forms.input type="password" placeholder="Custom Telegram Topic ID"
 | 
			
		||||
                        id="telegramNotificationsDeploymentFailureTopicId" />
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex flex-col">
 | 
			
		||||
                    <h4>Database Backup Status</h4>
 | 
			
		||||
                    <x-forms.checkbox instantSave="saveModel" id="telegramNotificationsDatabaseBackups"
 | 
			
		||||
                        label="Enabled" />
 | 
			
		||||
                    <x-forms.input
 | 
			
		||||
                        helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
 | 
			
		||||
                        id="telegramNotificationsDatabaseBackupsMessageThreadId" label="Custom Topic ID" />
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex flex-col">
 | 
			
		||||
                    <h4>Scheduled Tasks Status</h4>
 | 
			
		||||
                    <x-forms.checkbox instantSave="saveModel" id="telegramNotificationsScheduledTasks"
 | 
			
		||||
                        label="Enabled" />
 | 
			
		||||
                    <x-forms.input
 | 
			
		||||
                        helper="If you are using Group chat with Topics, you can specify the topics ID. If empty, General topic will be used."
 | 
			
		||||
                        id="telegramNotificationsScheduledTasksMessageThreadId" label="Custom Topic ID" />
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class="flex flex-col">
 | 
			
		||||
                    <h4>Server Disk Usage</h4>
 | 
			
		||||
                    <x-forms.checkbox instantSave="saveModel" id="telegramNotificationsServerDiskUsage"
 | 
			
		||||
                        label="Enabled" />
 | 
			
		||||
                <div class="pl-1 flex gap-2">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="statusChangeTelegramNotifications"
 | 
			
		||||
                            label="Container Status Changes"
 | 
			
		||||
                            helper="Send a notification when a container status changes. It will send a notification for Stopped and Restarted events of a container." />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <x-forms.input type="password" id="telegramNotificationsStatusChangeTopicId"
 | 
			
		||||
                        placeholder="Custom Telegram Topic ID" />
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        @endif
 | 
			
		||||
    </form>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="text-lg font-medium mb-3">Backups</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <div class="pl-1 flex gap-2">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="backupSuccessTelegramNotifications"
 | 
			
		||||
                            label="Backup Success" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <x-forms.input type="password" placeholder="Custom Telegram Topic ID"
 | 
			
		||||
                        id="telegramNotificationsBackupSuccessTopicId" />
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div class="pl-1 flex gap-2">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="backupFailureTelegramNotifications"
 | 
			
		||||
                            label="Backup Failure" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <x-forms.input type="password" placeholder="Custom Telegram Topic ID"
 | 
			
		||||
                        id="telegramNotificationsBackupFailureTopicId" />
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="text-lg font-medium mb-3">Scheduled Tasks</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <div class="pl-1 flex gap-2">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="scheduledTaskSuccessTelegramNotifications"
 | 
			
		||||
                            label="Scheduled Task Success" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <x-forms.input type="password" placeholder="Custom Telegram Topic ID"
 | 
			
		||||
                        id="telegramNotificationsScheduledTaskSuccessTopicId" />
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div class="pl-1 flex gap-2">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="scheduledTaskFailureTelegramNotifications"
 | 
			
		||||
                            label="Scheduled Task Failure" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <x-forms.input type="password" placeholder="Custom Telegram Topic ID"
 | 
			
		||||
                        id="telegramNotificationsScheduledTaskFailureTopicId" />
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
        <div class="border dark:border-coolgray-300 p-4 rounded-lg">
 | 
			
		||||
            <h3 class="text-lg font-medium mb-3">Server</h3>
 | 
			
		||||
            <div class="flex flex-col gap-1.5 pl-1">
 | 
			
		||||
                <div class="pl-1 flex gap-2">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="dockerCleanupSuccessTelegramNotifications"
 | 
			
		||||
                            label="Docker Cleanup Success" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <x-forms.input type="password" placeholder="Custom Telegram Topic ID"
 | 
			
		||||
                        id="telegramNotificationsDockerCleanupSuccessTopicId" />
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div class="pl-1 flex gap-2">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="dockerCleanupFailureTelegramNotifications"
 | 
			
		||||
                            label="Docker Cleanup Failure" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <x-forms.input type="password" placeholder="Custom Telegram Topic ID"
 | 
			
		||||
                        id="telegramNotificationsDockerCleanupFailureTopicId" />
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div class="pl-1 flex gap-2">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="serverDiskUsageTelegramNotifications"
 | 
			
		||||
                            label="Server Disk Usage" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <x-forms.input type="password" placeholder="Custom Telegram Topic ID"
 | 
			
		||||
                        id="telegramNotificationsServerDiskUsageTopicId" />
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
                <div class="pl-1 flex gap-2">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="serverReachableTelegramNotifications"
 | 
			
		||||
                            label="Server Reachable" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <x-forms.input type="password" placeholder="Custom Telegram Topic ID"
 | 
			
		||||
                        id="telegramNotificationsServerReachableTopicId" />
 | 
			
		||||
                </div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
                <div class="pl-1 flex gap-2">
 | 
			
		||||
                    <div class="w-96">
 | 
			
		||||
                        <x-forms.checkbox instantSave="saveModel" id="serverUnreachableTelegramNotifications"
 | 
			
		||||
                            label="Server Unreachable" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <x-forms.input type="password" placeholder="Custom Telegram Topic ID"
 | 
			
		||||
                        id="telegramNotificationsServerUnreachableTopicId" />
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -9,17 +9,27 @@
 | 
			
		||||
            <x-forms.button type="submit">
 | 
			
		||||
                Save
 | 
			
		||||
            </x-forms.button>
 | 
			
		||||
            @if (is_transactional_emails_enabled() && auth()->user()->isAdminFromSession())
 | 
			
		||||
                <x-modal-input buttonTitle="Send Test Email" title="Send Test Email">
 | 
			
		||||
                    <form wire:submit.prevent="sendTestEmail" class="flex flex-col w-full gap-2">
 | 
			
		||||
                        <x-forms.input wire:model="testEmailAddress" placeholder="test@example.com" id="testEmailAddress"
 | 
			
		||||
                            label="Recipients" required />
 | 
			
		||||
                        <x-forms.button type="submit" @click="modalOpen=false">
 | 
			
		||||
                            Send Email
 | 
			
		||||
                        </x-forms.button>
 | 
			
		||||
                    </form>
 | 
			
		||||
                </x-modal-input>
 | 
			
		||||
            @endif
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="pb-4 ">Email settings for password resets, invitations, etc.</div>
 | 
			
		||||
        <div class="flex  gap-4">
 | 
			
		||||
        <div class="pb-4">Instance wide email settings for password resets, invitations, etc.</div>
 | 
			
		||||
        <div class="flex gap-4">
 | 
			
		||||
            <x-forms.input required id="smtpFromName" helper="Name used in emails." label="From Name" />
 | 
			
		||||
            <x-forms.input required id="smtpFromAddress" helper="Email address used in emails." label="From Address" />
 | 
			
		||||
        </div>
 | 
			
		||||
 | 
			
		||||
    </form>
 | 
			
		||||
    <div class="flex flex-col gap-4">
 | 
			
		||||
        <div class="p-4 border dark:border-coolgray-300">
 | 
			
		||||
            <form wire:submit='submit' class="flex flex-col">
 | 
			
		||||
            <form wire:submit.prevent="submitSmtp" class="flex flex-col">
 | 
			
		||||
                <div class="flex gap-2">
 | 
			
		||||
                    <h3>SMTP Server</h3>
 | 
			
		||||
                    <x-forms.button type="submit">
 | 
			
		||||
@@ -49,7 +59,7 @@
 | 
			
		||||
            </form>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="p-4 border dark:border-coolgray-300">
 | 
			
		||||
            <form wire:submit='submit' class="flex flex-col">
 | 
			
		||||
            <form wire:submit.prevent="submitResend" class="flex flex-col">
 | 
			
		||||
                <div class="flex gap-2">
 | 
			
		||||
                    <h3>Resend</h3>
 | 
			
		||||
                    <x-forms.button type="submit">
 | 
			
		||||
 
 | 
			
		||||
@@ -2,42 +2,38 @@
 | 
			
		||||
    <x-slot:title>
 | 
			
		||||
        Subscribe | Coolify
 | 
			
		||||
    </x-slot>
 | 
			
		||||
    @if ($settings->is_resale_license_active)
 | 
			
		||||
        @if (auth()->user()->isAdminFromSession())
 | 
			
		||||
            <div>
 | 
			
		||||
                <div class="flex gap-2">
 | 
			
		||||
                    <h1>Subscriptions</h1>
 | 
			
		||||
                    @if (subscriptionProvider() === 'stripe' && $alreadySubscribed)
 | 
			
		||||
                        <x-forms.button wire:click='stripeCustomerPortal'>Manage My Subscription</x-forms.button>
 | 
			
		||||
                    @endif
 | 
			
		||||
                </div>
 | 
			
		||||
                @if (request()->query->get('cancelled'))
 | 
			
		||||
                    <div class="mb-6 rounded alert-error">
 | 
			
		||||
                        <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
 | 
			
		||||
                            viewBox="0 0 24 24">
 | 
			
		||||
                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
 | 
			
		||||
                                d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
 | 
			
		||||
                        </svg>
 | 
			
		||||
                        <span>Something went wrong with your subscription. Please try again or contact
 | 
			
		||||
                            support.</span>
 | 
			
		||||
                    </div>
 | 
			
		||||
    @if (auth()->user()->isAdminFromSession())
 | 
			
		||||
        <div>
 | 
			
		||||
            <div class="flex gap-2">
 | 
			
		||||
                <h1>Subscriptions</h1>
 | 
			
		||||
                @if (subscriptionProvider() === 'stripe' && $alreadySubscribed)
 | 
			
		||||
                    <x-forms.button wire:click='stripeCustomerPortal'>Manage My Subscription</x-forms.button>
 | 
			
		||||
                @endif
 | 
			
		||||
            </div>
 | 
			
		||||
            @if (request()->query->get('cancelled'))
 | 
			
		||||
                <div class="mb-6 rounded alert-error">
 | 
			
		||||
                    <svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6 stroke-current shrink-0" fill="none"
 | 
			
		||||
                        viewBox="0 0 24 24">
 | 
			
		||||
                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
 | 
			
		||||
                            d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
 | 
			
		||||
                    </svg>
 | 
			
		||||
                    <span>Something went wrong with your subscription. Please try again or contact
 | 
			
		||||
                        support.</span>
 | 
			
		||||
                </div>
 | 
			
		||||
            @endif
 | 
			
		||||
 | 
			
		||||
                @if (config('subscription.provider') === 'stripe')
 | 
			
		||||
                    <livewire:subscription.pricing-plans />
 | 
			
		||||
                @endif
 | 
			
		||||
            </div>
 | 
			
		||||
        @else
 | 
			
		||||
            <div class="flex flex-col justify-center mx-10">
 | 
			
		||||
                <div class="flex gap-2">
 | 
			
		||||
                    <h1>Subscription</h1>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div>You are not an admin or have been removed from this team. If this does not make sense, please <span
 | 
			
		||||
                        class="underline cursor-pointer dark:text-white" wire:click="help">contact
 | 
			
		||||
                        us</span>.</div>
 | 
			
		||||
            </div>
 | 
			
		||||
        @endif
 | 
			
		||||
            @if (config('subscription.provider') === 'stripe')
 | 
			
		||||
                <livewire:subscription.pricing-plans />
 | 
			
		||||
            @endif
 | 
			
		||||
        </div>
 | 
			
		||||
    @else
 | 
			
		||||
        <div class="px-10">Resale license is not active. Please contact your instance admin.</div>
 | 
			
		||||
        <div class="flex flex-col justify-center mx-10">
 | 
			
		||||
            <div class="flex gap-2">
 | 
			
		||||
                <h1>Subscription</h1>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div>You are not an admin or have been removed from this team. If this does not make sense, please <span
 | 
			
		||||
                    class="underline cursor-pointer dark:text-white" wire:click="help">contact
 | 
			
		||||
                    us</span>.</div>
 | 
			
		||||
        </div>
 | 
			
		||||
    @endif
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
    <div class="flex gap-2">
 | 
			
		||||
        <x-forms.button type="submit">Generate Invitation Link</x-forms.button>
 | 
			
		||||
        @if (is_transactional_emails_active())
 | 
			
		||||
        @if (is_transactional_emails_enabled())
 | 
			
		||||
            <x-forms.button wire:click.prevent='viaEmail'>Send Invitation via Email</x-forms.button>
 | 
			
		||||
        @endif
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@
 | 
			
		||||
    </div>
 | 
			
		||||
    @if (auth()->user()->isAdminFromSession())
 | 
			
		||||
        <div class="py-4">
 | 
			
		||||
            @if (is_transactional_emails_active())
 | 
			
		||||
            @if (is_transactional_emails_enabled())
 | 
			
		||||
                <h2 class="pb-4">Invite New Member</h2>
 | 
			
		||||
            @else
 | 
			
		||||
                <h2>Invite New Member</h2>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
<div class="min-h-screen hero">
 | 
			
		||||
    <div class="w-96 min-w-fit">
 | 
			
		||||
        <div class="flex flex-col items-center pb-8">
 | 
			
		||||
            <a href="{{ route('dashboard') }}">
 | 
			
		||||
                <div class="text-5xl font-bold tracking-tight text-center dark:text-white">Coolify</div>
 | 
			
		||||
            </a>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="flex items-center justify-center pb-4 text-center">
 | 
			
		||||
            <h2>Self-hosting in the cloud
 | 
			
		||||
                <svg class="inline-block w-8 h-8 dark:text-warning width="512" height="512" viewBox="0 0 20 20"
 | 
			
		||||
                    xmlns="http://www.w3.org/2000/svg">
 | 
			
		||||
                    <g fill="currentColor" fill-rule="evenodd" clip-rule="evenodd">
 | 
			
		||||
                        <path
 | 
			
		||||
                            d="M13 4h-1a4.002 4.002 0 0 0-3.874 3H8a4 4 0 1 0 0 8h8a4 4 0 0 0 .899-7.899A4.002 4.002 0 0 0 13 4Z"
 | 
			
		||||
                            opacity=".2" />
 | 
			
		||||
                        <path
 | 
			
		||||
                            d="M11 3h-1a4.002 4.002 0 0 0-3.874 3H6a4 4 0 1 0 0 8h8a4 4 0 0 0 .899-7.899A4.002 4.002 0 0 0 11 3ZM6.901 7l.193-.75A3.002 3.002 0 0 1 10 4h1c1.405 0 2.614.975 2.924 2.325l.14.61l.61.141A3.001 3.001 0 0 1 14 13H6a3 3 0 1 1 0-6h.901Z" />
 | 
			
		||||
                    </g>
 | 
			
		||||
                </svg>
 | 
			
		||||
            </h2>
 | 
			
		||||
        </div>
 | 
			
		||||
        <form class="flex items-end gap-2" wire:submit='submit'>
 | 
			
		||||
            <x-forms.input id="email" type="email" label="Email" placeholder="youareawesome@protonmail.com" />
 | 
			
		||||
            <x-forms.button type="submit">Join Waitlist</x-forms.button>
 | 
			
		||||
        </form>
 | 
			
		||||
        <div>People waiting in the line: <span class="font-bold dark:text-warning">{{ $waitingInLine }}</div>
 | 
			
		||||
        <div>Already using Coolify Cloud: <span class="font-bold dark:text-warning">{{ $users }}</div>
 | 
			
		||||
        <div class="pt-8">
 | 
			
		||||
            This is a paid & hosted version of Coolify.<br> See the pricing <a href="https://coolify.io/pricing"
 | 
			
		||||
                class="dark:text-warning">here</a>.
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="pt-4">
 | 
			
		||||
            If you are looking for the self-hosted version go <a href="https://coolify.io"
 | 
			
		||||
                class="dark:text-warning">here</a>.
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -88,7 +88,6 @@ Route::get('/admin', AdminIndex::class)->name('admin.index');
 | 
			
		||||
 | 
			
		||||
Route::post('/forgot-password', [Controller::class, 'forgot_password'])->name('password.forgot')->middleware('throttle:forgot-password');
 | 
			
		||||
Route::get('/realtime', [Controller::class, 'realtime_test'])->middleware('auth');
 | 
			
		||||
// Route::get('/waitlist', WaitlistIndex::class)->name('waitlist.index');
 | 
			
		||||
Route::get('/verify', [Controller::class, 'verify'])->middleware('auth')->name('verify.email');
 | 
			
		||||
Route::get('/email/verify/{id}/{hash}', [Controller::class, 'email_verify'])->middleware(['auth'])->name('verify.verify');
 | 
			
		||||
Route::middleware(['throttle:login'])->group(function () {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ use App\Http\Controllers\Webhook\Gitea;
 | 
			
		||||
use App\Http\Controllers\Webhook\Github;
 | 
			
		||||
use App\Http\Controllers\Webhook\Gitlab;
 | 
			
		||||
use App\Http\Controllers\Webhook\Stripe;
 | 
			
		||||
use App\Http\Controllers\Webhook\Waitlist;
 | 
			
		||||
use Illuminate\Support\Facades\Route;
 | 
			
		||||
 | 
			
		||||
Route::get('/source/github/redirect', [Github::class, 'redirect']);
 | 
			
		||||
@@ -20,6 +19,3 @@ Route::post('/source/bitbucket/events/manual', [Bitbucket::class, 'manual']);
 | 
			
		||||
Route::post('/source/gitea/events/manual', [Gitea::class, 'manual']);
 | 
			
		||||
 | 
			
		||||
Route::post('/payments/stripe/events', [Stripe::class, 'events']);
 | 
			
		||||
 | 
			
		||||
Route::get('/waitlist/confirm', [Waitlist::class, 'confirm'])->name('webhooks.waitlist.confirm');
 | 
			
		||||
Route::get('/waitlist/cancel', [Waitlist::class, 'cancel'])->name('webhooks.waitlist.cancel');
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								scripts/run
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								scripts/run
									
									
									
									
									
								
							@@ -32,18 +32,18 @@ function sync:bunny {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function db:reset {
 | 
			
		||||
    bash spin exec -u webuser coolify php artisan migrate:fresh --seed
 | 
			
		||||
    bash spin exec -u www-data coolify php artisan migrate:fresh --seed
 | 
			
		||||
}
 | 
			
		||||
function db:reset-prod {
 | 
			
		||||
    bash spin exec -u webuser coolify php artisan migrate:fresh --force --seed --seeder=ProductionSeeder ||
 | 
			
		||||
    bash spin exec -u www-data coolify php artisan migrate:fresh --force --seed --seeder=ProductionSeeder ||
 | 
			
		||||
        php artisan migrate:fresh --force --seed --seeder=ProductionSeeder
 | 
			
		||||
}
 | 
			
		||||
function coolify {
 | 
			
		||||
    bash spin exec -u webuser coolify bash
 | 
			
		||||
    bash spin exec -u www-data coolify sh
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function coolify:root {
 | 
			
		||||
    bash spin exec coolify bash
 | 
			
		||||
    bash spin exec coolify sh
 | 
			
		||||
}
 | 
			
		||||
function coolify:proxy {
 | 
			
		||||
    docker exec -ti coolify-proxy sh
 | 
			
		||||
@@ -58,7 +58,7 @@ function vite {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function tinker {
 | 
			
		||||
    bash spin exec -u webuser coolify php artisan tinker
 | 
			
		||||
    bash spin exec -u www-data coolify php artisan tinker
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function default {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user